Bỏ qua tới nội dung chính
← Tất cả bài viết
Kubernetes

Phí cross-AZ: khoản chi âm thầm trong cụm Kubernetes

Service gọi chéo giữa các AZ tạo ra phí truyền dữ liệu ở cả hai chiều. Bài viết đi qua cách đo những luồng này bằng CUR và VPC Flow Logs, rồi giảm chúng bằng trafficDistribution, topology spread và cấu hình load balancer.

CostKernel Engineering9 phút đọc

Khi một cụm EKS được dựng trải trên ba Availability Zone để đảm bảo độ sẵn sàng, phần lớn traffic giữa các service sẽ đi chéo AZ mà không ai chủ động quyết định điều đó. Mỗi GB đi qua ranh giới AZ đều bị tính phí, và khoản phí này không gắn với một service cụ thể nào trên hóa đơn. Bài viết này giải thích cách AWS tính phí cross-AZ, cách đo xem traffic nào đang gây ra nó, và những thay đổi giúp giảm khoản phí này mà vẫn giữ được độ sẵn sàng.

Phí cross-AZ được tính như thế nào

Dữ liệu truyền giữa hai AZ trong cùng một region bị tính phí ở cả hai chiều. Tại thời điểm viết bài, ở phần lớn region, mức phí là $0.01 cho mỗi GB đi ra khỏi AZ nguồn và $0.01 cho mỗi GB đi vào AZ đích. Nói cách khác, mỗi GB đi chéo AZ thực tế tốn khoảng $0.02. Giá có thể khác nhau giữa các region, nên bạn nên kiểm tra lại trang EC2 Data Transfer pricing của region mình đang dùng.

Trong một cụm Kubernetes, kube-proxy mặc định phân phối request tới tất cả endpoint của một Service mà không quan tâm endpoint đó nằm ở AZ nào. Với ba AZ, khoảng hai phần ba số request giữa các service sẽ đi chéo AZ. Nếu một request đi qua nhiều service trước khi trả kết quả, mỗi bước đều có thể phát sinh thêm phí.

Ví dụ giả định

Giả sử các service trong cụm trao đổi với nhau 50 TB dữ liệu mỗi tháng, phân bố đều trên ba AZ:

50.000 GB × 2/3 (đi chéo AZ)   ≈ 33.300 GB
33.300 GB × $0.02 (hai chiều)  ≈ $666 / tháng

Con số này chưa tính traffic giữa ứng dụng với database, cache hay message queue, vốn thường lớn hơn traffic giữa các service với nhau.

Bước 1: tìm xem traffic nào đang đi chéo AZ

Chúng tôi thường bắt đầu từ ba nguồn dữ liệu, đi từ tổng quan tới chi tiết:

  • Cost and Usage Report: lọc theo usage type có dạng <region>-DataTransfer-Regional-Bytes để biết tổng chi phí truyền dữ liệu trong region theo từng tài khoản. Nếu dòng này tăng đều cùng với số node của cụm, nhiều khả năng phần lớn đến từ traffic giữa các pod.
  • VPC Flow Logs: bật trên các subnet của cụm, kèm các trường az-id, pkt-srcaddr và pkt-dstaddr. Sau đó tổng hợp lượng byte theo cặp AZ nguồn và AZ đích để biết luồng nào đang đi chéo.
  • Kubecost hoặc OpenCost: ánh xạ IP về pod, namespace và workload, giúp bạn biết chính xác service nào gọi service nào và tốn bao nhiêu.

Một truy vấn Athena đơn giản trên Flow Logs đã đủ để thấy những luồng lớn nhất:

SELECT az_id, pkt_srcaddr, pkt_dstaddr, sum(bytes) / 1e9 AS gb
FROM vpc_flow_logs
WHERE day BETWEEN '2026/10/01' AND '2026/10/07'
GROUP BY 1, 2, 3
ORDER BY gb DESC
LIMIT 50;

Kết quả thường cho thấy một vài cặp service, hoặc một service và database của nó, chiếm phần lớn lượng traffic. Đó là nơi nên tập trung trước.

Bước 2: giữ traffic trong cùng AZ

Kubernetes có sẵn cơ chế ưu tiên endpoint cùng zone. Từ Kubernetes 1.33, trường trafficDistribution của Service đã ở trạng thái GA. Khi đặt giá trị PreferClose, kube-proxy sẽ gửi request tới các endpoint cùng zone trước, và chỉ chuyển sang zone khác khi zone hiện tại không còn endpoint nào sẵn sàng.

