Skip to content

Global External Application LB — Hành trình một request từ internet tới Pod

Why this matters in production

Global External Application Load Balancer (gọi tắt là Global ALB) là entry point mặc định cho gần như mọi web app/API public trên GCP. Nó là thứ đứng giữa hàng triệu client trên internet và backend của bạn. Nếu bạn không hiểu chính xác một request đi qua những tầng nào — anycast → Maglev → first-layer GFE → second-layer GFE → backend — thì bạn không thể trả lời những câu hỏi xuất hiện chắc chắn ở production:

  • Vì sao latency của client ở Singapore lại thấp dù backend chỉ ở us-central1? (anycast + cold potato routing)
  • Vì sao khi một region đầy capacity, traffic lại tự "tràn" sang region khác — và làm sao kiểm soát ngưỡng tràn đó? (balancing mode + capacity scaler + waterfall)
  • Vì sao session affinity "cookie" hoạt động nhưng affinity "client IP" lại phân phối lệch khi client đi qua NAT? (cơ chế hashing của affinity)
  • Vì sao backend thấy traffic đến từ 130.211.0.0/2235.191.0.0/16 chứ không phải IP client? (GFE là proxy)

Đây là file dài và sâu nhất chương, vì Global ALB là kiến trúc phức tạp nhất: nó kết hợp anycast, hai tầng proxy, consistent hashing ở tầng L3/4, và một thuật toán chọn region/backend nhiều bước. Hiểu nó một lần cho kỹ thì mọi proxy LB khác (regional, internal) chỉ là phiên bản đơn giản hóa.

Internal model — kiến trúc two-tier GFE

Bức tranh tổng thể: bốn tầng một request đi qua

Theo tài liệu request distribution, một request HTTP từ client tới backend đi qua bốn tầng riêng biệt. Đây là sơ đồ trung tâm của cả file:

   Client (ví dụ ở Singapore)
        │  (1) DNS trả về một anycast VIP duy nhất, ví dụ 34.x.x.x

┌──────────────────────────────────────────────────────────────┐
│  TẦNG 1 — Anycast + Maglev (L3/L4)                            │
│  • Edge router của Google quảng bá VIP ở MỌI biên mạng       │
│  • BGP đưa client tới PoP gần nhất (ví dụ PoP Singapore)     │
│  • Maglev tại PoP đó: consistent hashing + connection         │
│    tracking, coalesce các TCP stream, chọn 1 first-layer GFE  │
└───────────────────────────┬──────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  TẦNG 2 — First-layer GFE (gần client)                        │
│  • Terminate TLS với client NGAY TẠI ĐÂY (RTT thấp)          │
│  • Parse URL map → chọn backend SERVICE (hoặc backend bucket) │
│  • Quyết định region đích dựa trên capacity & proximity       │
└───────────────────────────┬──────────────────────────────────┘
                            ▼  (đi qua backbone nội bộ Google)
┌──────────────────────────────────────────────────────────────┐
│  TẦNG 3 — Second-layer GFE (gần backend)                      │
│  • Nằm trong region/zone chứa backend                        │
│  • Áp dụng balancing mode + capacity → chọn BACKEND cụ thể    │
│    (instance hoặc endpoint NEG) theo locality LB policy       │
└───────────────────────────┬──────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  TẦNG 4 — Backend (VM trong IG, hoặc Pod qua NEG)            │
│  • Nhận request với source IP = 130.211.0.0/22 / 35.191.0.0/16│
│  • Client IP thật nằm trong header X-Forwarded-For            │
└──────────────────────────────────────────────────────────────┘

Điểm mấu chốt cần khắc sâu: có HAI tầng GFE, không phải một. First-layer GFE ở gần client (để terminate TLS sớm, giảm RTT). Second-layer GFE ở gần backend (để chọn backend với thông tin capacity/health tươi nhất tại chỗ). Hai tầng nối với nhau qua backbone riêng của Google. Đây là lý do kiến trúc cho phép vừa terminate TLS sớm vừa chọn backend chính xác — hai mục tiêu mâu thuẫn về địa lý nếu chỉ có một tầng.

Tầng 1 — Anycast và Maglev: vì sao "global" mà không có SPOF

Khi bạn tạo một Global ALB, Google cấp một anycast external VIP và lập trình Maglev để cân bằng tải VIP đó trên toàn cầu tới các GFE ở biên mạng. "Anycast" nghĩa là: cùng một địa chỉ IP được quảng bá qua BGP từ hàng trăm PoP. Client không biết và không cần biết — họ chỉ kết nối tới VIP, và định tuyến internet (BGP) tự đưa packet của họ tới PoP gần nhất về mặt mạng.

