GKE Ingress Controller Internals — Reconciliation, External vs Internal ALB, Packet Path
Tại sao cần hiểu controller internals
GKE Ingress controller là một hệ thống reconciliation chạy ngầm. Mọi lỗi trong production — 502 error, health check failure, TLS certificate không được áp dụng, annotation bị bỏ qua — đều có nguyên nhân từ cách controller xử lý spec và tạo GCP resources. Nếu không hiểu cơ chế reconciliation, bạn sẽ debug bằng cách thử-sai mà không có định hướng.
Đặc biệt quan trọng: GKE Ingress đang ở maintenance mode. Hiểu internals của nó giúp bạn không chỉ vận hành hệ thống legacy mà còn quyết định khi nào nên migrate sang Gateway API.
Controller là gì và chạy ở đâu
GKE Ingress controller là một component được Google quản lý trong control plane, không phải một Pod chạy trong cluster của bạn. Đây là điểm khác biệt quan trọng so với các controller tự cài như nginx-ingress: bạn không thể SSH vào xem logs của controller, không thể restart nó, không thể upgrade riêng nó.
Controller được enable bởi HttpLoadBalancing add-on (được bật mặc định trong mọi GKE cluster). Khi add-on này bị tắt, không có Ingress resource nào được reconcile — GCP LB resources sẽ không được tạo ra.
Hoạt động cốt lõi của controller: watch Kubernetes API cho các events liên quan đến Ingress, Service, Endpoint, BackendConfig, FrontendConfig objects. Khi có thay đổi, controller tính toán "desired state" của GCP resources và thực hiện các API calls cần thiết để đạt đến trạng thái đó.
Reconciliation loop — Cơ chế hoạt động chi tiết
Phân loại Ingress bằng annotation
Bước đầu tiên của controller khi nhận một Ingress event là xác định loại Ingress dựa trên annotation kubernetes.io/ingress.class:
# External Application LB (mặc định nếu không có annotation)
metadata:
annotations:
kubernetes.io/ingress.class: "gce"
# Internal Application LB
metadata:
annotations:
kubernetes.io/ingress.class: "gce-internal"Theo tài liệu GKE chính thức: "Note: While the Kubernetes community has deprecated the ingress class annotation in favor of the IngressClass resource, GKE continues relying exclusively on the kubernetes.io/ingress.class annotation."
Nếu annotation không có hoặc là gce, controller tạo External ALB. Nếu là gce-internal, tạo Internal ALB. Nếu là bất kỳ giá trị nào khác (ví dụ: nginx), GKE controller bỏ qua hoàn toàn Ingress đó — để third-party controller xử lý.
Mapping Ingress spec sang GCP resources
Từ một Ingress spec, controller tạo ra một cây GCP resources theo thứ tự phụ thuộc:
Ingress
│
├── Forwarding Rule (external IP + port 80 và/hoặc 443)
│ │
│ └── Target HTTP Proxy / Target HTTPS Proxy
│ │
│ └── URL Map (chứa tất cả host + path rules)
│ │
│ └── Backend Service (một per Service:port combo)
│ │
│ ├── NEG (một per zone)
│ │ └── Endpoints: Pod IPs
│ │
│ └── Health Check
│
└── (nếu HTTPS) SSL CertificateMột điểm quan trọng về Backend Service mapping: Nếu cùng một Kubernetes Service được tham chiếu với cùng port trong nhiều Ingress objects khác nhau, chúng share cùng một GCP Backend Service. Điều này có nghĩa các cấu hình như Cloud Armor policy, CDN settings sẽ áp dụng cho mọi Ingress tham chiếu đến đó.
Ngược lại, nếu cùng một Service được tham chiếu với các ports khác nhau trong cùng một Ingress, mỗi port sẽ tạo ra một Backend Service riêng.
Firewall rules tự động
Một điều controller làm mà ít người chú ý: tự động tạo firewall rules để cho phép health check traffic từ GCP Load Balancer infrastructure đến nodes/pods. Nguồn IP của health check probes:
- External ALB:
130.211.0.0/22và35.191.0.0/16(dải IP của Google Front End) - Internal ALB: Proxy-only subnet (sẽ đề cập ở phần dưới)
Nếu bạn có firewall rules quá restrictive chặn các dải IP này, health checks sẽ fail và backend service sẽ không có healthy backend — dẫn đến 502 errors cho client.
Reconciliation delay ở scale
Controller không reconcile ngay lập tức. Tài liệu chính thức ghi rõ: "A cluster with 20 Ingresses, where each Ingress has 20 distinct NEG backends, may result in a latency of more than 30 minutes" cho các thay đổi để được áp dụng.
Nguyên nhân là controller phải thực hiện nhiều GCP API calls theo thứ tự (do dependency chain), và GCP API rate limiting có thể làm chậm quá trình. Ở scale lớn, đây là constraint thực sự cần tính đến khi thiết kế deployment pipeline.
External Application Load Balancer — Kiến trúc và data path
Đặc điểm kiến trúc
External ALB (annotation gce) là global load balancer. Traffic được xử lý tại Google Front End (GFE) — một lớp proxy toàn cầu nằm tại các Points of Presence (PoP) của Google, không nằm trong VPC của bạn.
Điều này có nghĩa:
- Client kết nối đến một anycast IP — cùng một IP được announced từ nhiều PoP trên thế giới
- BGP routing tự động đưa client đến PoP gần nhất
- TLS termination xảy ra tại PoP, không tại node hay Pod
- Kết nối từ GFE đến backend (Pod) đi qua Google's internal network
Data path chi tiết
Client
│
▼ TCP handshake + TLS handshake (nếu HTTPS)
Google Front End (PoP gần nhất)
│ TLS đã được terminate
│ HTTP/1.1 hoặc HTTP/2 connection mới đến backend
▼
URL Map lookup (host + path matching)
│
▼
Backend Service selection
│
▼
NEG Endpoint selection (weighted round-robin hoặc session affinity)
│ Direct connection đến Pod IP
▼
Pod (containerPort)Vì TLS kết thúc tại GFE, connection từ GFE đến Pod thường là HTTP (plain). Nếu bạn cần end-to-end encryption từ LB đến Pod, cần cấu hình Backend Service với HTTPS hoặc HTTP/2 protocol — điều này yêu cầu Pod phải serve TLS trên containerPort.
Source IP
Vì GFE là proxy, source IP mà Pod nhìn thấy là IP của GFE, không phải IP của client thực. Để có client IP, cần đọc header X-Forwarded-For được GFE tự động thêm vào.
Internal Application Load Balancer — Kiến trúc và constraints
Sự khác biệt cơ bản
Internal ALB (annotation gce-internal) hoàn toàn khác External ALB về mặt kiến trúc. Thay vì GFE nằm ngoài VPC, Internal ALB sử dụng Envoy proxy instances chạy trong VPC của bạn, trên một subnet đặc biệt gọi là proxy-only subnet.
metadata:
annotations:
kubernetes.io/ingress.class: "gce-internal"Proxy-only subnet
Proxy-only subnet là một requirement bắt buộc cho Internal ALB. Đây là một subnet trong VPC với role đặc biệt: chỉ chứa Envoy proxy instances, không chứa VMs hay Pods. Các Envoy instances này nhận IP từ subnet này và dùng IP đó để kết nối đến backends.
# Tạo proxy-only subnet
gcloud compute networks subnets create proxy-only-subnet \
--purpose=REGIONAL_MANAGED_PROXY \
--role=ACTIVE \
--region=us-central1 \
--range=10.129.0.0/23 \
--network=my-vpcNếu proxy-only subnet không tồn tại, Internal Ingress sẽ không thể được provisioned.
Yêu cầu bắt buộc dùng NEG
Theo tài liệu GKE chính thức: Internal ALB bắt buộc phải dùng NEGs. Instance Group-based backends không được hỗ trợ cho Internal ALB. Điều này có nghĩa cloud.google.com/neg: '{"ingress": true}' annotation phải có trên Service, hoặc container-native LB phải được enable tự động.
Data path của Internal ALB
Client (trong VPC)
│
▼
Forwarding Rule (Internal IP trong VPC)
│
▼
Envoy Proxy (IP từ proxy-only subnet)
│ HTTP/HTTPS connection
▼
URL Map lookup
│
▼
Backend Service → NEG → Pod IPSource IP mà Pod thấy sẽ là IP từ proxy-only subnet (IP của Envoy), không phải IP của client. Đây là behavior khác với External ALB.
Packet traversal từ client đến Pod — Toàn bộ con đường
Để làm rõ hơn, đây là full packet trace cho một HTTP request qua External ALB với container-native LB:
Bước 1: Client gửi TCP SYN đến external IP của Ingress.
Bước 2: BGP anycast routing đưa packet đến GFE PoP gần nhất. TCP handshake hoàn tất tại GFE (không phải tại Pod).
Bước 3: Nếu HTTPS, TLS handshake xảy ra giữa client và GFE. GFE dùng certificate được cấu hình trong Target HTTPS Proxy.
Bước 4: GFE đọc HTTP request (host header + path). Lookup URL Map để tìm Backend Service phù hợp.
Bước 5: GFE chọn một NEG endpoint (Pod IP:containerPort) từ Backend Service. Selection dựa trên health check status, session affinity (nếu được cấu hình), và load balancing algorithm.
Bước 6: GFE mở kết nối HTTP mới đến Pod IP trực tiếp qua Google's private network. Headers được forward, X-Forwarded-For được thêm với client IP thực.
Bước 7: Pod nhận request, xử lý, trả response. Response đi ngược lại theo cùng path.
Điểm quan trọng: Không có hop qua kube-proxy, không có DNAT tại node. GFE kết nối thẳng đến Pod IP — điều này chỉ có thể vì GKE dùng VPC-native networking (alias IP), Pod IPs là routable trong VPC.
Constraints và failure modes
Giới hạn số nodes với instance group backends
Với non-NEG backends (instance group), số nodes tối đa là 1,000. Với NEG-based backends, không có giới hạn tương tự (giới hạn là số endpoints per backend service, thường cao hơn nhiều). Đây là lý do tại sao cluster lớn bắt buộc phải dùng container-native LB.
URL Map quota
Mỗi GCP project có quota cho URL Map rules. Một Ingress với nhiều host rules và path rules có thể consume nhiều URL Map rules. Nếu hit quota, controller sẽ fail để reconcile Ingress mới.
Health check behavior
GKE Ingress tự động tạo health check dựa trên một trong hai nguồn:
- Nếu Service có
readinessProbeđược định nghĩa, GKE sẽ cố gắng dùng các tham số tương tự - Nếu không, health check mặc định là HTTP GET
/trên port của Service
Vấn đề: health check của GCP LB là external health check, không phải Kubernetes liveness/readiness probe. Nếu readinessProbe của Pod trả về không-200 status cho path mặc định / nhưng app thực ra healthy, GCP sẽ mark backend là unhealthy. Đây là nguyên nhân phổ biến của backend unhealthy syndrome.
BackendConfig CRD cho phép customize chính xác path và port của health check — xem Chương 03.
TLS certificate management
Ingress hỗ trợ TLS certificate qua:
- Google-managed certificates (
networking.gke.io/managed-certificatesannotation): Google tự provision và renew Let's Encrypt cert - Kubernetes Secrets: cert và key stored trong Secret, Ingress tham chiếu
spec:
tls:
- secretName: my-tls-secret
hosts:
- app.example.comVới Google-managed certs, có thể mất 10-15 phút sau khi Ingress được tạo để cert được provision, trong thời gian đó HTTPS requests sẽ fail.
Không hỗ trợ HTTP/2 upstream
GKE Ingress không hỗ trợ HTTP/2 giữa LB và backend. Nếu bạn đang dùng gRPC (cần HTTP/2), Ingress không phải lựa chọn đúng — Gateway API hỗ trợ HTTP/2 upstream tốt hơn.
Anti-patterns phổ biến
Tắt HttpLoadBalancing add-on
Một số teams tắt add-on này để tránh LB tự động được tạo, nhưng quên rằng điều này cũng tắt toàn bộ Ingress reconciliation. Kết quả: mọi Ingress resource bị bỏ qua hoàn toàn mà không có error message rõ ràng.
Cơ chế: Controller chạy dưới gce-l7 component — khi add-on disabled, component này không được deploy và không watch Ingress events.
Expect instant propagation
Ingress changes không áp dụng ngay. GCP API calls có latency, và ở scale lớn, controller phải sequence nhiều calls. Deploy pipeline nào expect Ingress changes apply trong vài giây sẽ gặp vấn đề — cần wait loop kiểm tra backend health status.
Chia sẻ Service giữa nhiều Ingresses với cấu hình khác nhau
Vì một Service:port pair map đến cùng một GCP Backend Service, cấu hình BackendConfig (Cloud Armor, CDN, etc.) phải nhất quán giữa tất cả Ingresses tham chiếu đến cùng Service. Nếu Ingress A và Ingress B cùng tham chiếu api-service:8080 nhưng với BackendConfigs khác nhau, controller sẽ overwrite lẫn nhau, gây behavior không dự đoán được.