Sau bài này bạn làm được: cấu hình health check đúng để LB không gửi traffic vào instance chết; giải thích connection draining và tại sao thiếu nó gây 502 khi deploy; chọn L4 hay L7 cho từng tình huống.


Load balancer là gì và tại sao cần

Khi có nhiều instance/Pod phục vụ cùng service, cần thành phần phân phối request — đó là Load Balancer:

[Client]
[Load Balancer: 1.2.3.4:443]
    ├──▶ [App instance 1: 10.0.2.10:8080]
    ├──▶ [App instance 2: 10.0.2.11:8080]
    └──▶ [App instance 3: 10.0.2.12:8080]

LB đảm bảo:

  • Không có SPOF (single point of failure) ở tầng app.
  • Scale out: thêm/xóa instance mà client không biết.
  • Health checking: tự loại instance bị lỗi khỏi pool.

L4 vs L7 Load Balancer

L4 (TCP/UDP)L7 (HTTP/HTTPS)
Nhìn tới tầngTCP/UDPHTTP header, URL, cookie
TLSPass-through hoặc terminateThường terminate
RoutingTheo IP:portTheo Host, Path, Header
Hiệu suấtCao hơn (ít xử lý)Thấp hơn (parse HTTP)
Dùng choDatabase proxy, game, streamingWeb API, microservice
Ví dụAWS NLB, TCP mode HAProxyAWS ALB, NGINX, Traefik

Khi nào dùng L4:

  • Cần preserve client IP một cách thuần túy.
  • Protocol không phải HTTP (MQTT, Redis, PostgreSQL wire protocol).
  • Cần throughput cực cao, latency cực thấp.

Khi nào dùng L7:

  • HTTP/HTTPS và cần routing theo path/host.
  • Cần ghi log chi tiết (URL, method, status code).
  • A/B test, canary deploy theo header.

gRPC cần LB biết HTTP/2

Lưu ý quan trọng: gRPC chạy trên HTTP/2, mở một connection rồi ghép nhiều RPC (stream) vào đó và giữ kết nối lâu. Nếu dùng L4 LB (NLB) với chế độ round-robin theo connection, tất cả RPC của một client sẽ đổ vào một backend → mất cân bằng nghiêm trọng khi có ít client nhưng nhiều RPC.

Giải pháp:

  • L7 LB biết HTTP/2 (Envoy, NGINX với grpc_pass, ALB hỗ trợ gRPC, Istio, Linkerd) — load balance theo stream/request không theo connection.
  • Hoặc client-side load balancing với service discovery (gRPC-LB, xDS từ Envoy).
  • Trên Kubernetes: Service ClusterIP + iptables load balance theo connection → gRPC cũng lệch tải; dùng headless Service + DNS round-robin hoặc service mesh để phân tải theo request.

Gateway API — thay thế Ingress ở L7

Với K8s production mới, cân nhắc Gateway API (xem bài 10) thay vì Ingress annotation vendor: tách vai trò platform (Gateway) khỏi team app (HTTPRoute), hỗ trợ weighted routing, gRPC, TCP native. Controller điển hình: Envoy Gateway, Istio, NGINX Gateway Fabric, Cilium.


Thuật toán phân tải

Thuật toánCách hoạt độngPhù hợp
Round RobinLần lượt theo thứ tựRequest xử lý nhanh, đồng đều
Least ConnectionsGửi tới instance ít connection nhấtRequest xử lý lâu, khác nhau
IP HashHash IP client → instance cố địnhSession affinity
WeightedPhân theo trọng sốCanary (10% traffic mới)

Health check — trái tim của LB

LB định kỳ probe từng instance. Nếu fail → loại khỏi pool; khi OK lại → thêm vào pool.

[LB] ─── probe mỗi 10s ──▶ [Instance: GET /health → 200 OK]
                            [Instance: GET /health → 503] ← loại khỏi pool
                            [Instance: timeout 5s]         ← loại khỏi pool

Cấu hình health check quan trọng

Tham sốÝ nghĩaGiá trị điển hình
PathEndpoint probe (HTTP)/health, /ready, /ping
IntervalTần suất probe10–30s
TimeoutThời gian chờ response5s
Healthy thresholdSố lần OK liên tiếp để vào pool2–3
Unhealthy thresholdSố lần fail để loại khỏi pool2–3

Health check endpoint tốt

GET /health → 200 OK
{
  "status": "ok",
  "db": "ok",
  "cache": "ok"
}

GET /health → 503 Service Unavailable (khi DB không reach được)

Không dùng endpoint phức tạp (query DB nặng) làm health check — gây quá tải khi LB probe liên tục.

Phân biệt /health vs /ready:

  • /health (liveness): app còn sống không? Nếu fail → restart pod (K8s).
  • /ready (readiness): app sẵn sàng nhận traffic không? Nếu fail → không gửi traffic, nhưng không restart.

Connection draining / deregistration delay

Khi LB remove một instance (deploy mới, scale down):

Không có draining:
  [LB] ─── stop gửi mới ──▶ [Instance cũ: đang xử lý 50 request]
  [LB] ─── đóng connection ngay ──▶ 50 request bị cắt → client 502

Với connection draining (60s):
  [LB] ─── stop gửi MỚI ──▶ [Instance cũ]
  [LB] ─── chờ tối đa 60s ──▶ [Instance hoàn thành 50 request hiện tại]
  [LB] ─── đóng sau khi drain xong ──▶ Không có 502

Cấu hình điển hình:

  • AWS ALB: “Deregistration delay” = 30–60s.
  • Kubernetes: terminationGracePeriodSeconds + preStop hook.

Graceful shutdown trong app

App cũng phải hỗ trợ graceful shutdown để không cắt request giữa chừng:

[SIGTERM nhận được]
    1. Dừng nhận request mới (close listening socket hoặc trả 503)
    2. Chờ request đang xử lý hoàn thành
    3. Đóng kết nối DB / cache
    4. Exit 0

Node.js ví dụ:

process.on("SIGTERM", () => {
  server.close(() => {
    db.disconnect().then(() => process.exit(0));
  });
  // Force exit nếu quá lâu
  setTimeout(() => process.exit(1), 30000);
});

Tóm tắt

  • L4 cho TCP/UDP thuần; L7 cho HTTP routing và visibility.
  • Health check đúng endpoint và threshold = không có traffic vào instance xấu.
  • Connection draining = deploy không 502; không có draining = cắt connection bất ngờ.
  • App phải handle SIGTERM gracefully — LB draining và app graceful shutdown phải khớp nhau.

Câu hỏi hay gặp

Health check gọi DB mỗi lần probe — có vấn đề không?

Trả lời: Có thể rất tải DB (30×10×instance = số query/phút). Nên endpoint nhẹ (process sống, file tĩnh) hoặc cached check; DB chỉ khi thật cần và throttle.

terminationGracePeriodSeconds: 30 nhưng request dài nhất 60s — điều gì xảy ra?

Trả lời: Sau 30s kubelet SIGKILL (sau SIGTERM); request có thể bị cắt — client 502/reset. Cần tăng grace period, preStop delay, hoặc drain ở LB trước.

Chỉ có L4 NLB mà muốn tách /api/admin — làm được không?

Trả lời: L4 không đọc path HTTP. Cần L7 (ALB, Ingress NGINX, HAProxy HTTP mode) hoặc hai listener/port khác nhau nếu tách bằng cổng.


Bài tiếp theo (Giai đoạn IV): TLS tự động trong cluster với cert-manager — tự động hóa vòng đời chứng chỉ để không bao giờ bị hết hạn bất ngờ.