Tại mỗi PoP, Maglev — hệ thống cân bằng tải L3/L4 phần mềm của Google — đứng trước GFE. Maglev dùng consistent hashing và connection tracking để gom (coalesce) các TCP stream và đảm bảo mọi packet của cùng một TCP connection tới cùng một first-layer GFE (vì GFE terminate TCP, packet phải tới đúng GFE đang giữ connection đó). Maglev chính là thứ làm cho một địa chỉ VIP đơn lẻ có thể được phục vụ bởi cả một fleet GFE mà vẫn giữ tính nhất quán của connection.

Hệ quả vận hành quan trọng về Premium vs Standard Tier và "cold potato routing": ở Premium Tier, traffic vào mạng Google ở PoP gần client nhất rồi đi phần lớn quãng đường trên backbone riêng của Google (cold potato — tối đa hóa quãng đường trên mạng nhanh của Google). Đây là lý do client ở Singapore vẫn có latency tốt khi backend ở us-central1: phần lớn hành trình đi trên fiber của Google, không phải qua chuỗi ISP công cộng. Standard Tier dùng hot potato (traffic đi trên internet công cộng tới gần region rồi mới vào mạng Google) — rẻ hơn nhưng latency và độ ổn định kém hơn, và Global ALB anycast yêu cầu Premium Tier.

Vì VIP tồn tại đồng thời ở mọi biên và GFE là một fleet phân tán, không có single point of failure: mất một PoP, BGP tự rút route và client được định tuyến tới PoP kế tiếp; mất một GFE, Maglev chọn GFE khác.

Tầng 2 — First-layer GFE: terminate TLS và parse URL map

First-layer GFE là nơi connection của client thực sự kết thúc. Đây là điểm cơ chế quan trọng: TLS handshake xảy ra giữa client và GFE gần nhất, không phải giữa client và backend. Vì handshake TLS tốn 1–2 RTT (phần đắt nhất của việc thiết lập một HTTPS connection), việc terminate ở PoP gần client cắt giảm latency đáng kể — RTT của handshake chỉ còn quãng client→PoP (vài ms) thay vì client→backend (có thể hàng trăm ms xuyên lục địa).

Sau khi terminate TLS, first-layer GFE đọc request HTTP và parse URL map để chọn backend service. Theo tài liệu, "first-layer GFE parse URL map và chọn một backend service hoặc backend bucket". URL map là cây quyết định L7:

URL Map
 ├── host "api.example.com"
 │     ├── path "/v1/*"      → backend-service-api-v1
 │     ├── path "/v2/*"      → backend-service-api-v2
 │     └── path "/*"         → backend-service-api-default
 ├── host "static.example.com"
 │     └── path "/*"         → backend-bucket-static   (Cloud Storage, qua CDN)
 └── default                  → backend-service-web

URL map cho phép routing theo host, path, header, query parameter, và phương thức; cộng thêm các hành động L7 như redirect, rewrite path, thêm/bớt header, và traffic splitting theo trọng số (dùng cho canary/blue-green — gửi 5% sang backend service mới). Tất cả các quyết định này xảy ra ở first-layer GFE, trước khi traffic rời PoP gần client.

Một chi tiết về header mà mọi backend đọc client IP cần biết: GFE thêm vào X-Forwarded-For hai IP — "IP của client kết nối tới LB" và "IP của forwarding rule của LB", ngăn cách bằng dấu phẩy. Nếu client đã tự gửi X-Forwarded-For, GFE thêm vào (append), nên backend phải biết lấy đúng phần tử (thường là phần tử kế cuối) và không tin tưởng mù quáng các phần tử do client tự thêm. Đây là nguồn gốc của nhiều lỗ hổng spoofing IP.

Tầng 3 — Second-layer GFE: chọn backend bằng balancing mode + capacity

First-layer GFE đã chọn được backend serviceregion đích. Việc còn lại — chọn backend instance/endpoint cụ thể — do second-layer GFE (nằm gần backend) thực hiện, vì nó có thông tin capacity và health tươi nhất tại region đó. Đây là nơi balancing modecapacity scaler phát huy tác dụng.

