Skip to content

Commit e711adb

Browse files
chore: add island-cluster example (#1918)
Co-authored-by: Bharath KKB <[email protected]>
1 parent f864e8a commit e711adb

File tree

7 files changed

+455
-0
lines changed

7 files changed

+455
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# GKE island cluster using VM as router
2+
3+
This example provisions a cluster in an island VPC allowing reuse of the IP address space for multiple clusters in the same project.
4+
5+
1. An appliance(VM as router) with multiple NICs is used to establish connectivity between the island VPC and the existing network.
6+
1. Outbound connections will go through the router.
7+
1. For inbound connections, use Private Service Connect.
8+
9+
## Deploy
10+
11+
1. Update `project_id`, `cluster_name` and `primary_subnet` values in `terraform.tfvars`, and update other variables as needed.
12+
1. Run `terraform apply`.
13+
14+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
15+
## Inputs
16+
17+
| Name | Description | Type | Default | Required |
18+
|------|-------------|------|---------|:--------:|
19+
| cluster\_name | n/a | `string` | n/a | yes |
20+
| master\_authorized\_networks | List of master authorized networks. If none are provided, disallow external access (except the cluster node IPs, which GKE automatically whitelists). | `list(object({ cidr_block = string, display_name = string }))` | n/a | yes |
21+
| node\_locations | n/a | `list(string)` | n/a | yes |
22+
| primary\_net\_cidrs | n/a | `list(string)` | n/a | yes |
23+
| primary\_subnet | n/a | `string` | n/a | yes |
24+
| project\_id | n/a | `string` | n/a | yes |
25+
| proxy\_subnet\_cidr | n/a | `string` | n/a | yes |
26+
| psc\_subnet\_cidr | n/a | `string` | n/a | yes |
27+
| region | n/a | `string` | n/a | yes |
28+
| router\_machine\_type | n/a | `string` | n/a | yes |
29+
| secondary\_ranges | n/a | `map(string)` | n/a | yes |
30+
| subnet\_cidr | n/a | `string` | n/a | yes |
31+
32+
## Outputs
33+
34+
| Name | Description |
35+
|------|-------------|
36+
| cluster\_id | n/a |
37+
38+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
resource "random_id" "rand" {
18+
byte_length = 4
19+
}
20+
21+
resource "google_service_account" "gke-sa" {
22+
account_id = "gke-sa-${random_id.rand.hex}"
23+
project = var.project_id
24+
}
25+
26+
module "net" {
27+
source = "terraform-google-modules/network/google"
28+
version = "~> 9.0"
29+
30+
network_name = "gke-net-${random_id.rand.hex}"
31+
routing_mode = "GLOBAL"
32+
project_id = var.project_id
33+
delete_default_internet_gateway_routes = true
34+
35+
subnets = [
36+
{
37+
subnet_name = "${var.cluster_name}-${var.region}-snet"
38+
subnet_ip = var.subnet_cidr
39+
subnet_region = var.region
40+
subnet_private_access = "true"
41+
},
42+
{
43+
subnet_name = "${var.cluster_name}-${var.region}-proxy-snet"
44+
subnet_ip = var.proxy_subnet_cidr
45+
subnet_region = var.region
46+
purpose = "REGIONAL_MANAGED_PROXY"
47+
role = "ACTIVE"
48+
},
49+
{
50+
subnet_name = "${var.cluster_name}-${var.region}-psc-snet"
51+
subnet_ip = var.psc_subnet_cidr
52+
subnet_region = var.region
53+
subnet_private_access = "true"
54+
purpose = "PRIVATE_SERVICE_CONNECT"
55+
}
56+
]
57+
58+
secondary_ranges = {
59+
"${var.cluster_name}-${var.region}-snet" = [
60+
{
61+
range_name = "${var.cluster_name}-${var.region}-snet-pods"
62+
ip_cidr_range = var.secondary_ranges["pods"]
63+
},
64+
{
65+
range_name = "${var.cluster_name}-${var.region}-snet-services"
66+
ip_cidr_range = var.secondary_ranges["services"]
67+
},
68+
]
69+
}
70+
71+
routes = flatten([
72+
[for k, v in var.primary_net_cidrs :
73+
{
74+
name = "${var.cluster_name}-egress-gke-${k}"
75+
description = "egress through the router for range ${v}"
76+
destination_range = v
77+
tags = "gke-${random_id.rand.hex}"
78+
next_hop_instance = google_compute_instance.vm.self_link
79+
priority = 100
80+
}
81+
],
82+
[
83+
{
84+
name = "${var.cluster_name}-default-igw"
85+
description = "internet through the router"
86+
destination_range = "0.0.0.0/0"
87+
tags = "gke-${random_id.rand.hex}"
88+
next_hop_instance = google_compute_instance.vm.self_link
89+
priority = 100
90+
}
91+
]
92+
])
93+
94+
firewall_rules = [
95+
{
96+
name = "${var.cluster_name}-iap"
97+
direction = "INGRESS"
98+
allow = [
99+
{
100+
protocol = "TCP"
101+
ports = ["22"]
102+
}
103+
]
104+
ranges = ["35.235.240.0/20"]
105+
},
106+
{
107+
name = "${var.cluster_name}-tcp-primary"
108+
direction = "INGRESS"
109+
allow = [
110+
{
111+
protocol = "TCP"
112+
}
113+
]
114+
ranges = [
115+
var.subnet_cidr,
116+
var.secondary_ranges["pods"]
117+
]
118+
},
119+
{
120+
name = "${var.cluster_name}-allow-psc"
121+
direction = "INGRESS"
122+
allow = [
123+
{
124+
protocol = "TCP"
125+
}
126+
]
127+
ranges = [var.psc_subnet_cidr]
128+
target_service_accounts = [google_service_account.gke-sa.email]
129+
},
130+
{
131+
name = "${var.cluster_name}-allow-proxy"
132+
direction = "INGRESS"
133+
allow = [
134+
{
135+
protocol = "TCP"
136+
}
137+
]
138+
ranges = [var.proxy_subnet_cidr]
139+
target_service_accounts = [google_service_account.gke-sa.email]
140+
},
141+
]
142+
}
143+
144+
module "gke" {
145+
source = "terraform-google-modules/kubernetes-engine/google//modules/beta-private-cluster"
146+
version = "~> 30.0"
147+
148+
depends_on = [google_compute_instance.vm]
149+
150+
name = var.cluster_name
151+
project_id = var.project_id
152+
region = var.region
153+
release_channel = "RAPID"
154+
zones = var.node_locations
155+
network = module.net.network_name
156+
subnetwork = "${var.cluster_name}-${var.region}-snet"
157+
ip_range_pods = "${var.cluster_name}-${var.region}-snet-pods"
158+
ip_range_services = "${var.cluster_name}-${var.region}-snet-services"
159+
enable_private_endpoint = true
160+
enable_private_nodes = true
161+
datapath_provider = "ADVANCED_DATAPATH"
162+
monitoring_enable_managed_prometheus = false
163+
enable_shielded_nodes = true
164+
master_global_access_enabled = false
165+
master_ipv4_cidr_block = var.secondary_ranges["master_cidr"]
166+
master_authorized_networks = var.master_authorized_networks
167+
deletion_protection = false
168+
remove_default_node_pool = true
169+
disable_default_snat = true
170+
gateway_api_channel = "CHANNEL_STANDARD"
171+
172+
node_pools = [
173+
{
174+
name = "default"
175+
machine_type = "e2-highcpu-2"
176+
min_count = 1
177+
max_count = 100
178+
local_ssd_count = 0
179+
spot = true
180+
local_ssd_ephemeral_count = 0
181+
disk_size_gb = 100
182+
disk_type = "pd-standard"
183+
image_type = "COS_CONTAINERD"
184+
logging_variant = "DEFAULT"
185+
auto_repair = true
186+
auto_upgrade = true
187+
service_account = google_service_account.gke-sa.email
188+
initial_node_count = 1
189+
enable_secure_boot = true
190+
},
191+
]
192+
193+
node_pools_tags = {
194+
all = ["gke-${random_id.rand.hex}"]
195+
}
196+
197+
node_pools_oauth_scopes = {
198+
all = [
199+
"https://www.googleapis.com/auth/logging.write",
200+
"https://www.googleapis.com/auth/monitoring",
201+
]
202+
}
203+
204+
timeouts = {
205+
create = "15m"
206+
update = "15m"
207+
delete = "15m"
208+
}
209+
}
210+
211+
resource "google_gke_hub_membership" "primary" {
212+
provider = google-beta
213+
214+
project = var.project_id
215+
membership_id = "${var.project_id}-${module.gke.name}"
216+
location = var.region
217+
218+
endpoint {
219+
gke_cluster {
220+
resource_link = "//container.googleapis.com/${module.gke.cluster_id}"
221+
}
222+
}
223+
authority {
224+
issuer = "https://container.googleapis.com/v1/${module.gke.cluster_id}"
225+
}
226+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
output "cluster_id" {
18+
value = module.gke.cluster_id
19+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
resource "google_compute_instance" "vm" {
18+
project = var.project_id
19+
zone = var.node_locations[0]
20+
name = "${var.cluster_name}-router-${random_id.rand.hex}"
21+
machine_type = var.router_machine_type
22+
allow_stopping_for_update = true
23+
boot_disk {
24+
initialize_params {
25+
image = "debian-cloud/debian-12"
26+
}
27+
}
28+
can_ip_forward = true
29+
shielded_instance_config {
30+
enable_secure_boot = true
31+
}
32+
network_interface {
33+
subnetwork = var.primary_subnet
34+
}
35+
network_interface {
36+
subnetwork = module.net.subnets["${var.region}/${var.cluster_name}-${var.region}-snet"]["self_link"]
37+
}
38+
metadata_startup_script = <<-EOT
39+
#!/bin/bash
40+
set -ex
41+
sudo apt-get update
42+
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
43+
sudo sysctl -p
44+
sudo iptables -A FORWARD -i ens5 -o ens4 -j ACCEPT
45+
sudo iptables -A FORWARD -i ens4 -o ens5 -m state --state ESTABLISHED,RELATED -j ACCEPT
46+
GWY_URL="http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/1/gateway"
47+
GWY_IP=$(curl $${GWY_URL} -H "Metadata-Flavor: Google")
48+
sudo ip route add ${var.secondary_ranges["pods"]} via $${GWY_IP} dev ens5
49+
sudo iptables -t nat -A POSTROUTING -o ens4 -s 0.0.0.0/0 -j MASQUERADE
50+
EOT
51+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
project_id = "<project_id>"
2+
region = "us-central1"
3+
cluster_name = "gke-island-cluster-test"
4+
node_locations = [
5+
"us-central1-a",
6+
"us-central1-b",
7+
"us-central1-f"
8+
]
9+
subnet_cidr = "100.64.0.0/20"
10+
router_machine_type = "n2-highcpu-4"
11+
primary_subnet = "projects/<project_id>/regions/<region>/subnetworks/<subnet>"
12+
secondary_ranges = {
13+
pods = "100.64.64.0/18"
14+
services = "100.64.128.0/20"
15+
master_cidr = "100.64.144.0/28"
16+
}
17+
proxy_subnet_cidr = "100.64.168.0/24"
18+
psc_subnet_cidr = "100.64.192.0/24"
19+
master_authorized_networks = [
20+
{
21+
cidr_block = "100.64.0.0/10"
22+
display_name = "cluster net"
23+
}
24+
]
25+
primary_net_cidrs = [
26+
"10.0.0.0/8",
27+
"192.168.0.0/16",
28+
"172.16.0.0/12"
29+
]

0 commit comments

Comments
 (0)