apiVersion: v1
kind: Service
metadata:
  name: orders
spec:
  selector:
    app: orders
  ports:
    - port: 80
      targetPort: 8080
  trafficDistribution: PreferClose

Cơ chế này chỉ hiệu quả khi mỗi zone có đủ pod để xử lý phần traffic của zone đó. Vì vậy cần đi kèm với topologySpreadConstraints để pod của service được dàn đều trên các zone:

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: orders

Với các phiên bản cũ hơn, annotation service.kubernetes.io/topology-mode: Auto (Topology Aware Routing) cho kết quả tương tự, nhưng hành vi khó dự đoán hơn vì controller có thể tự tắt việc ưu tiên zone khi số endpoint giữa các zone chênh lệch. Nếu cụm của bạn đang dùng service mesh như Istio hoặc Linkerd, các mesh này cũng có cấu hình locality-aware load balancing riêng và nên được bật ở đó thay vì ở kube-proxy.

Bước 3: database, cache và load balancer

Traffic giữa ứng dụng với dữ liệu thường lớn hơn nhiều so với traffic giữa các service. Một vài điểm chúng tôi hay kiểm tra:

  • ElastiCache và RDS: ứng dụng thường kết nối tới primary endpoint, nằm ở một AZ duy nhất. Với các truy vấn chỉ đọc, bạn có thể đọc từ replica cùng AZ. Redis cluster mode và các client hỗ trợ đọc từ replica gần nhất giúp giảm đáng kể lượng dữ liệu đi chéo AZ.
  • Kafka và MSK: consumer có thể đọc từ replica cùng AZ thông qua cấu hình client.rack (rack awareness), thay vì luôn đọc từ leader của partition.
  • Network Load Balancer: cross-zone load balancing của NLB mặc định tắt. Nếu bạn bật nó, mỗi GB mà node của NLB chuyển tới target ở AZ khác đều bị tính phí cross-AZ. Với Application Load Balancer, cross-zone luôn bật, và theo tài liệu của AWS, traffic từ ALB tới target ở AZ khác không bị tính phí cross-AZ theo cách tương tự.

Một điểm khác cũng thường gặp: Service kiểu LoadBalancer dùng target type instance khiến traffic đi qua NodePort trên một node bất kỳ, rồi mới tới pod ở node khác, có thể ở AZ khác. Chuyển sang target type ip với AWS Load Balancer Controller giúp load balancer gửi thẳng tới pod và bỏ được bước trung gian này.

Đánh đổi với độ sẵn sàng

Giữ traffic trong cùng AZ không miễn phí về mặt vận hành. Khi một zone có ít pod hơn các zone khác, các pod ở zone đó sẽ phải gánh nhiều tải hơn. Khi một AZ gặp sự cố, traffic vẫn được chuyển sang zone khác, nhưng các zone còn lại cần đủ dung lượng dự phòng để nhận phần tải đó.

Nên ưu tiên cùng AZ khi
  • Service có từ ba replica trở lên, dàn đều trên các zone
  • Traffic giữa các service lớn và ổn định
  • Đã có HPA hoặc dung lượng dự phòng ở mỗi zone
  • Database hoặc cache có replica ở từng AZ
Nên cân nhắc kỹ khi
  • Service chỉ có một hoặc hai replica
  • Traffic dồn vào một zone vào giờ cao điểm
  • Workload nhạy với độ trễ khi failover
  • Lượng traffic chéo AZ nhỏ so với các chi phí khác

Tóm lại

Thứ tự chúng tôi thường áp dụng là: đo lượng traffic chéo AZ bằng CUR và Flow Logs, xác định những cặp service hoặc kết nối tới dữ liệu chiếm phần lớn, rồi bật trafficDistribution: PreferClose cùng topologySpreadConstraints cho các service đó. Sau đó mới xem tới database, cache và cấu hình load balancer. Với nhiều cụm, chỉ riêng việc giữ traffic giữa ứng dụng và cache trong cùng AZ đã giảm được phần lớn khoản phí này.

Đây là loại chi phí mà công cụ tối ưu tự động hiếm khi chỉ ra, vì nó không nằm ở một tài nguyên nào bị lãng phí, mà nằm ở cách các thành phần trong hệ thống gọi nhau.