Multi-Cluster Ingress (MCI) — Global Load Balancing
Tại sao MCI Là Cần Thiết Cho Ingress
MCS giải quyết service-to-service discovery (east-west traffic giữa pods). Nhưng bạn cần gì cho ingress traffic từ clients bên ngoài cluster (north-south)?
┌─────────────────────────────────┐
│ Internet Clients │
├─────────────────────────────────┤
│ │
│ Client: GET http://app.example.com
│
│ Which cluster should handle request?
│ How does GCP know target clusters?
│ How does it maintain health across clusters?
│
└─────────────────────────────────┘Single cluster solution: Standard Ingress + LoadBalancer service (regional LB)
Client → Regional LB → endpoints tất cả ở 1 cluster
Problem: không hay nếu cluster downMulti-cluster solution: Multi-Cluster Ingress (MCI)
Client → Global HTTP(S) LB → anycast IP → nearest POP →
NEG-A (cluster-a endpoints) + NEG-B (cluster-b endpoints) →
health-aware routing → target cluster → podMCI kết nối global load balancer infrastructure của GCP với multiple clusters, cho phép single virtual IP để handle traffic từ tất cả clusters, với automatic failover & locality-aware routing.
Internal Model: Cơ Chế Hoạt Động
Config Cluster Pattern: Star Topology
Multi-Cluster Ingress menggunakan config cluster model:
┌──────────────────────────────────────┐
│ Config Cluster (Cluster C) │
│ (Defines MCI resources) │
│ │
│ MultiClusterIngress resource │
│ MultiClusterService resource │
│ │
│ (Không chạy workloads) │
└──────┬───────────────────────────────┘
│ controls
┌──┴──┐
│ │
┌───▼──┐ ┌─▼────┐
│ A │ │ B │
│ (members) │
└───────┘ └──────┘Cơ chế:
- Config Cluster: Chứa
MultiClusterIngress+MultiClusterServiceđịnh nghĩa - Member Clusters: Chạy Pod, expose thông qua
MultiClusterService - Control plane: GKE managed (tách rời các clusters), phát ngoài global LB
Lý do config cluster: Simplify management — một nơi để define global routing rules, thay vì replicate Ingress ở tất cả clusters.
Network Endpoint Groups (NEGs): Tracking Pod Endpoints
Vấn đề: Global LB cần biết danh sách endpoints (pod IPs) ở tất cả clusters. Nhưng LB không biết gì về Kubernetes.
Giải pháp: Network Endpoint Groups
# GCP Compute resource (created by MCI controller)
apiVersion: compute.cnrm.cloud.google.com/v1beta1
kind: ComputeNetworkEndpointGroup
metadata:
name: cluster-a-neg
namespace: gcp
spec:
networkEndpointType: GCE_VM_IP_PORT # Pod IP + port
networkRef:
name: cluster-a-vpc
zone: us-central1-aTracking mechanism:
Cluster A: ServiceExport api-service
↓ MCS controller
Endpoints: 10.0.1.20:8080, 10.0.1.21:8080
↓
GCP Cloud: Create/update NEG
cluster-a-neg: [10.0.1.20:8080, 10.0.1.21:8080]Key insight: NEG là "list of endpoints" yang GCP LB bisa maintain. Không phải Kubernetes resource — pure GCP resource.
Health Checking Cross-Cluster
┌──────────────────────────────────┐
│ Global LB Health Check │
├──────────────────────────────────┤
│ │
│ Every 5-10 seconds: │
│ ┌──────────────────────────────┐
│ │ Cluster A endpoints: │
│ │ GET /healthz:8080 → 10.0.1.20
│ │ GET /healthz:8080 → 10.0.1.21
│ └──────────────────────────────┘
│
│ ┌──────────────────────────────┐
│ │ Cluster B endpoints: │
│ │ GET /healthz:8080 → 10.1.1.20
│ │ GET /healthz:8080 → 10.1.1.21
│ └──────────────────────────────┘
│ │
│ Status: all healthy │
│ → balance traffic 50/50 │
│ │
│ Status: cluster-b unhealthy │
│ → route 100% to cluster-a │
│ │
└──────────────────────────────────┘Latency: ~10-15 giây từ pod down hingga LB detect + reroute.
Traffic Routing Path
Client (10.50.1.100):
GET http://app.example.com
1. DNS: app.example.com → <MCI-anycast-IP> (e.g., 1.2.3.4)
2. BGP anycast: Client's ISP routes packet tới nearest Google PoP
(user-centric routing → 100+ PoPs globally)
3. Global LB: Packet arrives at PoP
→ lookup backend service
→ check NEG-A, NEG-B health
→ select backend group (preferably same-region)
4. Cross-region routing (if needed):
→ packet tunnels within GCP backbone
→ arrives at cluster VPC
5. Cluster ingestion:
→ Ingress controller (Nginx, ALB, etc.)
→ application podExample:
┌──────────────────────────┐ ┌──────────────────────────┐
│ US Client (10.0.0.0) │ │ EU Client (20.0.0.0) │
└────────────┬─────────────┘ └──────────────┬────────────┘
│ │
│ nearest PoP │ nearest PoP
▼ ▼
┌─────────────┐ ┌─────────────┐
│ US-Central │ │ EU-West │
│ PoP │ │ PoP │
└────┬────────┘ └────┬────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Cluster-US │ │ Cluster-EU │
│ NEG: healthy│ │ NEG: healthy│
│ │ │ │
│ Pod-1 │ │ Pod-3 │
│ Pod-2 │ │ Pod-4 │
└─────────────┘ └─────────────┘
Result: US client hits Cluster-US (low latency, ~10ms)
EU client hits Cluster-EU (low latency, ~8ms)MultiClusterIngress & MultiClusterService Resources
Defining Global Ingress
# Config Cluster: Define global routing
apiVersion: cloud.google.com/v1
kind: MultiClusterIngress
metadata:
name: global-app
namespace: default
spec:
template:
spec:
backend:
serviceName: api-service
servicePort: 8080What it does:
- Create global HTTP(S) load balancer
- Allocate global anycast IP
- Connect to NEGs (dari ServiceExport)
- Setup health checks
What it doesn't do:
- Tidak define routing rules (use HTTPRoute dalam Gateway API untuk itu)
- Không handle TLS termination (konfigurasi terpisah via Certificate resource)
Defining Multi-Cluster Service Targets
# Config Cluster: Which clusters should receive traffic?
apiVersion: net.gke.io/v1
kind: MultiClusterService
metadata:
name: api-service
namespace: default
spec:
template:
spec:
selector:
app: api
ports:
- port: 8080
targetPort: 8080
name: httpMechanism:
MultiClusterService (dalam config cluster)
↓ Controller watches untuk ServiceExport di member clusters
↓ Finds Endpoints dari cluster A, B, C
↓ Creates NEG untuk tiap cluster
↓ Links NEG ke MultiClusterIngressTraffic Splitting & Canary Deployments
MCI dengan NEG memungkinkan traffic splitting per-cluster:
apiVersion: compute.cnrm.cloud.google.com/v1beta1
kind: ComputeBackendService
metadata:
name: mci-backend
spec:
backends:
- group: projects/PROJECT/zones/us-central1-a/networkEndpointGroups/cluster-a-neg
balancingMode: RATE
maxRatePerEndpoint: 1000
- group: projects/PROJECT/zones/us-west1-b/networkEndpointGroups/cluster-b-neg
balancingMode: RATE
maxRatePerEndpoint: 100 # 10x less traffic to cluster-b (canary)Canary workflow:
1. Deploy v1.1 to cluster-b (10% capacity)
2. MCI routes 10% traffic (maxRate: 100 vs 1000)
3. Monitor error rate: if OK, scale up; if FAIL, drain v1.1
4. Gradually: 100→200→500→1000 maxRate
5. Promote: v1.1 becomes stableConstraints & Limitations
Architecture Constraints
- Config cluster must be member: Config cluster juga harus jadi member (tidak bisa pure control plane)
- Same project requirement: Semua clusters harus ở same project (MCI managed resource)
- VPC Peering required: Member clusters harus routable via VPC peering (NEG health check perlu reach pods)
Service Requirements
- Headless service bukan target: MCI hanya bisa target ClusterIP (stateful)
- Health check endpoint required: Service harus expose
/healthzatau endpoint yang bisa di-health-check - Consistent port names: Port harus named dan consistent across clusters
Performance & Scale
- NEG capacity: ~65k endpoints per backend service
- Cluster limit: ~200+ member clusters per MCI (tested)
- Health check latency: ~10s detection + propagation
Production Patterns
✅ Pattern: Global LB + Regional Failover
# Config cluster
apiVersion: cloud.google.com/v1
kind: MultiClusterIngress
metadata:
name: app-ingress
spec:
template:
spec:
backend:
serviceName: app
servicePort: 8080
---
apiVersion: net.gke.io/v1
kind: MultiClusterService
metadata:
name: app
spec:
template:
spec:
selector:
app: api
ports:
- port: 8080
name: http
---
apiVersion: compute.cnrm.cloud.google.com/v1beta1
kind: ComputeHealthCheck
metadata:
name: app-health
spec:
httpHealthCheck:
port: 8080
requestPath: /healthz
checkIntervalSec: 10
timeoutSec: 5
healthyThreshold: 2
unhealthyThreshold: 2Failover behavior:
All clusters healthy: traffic 50/50
Cluster A down: traffic 100% → Cluster B (automatic)
Cluster B down: traffic 100% → Cluster A (automatic)
Both down: Global LB returns 503 error❌ Anti-Pattern: Single Cluster + MCI
# WRONG: Why use MCI if only one cluster?
apiVersion: cloud.google.com/v1
kind: MultiClusterIngress
spec:
template:
spec:
backend:
serviceName: app
servicePort: 8080
# Only cluster-a in fleetBetter: Use standard Ingress + LoadBalancer service:
# RIGHT: Standard Ingress for single cluster
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
ingressClassName: gce # Google-managed LB
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 8080Reason: MCI adds complexity (config cluster, NEGs, fleet). Untuk single cluster, standard Ingress lebih simple.
Common Issues & Debugging
1. MCI IP Not Assigned
Symptom:
kubectl get mci -n default
NAME VIP STATUS
app-ingress <none> PENDINGCauses:
- Config cluster belum di-register sebagai member
- Quota exhausted (global static IPs)
- Network policy blocks LB
Debug:
# Check config cluster status
gcloud container fleet memberships list
# Ensure config cluster di list
# Check quota
gcloud compute project-info describe --project=PROJECT \
--format='value(quotas[name="GLOBAL_STATIC_ADDRESSES"].limit)'
# Check LB resource
gcloud compute forwarding-rules list | grep mci2. NEG Health Check Failing
Symptom:
Cluster A NEG: UNHEALTHY (all endpoints)
Cluster B NEG: HEALTHY
→ Traffic 100% to cluster B (imbalanced)Causes:
- Pod
/healthzendpoint down - Firewall rule blocks health check
- VPC peering not configured (health check can't reach pod IP)
Debug:
# Cluster A: simulate health check
kubectl exec -it <pod> -- curl localhost:8080/healthz
# If fails: fix pod health check endpoint
# Check firewall: GCP health checks from 35.191.0.0/16 and 130.211.0.0/22
gcloud compute firewall-rules list --filter="sourceRanges:35.191.0.0/16"
# If missing, create:
gcloud compute firewall-rules create allow-health-check \
--direction=INGRESS \
--priority=1000 \
--sourceRanges=35.191.0.0/16,130.211.0.0/22 \
--allow=tcp:80803. Asymmetric Routing: A→B fast, B→A slow
Symptom:
Client A → Cluster B: 50ms
Client B → Cluster A: 200ms
Asymmetry indicates: VPC peering one-directional or cross-region routing suboptimalDebug:
# From Cluster A pod, check latency to Cluster B pod
kubectl exec -it <pod-a> -- ping 10.1.1.20 # Cluster B pod IP
# Should be <50ms
# From Cluster B pod, check latency to Cluster A pod
kubectl exec -it <pod-b> -- ping 10.0.1.20 # Cluster A pod IP
# If >100ms, routing suboptimal
# Check VPC peering bidirectional
gcloud compute networks peerings list --network=cluster-a-vpc
gcloud compute networks peerings list --network=cluster-b-vpc
# Should see peering both directions with ACTIVE statusSummary
| Aspect | Implementation |
|---|---|
| Ingress Type | Global HTTP(S) load balancer (GCP managed) |
| Backend Discovery | NEG tracking pod endpoints per cluster |
| Routing | Health-aware, locality-aware (nearest cluster first) |
| Failover | Automatic, <15s detection + reroute |
| Traffic Splitting | Per-NEG maxRate for canary deployments |
| Complexity | High (config cluster, NEG management, health checks) |
Next: MCI & MCS handle north-south & east-west networking. Untuk advanced routing rules (host/path-based routing, request rewriting), gunakan Gateway API — topic selanjutnya.