This blueprint presents an opinionated architecture to handle multiple homogeneous GKE clusters. The general idea behind this blueprint is to deploy a single project hosting multiple clusters leveraging several useful GKE features.
The pattern used in this design is useful, for blueprint, in cases where multiple clusters host/support the same workloads, such as in the case of a multi-regional deployment. Furthermore, combined with Anthos Config Sync and proper RBAC, this architecture can be used to host multiple tenants (e.g. teams, applications) sharing the clusters.
This blueprint is used as part of the FAST GKE stage but it can also be used independently if desired.
The overall architecture is based on the following design decisions:
- All clusters are assumed to be private, therefore only VPC-native clusters are supported.
- Logging and monitoring configured to use Cloud Operations for system components and user workloads.
- GKE metering enabled by default and stored in a bigquery dataset created within the project.
- Optional GKE Fleet support with the possibility to enable any of the following features:
- Support for Config Sync, Hierarchy Controller, and Policy Controller when using Anthos Config Management.
- Groups for GKE can be enabled to facilitate the creation of flexible RBAC policies referencing group principals.
- Support for application layer secret encryption.
- Support to customize peering configuration of the control plane VPC (e.g. to import/export routes to the peered network)
- Some features are enabled by default in all clusters:
The following example shows how to deploy a single cluster and a single node pool
module "gke" {
source = "./fabric/blueprints/gke/multitenant-fleet/"
project_id = var.project_id
billing_account_id = var.billing_account_id
folder_id = var.folder_id
prefix = "myprefix"
vpc_config = {
host_project_id = "my-host-project-id"
vpc_self_link = "projects/my-host-project-id/global/networks/my-network"
}
authenticator_security_group = "[email protected]"
group_iam = {
"[email protected]" = [
"roles/container.admin"
]
}
iam = {
"roles/container.clusterAdmin" = [
"[email protected]"
]
}
clusters = {
mycluster = {
cluster_autoscaling = null
description = "My cluster"
dns_domain = null
location = "europe-west1"
labels = {}
net = {
master_range = "172.17.16.0/28"
pods = "pods"
services = "services"
subnet = "projects/my-host-project-id/regions/europe-west1/subnetworks/mycluster-subnet"
}
overrides = null
}
}
nodepools = {
mycluster = {
mynodepool = {
initial_node_count = 1
node_count = 1
node_type = "n2-standard-4"
overrides = null
spot = false
}
}
}
}
# tftest modules=5 resources=26
The following example shows how to deploy two clusters with different configurations.
The first cluster cluster-euw1
defines the mandatory configuration parameters (description, location, network setup) and inherits the some defaults from the cluster_defaults
and nodepool_deaults
variables. These two variables are used whenever the override
key of the clusters
and nodepools
variables are set to null
.
On the other hand, the second cluster (cluster-euw3
) defines its own configuration by providing a value to the overrides
key.
module "gke" {
source = "./fabric/blueprints/gke/multitenant-fleet/"
project_id = var.project_id
billing_account_id = var.billing_account_id
folder_id = var.folder_id
prefix = "myprefix"
vpc_config = {
host_project_id = "my-host-project-id"
vpc_self_link = "projects/my-host-project-id/global/networks/my-network"
}
clusters = {
cluster-euw1 = {
cluster_autoscaling = null
description = "Cluster for europ-west1"
dns_domain = null
location = "europe-west1"
labels = {}
net = {
master_range = "172.17.16.0/28"
pods = "pods"
services = "services"
subnet = "projects/my-host-project-id/regions/europe-west1/subnetworks/euw1-subnet"
}
overrides = null
}
cluster-euw3 = {
cluster_autoscaling = null
description = "Cluster for europe-west3"
dns_domain = null
location = "europe-west3"
labels = {}
net = {
master_range = "172.17.17.0/28"
pods = "pods"
services = "services"
subnet = "projects/my-host-project-id/regions/europe-west3/subnetworks/euw3-subnet"
}
overrides = {
cloudrun_config = false
database_encryption_key = null
gcp_filestore_csi_driver_config = true
master_authorized_ranges = {
rfc1918_1 = "10.0.0.0/8"
}
max_pods_per_node = 64
pod_security_policy = true
release_channel = "STABLE"
vertical_pod_autoscaling = false
}
}
}
nodepools = {
cluster-euw1 = {
pool-euw1 = {
initial_node_count = 1
node_count = 1
node_type = "n2-standard-4"
overrides = null
spot = false
}
}
cluster-euw3 = {
pool-euw3 = {
initial_node_count = 1
node_count = 1
node_type = "n2-standard-4"
overrides = {
image_type = "UBUNTU_CONTAINERD"
max_pods_per_node = 64
node_locations = []
node_tags = []
node_taints = []
}
spot = true
}
}
}
}
# tftest modules=7 resources=28
This example deploys two clusters and configures several GKE Fleet features:
- Enables multi-cluster ingress and sets the configuration cluster to be
cluster-eu1
. - Enables Multi-cluster services and assigns the required roles to its service accounts.
- A
default
Config Management template is created with binary authorization, config sync enabled with a git repository, hierarchy controller, and policy controller. - The two clusters are configured to use the
default
Config Management template.
module "gke" {
source = "./fabric/blueprints/gke/multitenant-fleet/"
project_id = var.project_id
billing_account_id = var.billing_account_id
folder_id = var.folder_id
prefix = "myprefix"
vpc_config = {
host_project_id = "my-host-project-id"
vpc_self_link = "projects/my-host-project-id/global/networks/my-network"
}
clusters = {
cluster-euw1 = {
cluster_autoscaling = null
description = "Cluster for europe-west1"
dns_domain = null
location = "europe-west1"
labels = {}
net = {
master_range = "172.17.16.0/28"
pods = "pods"
services = "services"
subnet = "projects/my-host-project-id/regions/europe-west1/subnetworks/euw1-subnet"
}
overrides = null
}
cluster-euw3 = {
cluster_autoscaling = null
description = "Cluster for europe-west3"
dns_domain = null
location = "europe-west3"
labels = {}
net = {
master_range = "172.17.17.0/28"
pods = "pods"
services = "services"
subnet = "projects/my-host-project-id/regions/europe-west3/subnetworks/euw3-subnet"
}
overrides = null
}
}
nodepools = {
cluster-euw1 = {
pool-euw1 = {
initial_node_count = 1
node_count = 1
node_type = "n2-standard-4"
overrides = null
spot = false
}
}
cluster-euw3 = {
pool-euw3 = {
initial_node_count = 1
node_count = 1
node_type = "n2-standard-4"
overrides = null
spot = true
}
}
}
fleet_features = {
appdevexperience = false
configmanagement = true
identityservice = true
multiclusteringress = "cluster-euw1"
multiclusterservicediscovery = true
servicemesh = true
}
fleet_workload_identity = true
fleet_configmanagement_templates = {
default = {
binauthz = true
config_sync = {
git = {
gcp_service_account_email = null
https_proxy = null
policy_dir = "configsync"
secret_type = "none"
source_format = "hierarchy"
sync_branch = "main"
sync_repo = "https://github.com/myorg/myrepo"
sync_rev = null
sync_wait_secs = null
}
prevent_drift = true
source_format = "hierarchy"
}
hierarchy_controller = {
enable_hierarchical_resource_quota = true
enable_pod_tree_labels = true
}
policy_controller = {
audit_interval_seconds = 30
exemptable_namespaces = ["kube-system"]
log_denies_enabled = true
referential_rules_enabled = true
template_library_installed = true
}
version = "1.10.2"
}
}
fleet_configmanagement_clusters = {
default = ["cluster-euw1", "cluster-euw3"]
}
}
# tftest modules=8 resources=39
name | description | modules |
---|---|---|
gke-clusters.tf | None | gke-cluster |
gke-hub.tf | None | gke-hub |
gke-nodepools.tf | None | gke-nodepool |
main.tf | Module-level locals and resources. | bigquery-dataset · project |
outputs.tf | Output variables. | |
variables.tf | Module variables. |
name | description | type | required | default | producer |
---|---|---|---|---|---|
billing_account_id | Billing account id. | string |
✓ | ||
clusters | map(object({…})) |
✓ | |||
folder_id | Folder used for the GKE project in folders/nnnnnnnnnnn format. | string |
✓ | ||
nodepools | map(map(object({…}))) |
✓ | |||
prefix | Prefix used for resources that need unique names. | string |
✓ | ||
project_id | ID of the project that will contain all the clusters. | string |
✓ | ||
vpc_config | Shared VPC project and VPC details. | object({…}) |
✓ | ||
authenticator_security_group | Optional group used for Groups for GKE. | string |
null |
||
cluster_defaults | Default values for optional cluster configurations. | object({…}) |
{…} |
||
dns_domain | Domain name used for clusters, prefixed by each cluster name. Leave null to disable Cloud DNS for GKE. | string |
null |
||
fleet_configmanagement_clusters | Config management features enabled on specific sets of member clusters, in config name => [cluster name] format. | map(list(string)) |
{} |
||
fleet_configmanagement_templates | Sets of config management configurations that can be applied to member clusters, in config name => {options} format. | map(object({…})) |
{} |
||
fleet_features | Enable and configue fleet features. Set to null to disable GKE Hub if fleet workload identity is not used. | object({…}) |
null |
||
fleet_workload_identity | Use Fleet Workload Identity for clusters. Enables GKE Hub if set to true. | bool |
false |
||
group_iam | Project-level IAM bindings for groups. Use group emails as keys, list of roles as values. | map(list(string)) |
{} |
||
iam | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | map(list(string)) |
{} |
||
labels | Project-level labels. | map(string) |
{} |
||
nodepool_defaults | object({…}) |
{…} |
|||
peering_config | Configure peering with the control plane VPC. Requires compute.networks.updatePeering. Set to null if you don't want to update the default peering configuration. | object({…}) |
{…} |
||
project_services | Additional project services to enable. | list(string) |
[] |
name | description | sensitive | consumers |
---|---|---|---|
cluster_ids | Cluster ids. | ||
clusters | Cluster resources. | ||
project_id | GKE project id. |