An AKS Performance Journey: Part 2 — Networking It Out

Bài tóm tắt ASOS 2021 3. Quy mô lớn Độ khó: Khó

ASOS · 2021 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Phần 2/2. Phần 1: ASOS 2021 (SNAT, SKU, kích thước pod).

Bối cảnh

  • Sau phần 1, PLP trên AKS vẫn chậm hơn Cloud Services khoảng 100 ms ở percentile cao. Log có nhiều timeout gọi API, kèm lỗi getaddrinfo EAI_AGAIN từ NodeJS và các lần chờ đúng 5 giây.

Thay đổi chính

  • Race của conntrack: DNS lookup trong pod đi qua ClusterIP của CoreDNS, kube-proxy (iptables) dùng DNAT. glibc và musl gửi truy vấn A và AAAA song song qua cùng một socket UDP; hai gói đua nhau qua DNAT, một gói bị kernel bỏ, client chờ timeout 5 giây rồi thử lại. Kiểm bằng counter insert_failed của conntrack -S. SNAT cũng có race tương tự khi cấp cổng; cờ --random-fully cho MASQUERADE vào kube-proxy từ 9/2019 (Kubernetes 1.16) nhưng tới khoảng giữa 2020 cloud mới nâng iptables đủ để có tác dụng.
  • Các cách giảm race: single-request-reopen trong dnsConfig (chỉ glibc, musl/Alpine không hỗ trợ); ép NodeJS chỉ dùng IPv4 (family: 4) để bỏ truy vấn AAAA; DNS qua TCP (use-vc), chậm hơn một chút mỗi lần nhưng bỏ được ca 3 lần thử × 5 giây; chạy DNS dạng daemonset để hai gói đua đi tới cùng một server.
  • Mặc định của Kubernetes nhân số truy vấn: ndots: 5 cộng bốn search domain làm www.asos.com bị thử bốn tên nội bộ trước tên thật, nhân hai vì A và AAAA, thành 10 lần resolve, tăng xác suất race. Dùng FQDN có dấu chấm cuối, hoặc đặt ndots: 2: nhanh hơn 20% ở mọi percentile. dnsPolicy: Default nhanh hơn chút nhưng mất tên ngắn của Service.
  • NodeJS không hoàn toàn bất đồng bộ: DNS lookup là lời gọi đồng bộ, được đẩy sang thread pool libuv mặc định 4 thread. PLP có thể làm 5 lookup đồng thời nên pool đầy, lookup xếp hàng và request timeout trước khi kịp bắt đầu. UV_THREADPOOL_SIZE=8 nhanh hơn khoảng 3% và latency mượt hơn hẳn.
  • Keep-alive: theo gợi ý của Microsoft, dùng lại kết nối HTTP (agent keepAlive, gói agentkeepalive) để bớt DNS, handshake và cổng SNAT mới, đồng thời giảm tải DNAT/SNAT: latency giảm một nửa ở hầu hết percentile.

Kết quả (theo ASOS)

  • Tổng hợp các thay đổi, AKS nhanh hơn Cloud Services khoảng 30% (so với Cloud Services đã áp cùng thay đổi NodeJS), và hơn hai lần so với mốc Cloud Services ban đầu; đã chạy production vài tháng.
  • Hướng tiếp: NodeLocal DNSCache (cache DNS trên mỗi node, nói với CoreDNS qua TCP; AKS chưa hỗ trợ vào 2/2021) và chế độ Transparent của Azure CNI (1/2021, giảm race conntrack).

Vì sao

  • Latency đuôi đến từ những chi phí cố định cho mỗi kết nối ra ngoài (DNS, NAT, handshake) bị nhân lên bởi mặc định của nền tảng; dùng lại kết nối là cách gỡ nhiều nhất vì nó trả chi phí đó một lần cho nhiều request (Batching và khấu hao round-trip).
  • Đổi nền tảng chạy làm lộ hành vi của tầng mạng mà ứng dụng không hề đổi; phải đo ở tải thật trước khi chuyển (Migration không downtime).

Bài học cho SWE

  • Thấy timeout đều đặn 5 giây trên Kubernetes thì nghĩ tới DNS và conntrack trước.
  • Kiểm tra ndots và search domain; tên ngoài nên là FQDN.
  • Bật keep-alive cho client HTTP gọi service khác; nó giảm cả latency lẫn tài nguyên NAT.
  • Thread pool ẩn của runtime (libuv) cũng là một hàng đợi có giới hạn.

Liên quan