Balancing mode định nghĩa "đầy" nghĩa là gì cho một backend (nguồn: backend services overview):

  • RATE — đo capacity bằng số HTTP request/giây. Cấu hình qua max-rate (cho cả zone), max-rate-per-instance, hoặc max-rate-per-endpoint (cho NEG). Đây là mode phổ biến nhất cho web/API. Ví dụ: một zonal IG có h instance healthy và max-rate-per-instance = X → target capacity của zone = X × h RPS.
  • UTILIZATION — đo bằng CPU utilization của VM (cộng các yếu tố khác). Chỉ áp dụng cho instance group backend, không dùng được cho NEG (NEG không có "VM" để đo CPU). Tài liệu khuyến cáo chỉ dùng UTILIZATION với session affinity NONE.
  • CONNECTION — đo bằng số TCP connection (dùng cho Proxy NLB / L4, không phải mode chính của ALB).
  • IN_FLIGHT (rate of in-flight requests) — đo bằng số request đang xử lý đồng thời, hữu ích cho request có thời lượng dài.

Điểm cơ chế quan trọng nhất, hay bị hiểu sai: balancing mode KHÔNG phải là circuit breaker. Khi một zone đạt target capacity, LB không từ chối request — nó chuyển request mới sang zone khác (nếu có backend ở ≥2 zone). Khi mọi zone đã đạt target capacity, LB overfill proportionally — tức tiếp tục đẩy request vào, vượt quá "target", phân bổ theo tỷ lệ. "Target capacity" là một ngưỡng để bắt đầu lan tỏa, không phải một bức tường cứng. Hiểu sai điều này dẫn tới kỳ vọng "LB sẽ bảo vệ backend khỏi quá tải" — nó không; nó chỉ trải tải, và khi hết chỗ trải, nó vẫn dồn vào.

Capacity scaler (0.0 đến 1.0) là một hệ số nhân thủ công lên target capacity. Đặt 0.5 nghĩa là "chỉ dùng 50% capacity đã khai báo" — hữu ích để drain dần một backend (giảm scaler về 0.0 để rút cạn) hoặc để giữ headroom. Đặt 0 rút cạn hoàn toàn backend (không nhận traffic mới), nhưng không dùng được nếu đó là backend duy nhất.

Cách region được chọn: waterfall và overflow

Với deployment đa region, first-layer GFE chọn region theo nguyên tắc gần nhất có capacity, tràn sang region khác khi đầy. Theo tài liệu: "first-layer GFE chọn second-layer GFE từ một tập các region khả dụng" và có "spillover sang region khác" khi "mọi backend ở region lựa chọn đầu tiên đã đạt capacity".

Locality LB policy mặc định là WATERFALL_BY_REGION: mỗi second-layer GFE ưu tiên chọn backend trong cùng zone với chính nó, rồi lan ra trong region, rồi mới tràn sang region khác. Các policy khác: SPRAY_TO_REGION (rải đều khắp region thay vì ưu tiên cục bộ), WATERFALL_BY_ZONE (đổ đầy zone rồi mới sang zone khác). Khi cần affinity nhất quán theo hash, có RING_HASHMAGLEV như locality policy ở tầng backend.

Mental model đúng: traffic chảy như nước — đổ đầy chỗ gần nhất trước, tràn ra xa khi đầy. "Gần" và "đầy" được định nghĩa bởi proximity của GFE và bởi balancing mode + capacity scaler. Đây là vì sao điều chỉnh max-ratecapacity scaler thực chất là điều chỉnh hình dạng dòng chảy của traffic giữa các region.

Session affinity ở tầng L7

Với Application LB, session affinity quyết định "request của cùng một client có luôn tới cùng một backend không". Các tùy chọn (khác hẳn passthrough — xem file 04):

  • NONE — không affinity; mỗi request được cân bằng độc lập theo balancing mode. Mặc định và tốt nhất cho stateless backend.
  • CLIENT_IP — hash theo IP client. Vấn đề cơ chế: nếu nhiều client sau cùng một NAT/proxy, tất cả chung một IP → tất cả dồn về một backend → phân phối lệch. Affinity này "thô" và dễ gây hotspot.
  • GENERATED_COOKIE — LB tự sinh cookie (GCLB) gắn vào response; request sau mang cookie → LB route về đúng backend. Đây là affinity chính xác nhất vì nó định danh từng client thật, không bị NAT đánh lừa.
  • HTTP_COOKIE / HEADER_FIELD — affinity theo một cookie do app định nghĩa hoặc một header cụ thể; dùng khi bạn muốn kiểm soát khóa affinity.

Điểm cơ chế: affinity là best-effort, không phải đảm bảo tuyệt đối. Khi backend được chọn trở nên unhealthy hoặc vượt capacity, LB sẽ route đi nơi khác — affinity "vỡ" có chủ đích để bảo toàn availability. Đừng thiết kế hệ thống phụ thuộc cứng vào affinity (ví dụ giữ session state chỉ trong RAM của một instance); affinity là tối ưu, không phải hợp đồng.

