modules/scheduler/gke-cluster/main.tf (352 lines of code) (raw):

/** * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ locals { # This label allows for billing report tracking based on module. labels = merge(var.labels, { ghpc_module = "gke-cluster", ghpc_role = "scheduler" }) } locals { upgrade_settings = { strategy = var.upgrade_settings.strategy max_surge = coalesce(var.upgrade_settings.max_surge, 0) max_unavailable = coalesce(var.upgrade_settings.max_unavailable, 1) } } locals { dash = var.prefix_with_deployment_name && var.name_suffix != "" ? "-" : "" prefix = var.prefix_with_deployment_name ? var.deployment_name : "" name_maybe_empty = "${local.prefix}${local.dash}${var.name_suffix}" name = local.name_maybe_empty != "" ? local.name_maybe_empty : "NO-NAME-GIVEN" cluster_authenticator_security_group = var.authenticator_security_group == null ? [] : [{ security_group = var.authenticator_security_group }] default_sa_email = "${data.google_project.project.number}-compute@developer.gserviceaccount.com" sa_email = coalesce(var.service_account_email, local.default_sa_email) # additional VPCs enable multi networking derived_enable_multi_networking = coalesce(var.enable_multi_networking, length(var.additional_networks) > 0) # multi networking needs enabled Dataplane v2 derived_enable_dataplane_v2 = coalesce(var.enable_dataplane_v2, local.derived_enable_multi_networking) default_monitoring_component = [ "SYSTEM_COMPONENTS", "POD", "DAEMONSET", "DEPLOYMENT", "STATEFULSET", "STORAGE", "HPA", "CADVISOR", "KUBELET" ] default_logging_component = [ "SYSTEM_COMPONENTS", "WORKLOADS" ] } data "google_project" "project" { project_id = var.project_id } data "google_container_engine_versions" "version_prefix_filter" { provider = google-beta location = var.cluster_availability_type == "ZONAL" ? var.zone : var.region version_prefix = var.version_prefix } locals { master_version = var.min_master_version != null ? var.min_master_version : data.google_container_engine_versions.version_prefix_filter.latest_master_version } resource "google_container_cluster" "gke_cluster" { provider = google-beta project = var.project_id name = local.name location = var.cluster_availability_type == "ZONAL" ? var.zone : var.region resource_labels = local.labels networking_mode = var.networking_mode # decouple node pool lifecycle from cluster life cycle remove_default_node_pool = true initial_node_count = 1 # must be set when remove_default_node_pool is set deletion_protection = var.deletion_protection dynamic "enable_k8s_beta_apis" { for_each = var.enable_k8s_beta_apis != null ? [1] : [] content { enabled_apis = var.enable_k8s_beta_apis } } network = var.network_id subnetwork = var.subnetwork_self_link # Note: the existence of the "master_authorized_networks_config" block enables # the master authorized networks even if it's empty. master_authorized_networks_config { dynamic "cidr_blocks" { for_each = var.master_authorized_networks content { cidr_block = cidr_blocks.value.cidr_block display_name = cidr_blocks.value.display_name } } gcp_public_cidrs_access_enabled = var.gcp_public_cidrs_access_enabled } private_ipv6_google_access = var.enable_private_ipv6_google_access ? "PRIVATE_IPV6_GOOGLE_ACCESS_TO_GOOGLE" : null default_max_pods_per_node = var.default_max_pods_per_node master_auth { client_certificate_config { issue_client_certificate = false } } enable_shielded_nodes = true cluster_autoscaling { # Controls auto provisioning of node-pools enabled = false # Controls autoscaling algorithm of node-pools autoscaling_profile = var.autoscaling_profile } datapath_provider = local.derived_enable_dataplane_v2 ? "ADVANCED_DATAPATH" : "LEGACY_DATAPATH" enable_multi_networking = local.derived_enable_multi_networking network_policy { # Enabling NetworkPolicy for clusters with DatapathProvider=ADVANCED_DATAPATH # is not allowed. Dataplane V2 will take care of network policy enforcement # instead. enabled = false # GKE Dataplane V2 support. This must be set to PROVIDER_UNSPECIFIED in # order to let the datapath_provider take effect. # https://github.com/terraform-google-modules/terraform-google-kubernetes-engine/issues/656#issuecomment-720398658 provider = "PROVIDER_UNSPECIFIED" } private_cluster_config { enable_private_nodes = var.enable_private_nodes enable_private_endpoint = var.enable_private_endpoint master_ipv4_cidr_block = var.master_ipv4_cidr_block master_global_access_config { enabled = var.enable_master_global_access } } ip_allocation_policy { cluster_secondary_range_name = var.pods_ip_range_name services_secondary_range_name = var.services_ip_range_name } workload_identity_config { workload_pool = "${var.project_id}.svc.id.goog" } dynamic "authenticator_groups_config" { for_each = local.cluster_authenticator_security_group content { security_group = authenticator_groups_config.value.security_group } } release_channel { channel = var.release_channel } min_master_version = local.master_version maintenance_policy { daily_maintenance_window { start_time = var.maintenance_start_time } dynamic "maintenance_exclusion" { for_each = var.maintenance_exclusions content { exclusion_name = maintenance_exclusion.value.name start_time = maintenance_exclusion.value.start_time end_time = maintenance_exclusion.value.end_time exclusion_options { scope = maintenance_exclusion.value.exclusion_scope } } } } dynamic "dns_config" { for_each = var.cloud_dns_config != null ? [1] : [] content { additive_vpc_scope_dns_domain = var.cloud_dns_config.additive_vpc_scope_dns_domain cluster_dns = var.cloud_dns_config.cluster_dns cluster_dns_scope = var.cloud_dns_config.cluster_dns_scope cluster_dns_domain = var.cloud_dns_config.cluster_dns_domain } } addons_config { gcp_filestore_csi_driver_config { enabled = var.enable_filestore_csi } gcs_fuse_csi_driver_config { enabled = var.enable_gcsfuse_csi } gce_persistent_disk_csi_driver_config { enabled = var.enable_persistent_disk_csi } dns_cache_config { enabled = var.enable_node_local_dns_cache } parallelstore_csi_driver_config { enabled = var.enable_parallelstore_csi } ray_operator_config { enabled = var.enable_ray_operator } } timeouts { create = var.timeout_create update = var.timeout_update } node_config { shielded_instance_config { enable_secure_boot = var.system_node_pool_enable_secure_boot enable_integrity_monitoring = true } } control_plane_endpoints_config { dns_endpoint_config { allow_external_traffic = var.enable_external_dns_endpoint } } lifecycle { # Ignore all changes to the default node pool. It's being removed after creation. ignore_changes = [ node_config, min_master_version, ] precondition { condition = var.default_max_pods_per_node == null || var.networking_mode == "VPC_NATIVE" error_message = "default_max_pods_per_node does not work on `routes-based` clusters, that don't have IP Aliasing enabled." } precondition { condition = coalesce(var.enable_dataplane_v2, true) || !local.derived_enable_multi_networking error_message = "'enable_dataplane_v2' cannot be false when enabling multi networking." } precondition { condition = coalesce(var.enable_multi_networking, true) || length(var.additional_networks) == 0 error_message = "'enable_multi_networking' cannot be false when using multivpc module, which passes additional_networks." } } monitoring_config { enable_components = var.enable_dcgm_monitoring ? concat(local.default_monitoring_component, ["DCGM"]) : local.default_monitoring_component managed_prometheus { enabled = true } } logging_config { enable_components = local.default_logging_component } } # We define explicit node pools, so that it can be modified without # having to destroy the entire cluster. resource "google_container_node_pool" "system_node_pools" { provider = google-beta count = var.system_node_pool_enabled ? 1 : 0 project = var.project_id name = var.system_node_pool_name cluster = var.cluster_reference_type == "NAME" ? google_container_cluster.gke_cluster.name : google_container_cluster.gke_cluster.self_link location = var.cluster_availability_type == "ZONAL" ? var.zone : var.region version = local.master_version autoscaling { total_min_node_count = var.system_node_pool_node_count.total_min_nodes total_max_node_count = var.system_node_pool_node_count.total_max_nodes } upgrade_settings { strategy = local.upgrade_settings.strategy max_surge = local.upgrade_settings.max_surge max_unavailable = local.upgrade_settings.max_unavailable } management { auto_repair = true auto_upgrade = true } node_config { labels = var.system_node_pool_kubernetes_labels resource_labels = local.labels service_account = var.service_account_email oauth_scopes = var.service_account_scopes machine_type = var.system_node_pool_machine_type disk_size_gb = var.system_node_pool_disk_size_gb disk_type = var.system_node_pool_disk_type dynamic "taint" { for_each = var.system_node_pool_taints content { key = taint.value.key value = taint.value.value effect = taint.value.effect } } # Forcing the use of the Container-optimized image, as it is the only # image with the proper logging daemon installed. # # cos images use Shielded VMs since v1.13.6-gke.0. # https://cloud.google.com/kubernetes-engine/docs/how-to/node-images # # We use COS_CONTAINERD to be compatible with (optional) gVisor. # https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods image_type = var.system_node_pool_image_type shielded_instance_config { enable_secure_boot = var.system_node_pool_enable_secure_boot enable_integrity_monitoring = true } gvnic { enabled = var.system_node_pool_image_type == "COS_CONTAINERD" } # Implied by Workload Identity workload_metadata_config { mode = "GKE_METADATA" } # Implied by workload identity. metadata = { "disable-legacy-endpoints" = "true" } } lifecycle { ignore_changes = [ node_config[0].labels, node_config[0].taint, version, ] precondition { condition = contains(["SURGE"], local.upgrade_settings.strategy) error_message = "Only SURGE strategy is supported" } precondition { condition = local.upgrade_settings.max_unavailable >= 0 error_message = "max_unavailable should be set to 0 or greater" } precondition { condition = local.upgrade_settings.max_surge >= 0 error_message = "max_surge should be set to 0 or greater" } precondition { condition = local.upgrade_settings.max_unavailable > 0 || local.upgrade_settings.max_surge > 0 error_message = "At least one of max_unavailable or max_surge must greater than 0" } } } data "google_client_config" "default" {} provider "kubernetes" { host = "https://${google_container_cluster.gke_cluster.endpoint}" cluster_ca_certificate = base64decode(google_container_cluster.gke_cluster.master_auth[0].cluster_ca_certificate) token = data.google_client_config.default.access_token } module "workload_identity" { count = var.configure_workload_identity_sa ? 1 : 0 source = "terraform-google-modules/kubernetes-engine/google//modules/workload-identity" version = "~> 34.0" use_existing_gcp_sa = true name = var.k8s_service_account_name gcp_sa_name = local.sa_email project_id = var.project_id # https://github.com/terraform-google-modules/terraform-google-kubernetes-engine/issues/1059 depends_on = [ data.google_project.project, google_container_cluster.gke_cluster ] } locals { k8s_service_account_name = one(module.workload_identity[*].k8s_service_account_name) } locals { # Separate gvnic and rdma networks and assign indexes gvnic_networks = [for idx, net in [for n in var.additional_networks : n if strcontains(upper(n.nic_type), "GVNIC")] : merge(net, { name = "${var.k8s_network_names.gvnic_prefix}${idx + var.k8s_network_names.gvnic_start_index}${var.k8s_network_names.gvnic_postfix}" }) ] rdma_networks = [for idx, net in [for n in var.additional_networks : n if strcontains(upper(n.nic_type), "RDMA")] : merge(net, { name = "${var.k8s_network_names.rdma_prefix}${idx + var.k8s_network_names.rdma_start_index}${var.k8s_network_names.rdma_postfix}" }) ] all_networks = concat(local.gvnic_networks, local.rdma_networks) } module "kubectl_apply" { source = "../../management/kubectl-apply" cluster_id = google_container_cluster.gke_cluster.id project_id = var.project_id apply_manifests = flatten([ for idx, network_info in local.all_networks : [ { source = "${path.module}/templates/gke-network-paramset.yaml.tftpl", template_vars = { name = network_info.name, network_name = network_info.network subnetwork_name = network_info.subnetwork, device_mode = strcontains(upper(network_info.nic_type), "RDMA") ? "RDMA" : "NetDevice" } }, { source = "${path.module}/templates/network-object.yaml.tftpl", template_vars = { name = network_info.name } } ] ]) }