Cloud NAT với GKE: Egress Node VM & Private Cluster
Tại Sao GKE Đặc Biệt Cần Hiểu Kỹ Cloud NAT
GKE private clusters là kiến trúc tiêu chuẩn cho production — không có external IP trên nodes, control plane không accessible từ internet, bảo mật network mạnh hơn. Nhưng kiến trúc này tạo ra một dependency cứng: Cloud NAT là mandatory cho egress.
Điều phức tạp là mô hình traffic của GKE khác với Compute Engine thuần:
- Mỗi node là một VM, nhưng nó host nhiều pods đồng thời
- Pods share outbound connectivity qua node's NAT allocation
- Node có alias IP ranges (Pod CIDR) — thay đổi cách Cloud NAT tính minimum ports
- GKE Autopilot có behavior khác với GKE Standard
Hiểu những đặc điểm này là bắt buộc cho capacity planning và troubleshooting GKE NAT issues.
Internal Model: Pod Egress Flow Qua Cloud NAT
Luồng Traffic Từ Pod Ra Internet
Trong GKE VPC-native cluster, mỗi pod có một IP từ alias IP range của node. Khi pod muốn reach internet:
Pod (10.56.1.5:random_port) → Internet (142.250.1.1:443/TCP)
Bước 1: Pod gửi packet với source IP = Pod IP
└─ Source: 10.56.1.5:52340
└─ Destination: 142.250.1.1:443
Bước 2: Packet đến node's network namespace
└─ ip-masq-agent (hoặc kubelet masquerade) kiểm tra destination
└─ Nếu destination là non-RFC1918: apply masquerade (SNAT tại node)
Source → 10.128.0.5:52341 (node's primary IP và ephemeral port)
└─ Nếu destination là RFC1918: KHÔNG masquerade, giữ pod IP
Bước 3: Packet rời node với source = Node Primary IP
└─ Source: 10.128.0.5:52341
└─ Destination: 142.250.1.1:443
Bước 4: Andromeda/Cloud NAT thực hiện SNAT thứ hai
└─ Source: 34.100.200.50:44001 (NAT external IP)
└─ Destination: 142.250.1.1:443
Bước 5: Packet ra internet với NAT external IPCó hai lớp SNAT:
- Node-level masquerade (iptables/nftables trên node): Pod IP → Node IP
- Cloud NAT SNAT (Andromeda): Node IP → NAT external IP
Lớp đầu tiên xảy ra trên node OS. Lớp thứ hai xảy ra trong Andromeda dataplane.
ip-masq-agent: Kiểm Soát Lớp Đầu
ip-masq-agent là DaemonSet chạy trên mỗi GKE node, quản lý iptables rules quyết định traffic nào được masquerade (SNAT) tại node level trước khi ra Cloud NAT.
Logic mặc định:
- Traffic đến RFC 1918 addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16): không masquerade — giữ pod IP, vì đây là traffic nội bộ VPC
- Traffic đến non-RFC 1918 addresses (internet): masquerade → thay bằng node IP
Điều này có nghĩa là Cloud NAT chỉ nhìn thấy Node IP làm source, không nhìn thấy Pod IP. Cloud NAT phân bổ port cho Node IP, không phải cho từng Pod.
Tùy chỉnh ip-masq-agent:
# ConfigMap ip-masq-agent
apiVersion: v1
kind: ConfigMap
metadata:
name: ip-masq-agent
namespace: kube-system
data:
config: |
nonMasqueradeCIDRs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 100.64.0.0/10 # Shared address space, không masquerade
masqLinkLocal: false
resyncInterval: 60sNếu bạn muốn một IP cụ thể không đi qua Cloud NAT (ví dụ: traffic đến một on-prem service qua VPN không cần SNAT), thêm CIDR đó vào nonMasqueradeCIDRs.
Alias IP và NAT Scope
GKE VPC-native nodes có alias IP ranges — mỗi node được gán một Pod CIDR (ví dụ /24 với 256 addresses) ngoài primary IP của node. Cloud NAT phải được cấu hình đúng scope để cover cả Pod CIDR:
# NAT scope phải cover cả primary IP và alias IP ranges:
gcloud compute routers nats create NAT_GATEWAY_NAME \
--router=ROUTER_NAME \
--region=REGION \
--nat-all-subnet-ip-ranges # Cover tất cả: primary + alias IP rangesHoặc --nat-all-subnet-ip-ranges tương đương với ALL_SUBNETWORKS_ALL_IP_RANGES trong API. Nếu chỉ set --nat-primary-subnet-ip-ranges, Pod IPs sẽ không được NAT và pod không thể ra internet.
Tại Sao GKE Private Cluster Bắt Buộc Cloud NAT
Anatomy of Private Cluster
Trong GKE private cluster:
- Node VMs không có external IP
- Control plane endpoint có thể có private endpoint only
- Pods (với VPC-native) cũng không có external IP
Điều này có nghĩa là mọi outbound traffic đều phải qua Cloud NAT. Bao gồm:
- Node pull container images từ Artifact Registry
- Kubelet liên lạc với control plane (nếu dùng public endpoint)
- Application pods gọi Google APIs (Cloud Storage, BigQuery, Pub/Sub, etc.)
- Workloads gọi external services (payment gateways, SMS providers, external APIs)
Không có Cloud NAT → private cluster nodes không thể làm bất kỳ điều nào trong số này.
Artifact Registry Pull: Dependency Quan Trọng
Khi node cần pull container image để chạy Pod, đây là thao tác outbound từ node đến Artifact Registry. Nếu Cloud NAT không có hoặc bị cạn kiệt port, kubelet không thể pull image và Pod sẽ stuck ở ImagePullBackOff.
Đây là lý do Cloud NAT exhaustion trong GKE có thể gây ra cluster-wide scheduling failures: không phải chỉ applications bị ảnh hưởng mà cả Kubernetes control operations cũng bị block.
Private Google Access vs Cloud NAT
Có một điểm tinh tế: Private Google Access (PGA) cho phép nodes truy cập Google APIs (Storage, BigQuery...) mà không cần Cloud NAT nếu sử dụng restricted.googleapis.com hoặc private.googleapis.com endpoints.
Không cần Cloud NAT (với Private Google Access):
Node → 199.36.153.4/30 (restricted.googleapis.com)
→ Đi qua GFE edge nội bộ Google, không ra internet
Cần Cloud NAT:
Node → Non-Google internet destination (external APIs)
Node → DockerHub, GitHub Container Registry (nếu không dùng Artifact Registry)Strategy tốt nhất:
- Bật Private Google Access trên subnet → giảm load trên Cloud NAT
- Mirror images vào Artifact Registry → loại bỏ nhu cầu pull từ DockerHub
- Cloud NAT chỉ cần cho các destination thực sự là internet
Capacity Planning Cho GKE: Tính Theo Node, Không Phải Pod
Đơn Vị Phân Bổ Port Là Node, Không Phải Pod
Đây là điểm gây nhầm lẫn phổ biến nhất. Cloud NAT phân bổ port theo Node VM, không phải theo Pod. Nếu một node chạy 50 pods và mỗi pod duy trì 10 concurrent connections đến internet:
Ports cần thiết cho 1 node:
50 pods × 10 concurrent connections = 500 connections đồng thời
+ Buffer cho TIME_WAIT: 500 × TIME_WAIT_factor
→ Cần ~512–1.024 ports/node
Nếu cấu hình 64 ports/VM (mặc định):
→ 64 ports / 50 pods ≈ 1.28 connections/pod ← KHÔNG ĐỦ!Recommendation cho GKE nodes: tăng minimum ports-per-VM lên ít nhất 1.024 hoặc bật Dynamic Port Allocation với max = 4.096 hoặc 8.192.
Công Thức Capacity Planning GKE
Tổng pods trên cluster = Nodes × max-pods-per-node
Ports cần thiết per node:
= (max-pods-per-node) × (avg concurrent connections per pod) × TIME_WAIT_multiplier
TIME_WAIT_multiplier ≈ 2–4 (tùy workload)
Số NAT IPs cần thiết:
= ⌈(Nodes × ports-per-node) / 64.512⌉Ví dụ thực tế:
Cluster với:
- 100 nodes
- 110 pods/node (max-pods-per-node)
- Mỗi pod: 5 concurrent HTTP connections đến external APIs
- TIME_WAIT multiplier: 3×
Ports per node = 110 × 5 × 3 = 1.650 ports/node
Tổng ports cần = 100 × 1.650 = 165.000 ports
NAT IPs cần = ⌈165.000 / 64.512⌉ = 3 IPsVới Dynamic Port Allocation:
min-ports-per-vm = 256
max-ports-per-vm = 4.096
Baseline (tất cả nodes ở minimum):
100 × 256 = 25.600 ports → 1 NAT IP đủ cho baseline
Peak (tất cả nodes spike):
100 × 4.096 = 409.600 ports → cần 7 NAT IPs
→ Nhưng trong thực tế không phải tất cả nodes spike cùng lúc
→ 3–4 NAT IPs thường đủ với auto-allocationAlias IP và Minimum Port Adjustment
Khi GKE VPC-native node có alias IP ranges, Cloud NAT tự động điều chỉnh minimum static allocation:
Effective minimum = max(configured_minimum, 1.024)Điều này có nghĩa là ngay cả khi bạn cấu hình minimum-ports-per-vm = 64, Cloud NAT sẽ thực sự phân bổ 1.024 ports cho mỗi GKE node có alias IPs. Điều này thực ra là bảo vệ bạn khỏi misconfiguration — nhưng cũng có nghĩa là capacity planning phải dùng 1.024 làm baseline, không phải giá trị configured.
Cấu Hình Cloud NAT Cho GKE Private Cluster
Tạo Cloud NAT Cho GKE
# 1. Tạo Cloud Router (nếu chưa có)
gcloud compute routers create GKE_ROUTER \
--network=NETWORK_NAME \
--region=REGION
# 2. Tạo Cloud NAT gateway với tất cả subnet IP ranges
gcloud compute routers nats create GKE_NAT_GATEWAY \
--router=GKE_ROUTER \
--region=REGION \
--auto-allocate-nat-external-ips \
--nat-all-subnet-ip-ranges \
--enable-logging--nat-all-subnet-ip-ranges là critical: nếu chỉ dùng --nat-primary-subnet-ip-ranges, Pod IPs (từ alias IP range = secondary subnet range) sẽ không được NAT.
Dynamic Port Allocation Cho GKE
gcloud compute routers nats update GKE_NAT_GATEWAY \
--router=GKE_ROUTER \
--region=REGION \
--enable-dynamic-port-allocation \
--min-ports-per-vm=1024 \
--max-ports-per-vm=8192 \
--tcp-time-wait-timeout=30sVới min-ports-per-vm=1024 và max-ports-per-vm=8192:
- Baseline: 1.024 ports/node (đủ cho node nhẹ)
- Peak: đến 8.192 ports/node khi burst
Terraform Configuration
resource "google_compute_router_nat" "gke_nat" {
name = "gke-nat-gateway"
router = google_compute_router.gke_router.name
region = var.region
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
enable_dynamic_port_allocation = true
min_ports_per_vm = 1024
max_ports_per_vm = 8192
tcp_time_wait_timeout_sec = 30
tcp_established_idle_timeout_sec = 1200
tcp_transitory_idle_timeout_sec = 30
udp_idle_timeout_sec = 30
log_config {
enable = true
filter = "ERRORS_ONLY" # ALL, TRANSLATIONS_ONLY, ERRORS_ONLY
}
}GKE Autopilot và Cloud NAT
Autopilot Private Cluster
GKE Autopilot quản lý nodes transparent — bạn không cấu hình node pool trực tiếp. Tuy nhiên, Cloud NAT setup cho Autopilot private cluster là giống nhau về mặt networking:
- Tạo Cloud Router trong VPC/region của cluster
- Tạo Cloud NAT gateway với
--nat-all-subnet-ip-ranges - Auto-allocation IPs
Autopilot nodes vẫn là VMs và cần NAT cho outbound traffic.
Capacity Planning Khó Hơn Với Autopilot
Thách thức với Autopilot: bạn không biết trước số nodes vì Autopilot auto-scale. Recommendation:
- Dùng
AUTO_ONLYIP allocation (Cloud NAT tự thêm IP khi cần) - Bật Dynamic Port Allocation với generous max (16.384 ports/VM)
- Monitor
nat_allocation_failedmetric và set alert - Đặt hard limit trên số NAT IPs nếu cần (tránh unexpected cost)
ip-masq-agent Trên Autopilot
Autopilot managed ip-masq-agent — bạn không thể tự cấu hình DaemonSet này. Autopilot có default masquerade policy:
- Traffic đến private RFC 1918 ranges: không masquerade
- Traffic ra internet: masquerade → node IP → Cloud NAT
Nếu cần custom masquerade policy (ví dụ: không masquerade traffic đến một CIDR cụ thể), dùng NetworkConfig.enableFqdnNetworkPolicy hoặc contact Google Cloud support về Autopilot networking customization.
Anti-Pattern: Dùng Cloud NAT Cho Traffic Trong VPC
Một anti-pattern phổ biến: cấu hình --nat-all-subnet-ip-ranges và để ip-masq-agent với default config, dẫn đến tất cả traffic (kể cả traffic VPC-internal) đều đi qua node-level masquerade.
Vấn đề: Traffic giữa pods trong cùng cluster, hoặc giữa cluster và các services trong VPC, không nên bị SNAT. SNAT tại node level:
- Che giấu pod IP — service nhận thấy node IP, không phải pod IP
- Gây khó debug network policy violations
- Tạo load không cần thiết trên connection tracking
Đảm bảo: ip-masq-agent phải có nonMasqueradeCIDRs cover toàn bộ VPC address space (bao gồm cả Pod CIDR, Service CIDR, và tất cả secondary ranges).
Failure Mode: NAT Exhaustion Gây Cascade Failure
Khi Cloud NAT exhausted trong GKE cluster, impact có thể cascade:
Port exhaustion xảy ra
↓
Kubelet không pull được image từ Artifact Registry
↓
Pods mới không thể start → stuck ở ImagePullBackOff
↓
Cluster không thể schedule workloads mới
↓
Rolling deploys fail
↓
Auto-scaling triggers nhưng nodes mới cũng không pull được images
↓
Cluster effectively non-functional mặc dù nodes "healthy"Đây là lý do tại sao nat_allocation_failed và dropped_sent_packets_count phải trong danh sách alert bắt buộc của bất kỳ GKE production environment nào.