Constraints, trade-offs & failure modes

Trade-off: anycast/global đổi lấy mất client IP và một hop

Global ALB cho bạn anycast toàn cầu, TLS termination ở edge, URL routing, WAF, CDN — nhưng cái giá cơ chế là: backend không bao giờ thấy client IP ở socket (phải đọc X-Forwarded-For), và mọi request đi qua hai tầng proxy (thêm latency proxy, dù nhỏ). Nếu ứng dụng của bạn bắt buộc cần client IP nguyên bản ở tầng TCP (ví dụ IP allowlisting ở tầng socket, hoặc giao thức non-HTTP), Global ALB không phải lựa chọn — bạn cần passthrough NLB.

Failure mode: balancing mode đặt sai gây "phân phối lệch giả"

Một sự cố kinh điển: team đặt max-rate-per-instance quá cao (ví dụ 10000 RPS trong khi instance chỉ chịu được 1000). Hệ quả: LB tin rằng mỗi instance còn rất nhiều capacity, dồn hết traffic vào zone gần nhất, không tràn sang zone khác → một zone quá tải, các zone khác nhàn rỗi. Nhìn từ ngoài giống "LB phân phối lệch", nhưng thực chất LB đang làm đúng theo capacity bạn khai báo — bạn khai báo sai. Bài học: max-rate phải phản ánh capacity thật của backend (đo bằng load test), nếu không thuật toán waterfall sẽ "đổ nước" sai chỗ.

Failure mode: capacity scaler 0 trên backend duy nhất

Đặt capacity scaler về 0 để drain một backend là kỹ thuật hợp lệ — nhưng nếu đó là backend duy nhất của backend service, LB không còn đích nào để gửi traffic. Tài liệu nêu rõ scaler 0 không khả dụng khi chỉ có một backend. Khi drain, luôn đảm bảo có backend thay thế nhận traffic trước.

Tích hợp Cloud CDN và Cloud Armor

Hai tích hợp này không phải "tính năng rời" mà gắn trực tiếp vào datapath GFE, nên hiểu chúng ở đây giúp hoàn chỉnh bức tranh:

  • Cloud CDN bật ở mức backend service (--enable-cdn). Khi bật, GFE ở edge cache response tại PoP gần client. Cache hit được phục vụ ngay tại edge, không bao giờ chạm second-layer GFE hay backend — đây là lý do CDN giảm tải backend và giảm latency cùng lúc. Cache key, TTL, và chế độ cache (CACHE_ALL_STATIC, USE_ORIGIN_HEADERS...) cấu hình trên backend service. Backend bucket (Cloud Storage) thường đi kèm CDN cho static asset.
  • Cloud Armor gắn vào backend service như một security policy, enforce tại GFE edge — tức request độc hại bị chặn trước khi tới second-layer GFE và backend. Đây là điểm cơ chế quan trọng: vì GFE là proxy đọc HTTP, nó có thể chạy WAF rule (SQLi, XSS) ngay ở biên. Chi tiết ở file 10.

Thứ tự enforce ở GFE (mental model): Cloud Armor (chặn/cho phép) → CDN (cache lookup) → URL map (routing) → backend selection. Một request bị Cloud Armor deny sẽ không bao giờ tiêu tốn capacity backend — đó là toàn bộ giá trị của việc enforce ở edge.

Common mistakes / anti-patterns

Anti-pattern 1 — Tin rằng LB bảo vệ backend khỏi quá tải. Hiểu lầm cơ chế: coi max-rate là circuit breaker. Hậu quả: khi traffic vượt tổng capacity mọi zone, LB overfill và backend sập — team bất ngờ vì "đã đặt max-rate rồi mà". Sửa: max-rate chỉ định hình dòng chảy; bảo vệ quá tải cần autoscaling + rate limiting (Cloud Armor) + circuit breaker ở app.

Anti-pattern 2 — Dùng CLIENT_IP affinity cho client sau NAT. Hiểu lầm: nghĩ affinity theo IP là "đủ tốt". Hậu quả: hàng nghìn client sau một corporate NAT dồn về một backend, gây hotspot. Sửa: dùng GENERATED_COOKIE để định danh từng client thật.

Anti-pattern 3 — Đọc sai X-Forwarded-For. Hiểu lầm: lấy phần tử đầu tiên của X-Forwarded-For làm client IP. Hậu quả: client có thể spoof bằng cách tự thêm header; phần tử đầu là giá trị client tự gửi, không đáng tin. Sửa: với Global ALB, client IP thật là phần tử kế cuối (do GFE thêm vào); cấu hình app/framework hiểu đúng vị trí.

References