Nhiều replica không tự động tạo high availability. Nếu scheduler đặt tất cả Pod lên cùng node hoặc cùng availability zone, một sự cố duy nhất vẫn có thể làm service biến mất. Kubernetes Topology Spread Constraints cho phép mô tả mức phân bố mong muốn theo zone, node hoặc fault domain tùy chỉnh—nhưng hiệu quả phụ thuộc vào label chính xác, selector đúng, capacity thật và lựa chọn rõ giữa “không được schedule” với “schedule dù chưa cân bằng”.
Bài viết dựa trên tài liệu chính thức về Pod Topology Spread Constraints và kube-scheduler configuration. Một số field nâng cao phụ thuộc phiên bản/feature gate; hãy dùng kubectl explain và tài liệu đúng minor version của cluster.
1. Fault domain là gì?
Một topology domain là một giá trị của node label. Với topology.kubernetes.io/zone, các node cùng giá trị zone thuộc một domain; với kubernetes.io/hostname, mỗi node thường là một domain.
| Mục tiêu | Topology key thường dùng | Sự cố muốn chịu |
|---|---|---|
| Rải qua region | topology.kubernetes.io/region | Mất region, cần kiến trúc dữ liệu/mạng tương ứng |
| Rải qua zone | topology.kubernetes.io/zone | Mất availability zone |
| Rải qua node | kubernetes.io/hostname | Node reboot, drain hoặc hỏng |
| Rải qua rack | Label tổ chức quản lý | Mất rack, nguồn hoặc switch |
Label chỉ có giá trị nếu phản ánh hạ tầng thật và được gắn nhất quán. Tự đặt label zone-a cho node cùng một phòng máy không tạo khả năng chịu lỗi vật lý.
2. Manifest hai tầng zone và node
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
spec:
replicas: 6
selector:
matchLabels:
app: payments-api
template:
metadata:
labels:
app: payments-api
spec:
topologySpreadConstraints:
- maxSkew: 1
minDomains: 3
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: payments-api
matchLabelKeys:
- pod-template-hash
nodeAffinityPolicy: Honor
nodeTaintsPolicy: Honor
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: payments-api
matchLabelKeys:
- pod-template-hash
containers:
- name: app
image: registry.example.com/payments/api@sha256:<digest>
resources:
requests:
cpu: 250m
memory: 256Mi
Ví dụ này yêu cầu revision hiện tại trải qua ít nhất ba zone với skew tối đa 1; trong mỗi zone scheduler ưu tiên rải qua node nhưng vẫn có thể schedule khi capacity node không đủ cân bằng. Đây là policy minh họa: cluster hai zone không thể dùng nguyên minDomains: 3.
3. Hiểu đúng maxSkew và global minimum
Với hard constraint, scheduler so số Pod matching trong domain mục tiêu với global minimum của các domain đủ điều kiện. Nếu ba zone có số Pod 2, 2 và 1, global minimum là 1; đặt Pod tiếp vào một zone đang có 2 làm skew thành 2 và vi phạm maxSkew: 1, còn đặt vào zone có 1 tạo 2, 2, 2.
minDomains khai báo số domain tối thiểu kỳ vọng và chỉ dùng cùng DoNotSchedule. Khi số eligible domain thấp hơn giá trị này, global minimum được coi là 0, khiến scheduler không âm thầm xem một cluster thiếu zone là “đã cân bằng”. Nó có thể để Pod Pending—đúng nếu availability quan trọng hơn việc chạy bằng mọi giá.
4. DoNotSchedule hay ScheduleAnyway?
| Chế độ | Hành vi | Khi phù hợp |
|---|---|---|
DoNotSchedule | Lọc node làm constraint bị vi phạm | Không chấp nhận co cụm vào ít fault domain |
ScheduleAnyway | Vẫn schedule, ưu tiên node giảm skew | Capacity/availability của workload quan trọng hơn cân bằng tuyệt đối |
Hard constraint có thể biến thiếu capacity thành Pending Pod và làm rollout đứng. Soft constraint có thể làm toàn bộ replica chạy nhưng cùng zone khi cluster mất zone. Chọn theo failure policy và SLO, không theo việc manifest nào “xanh” nhanh hơn. Zone thường hard, node có thể soft là điểm khởi đầu hợp lý cho nhiều stateless service, nhưng không phải quy luật phổ quát.
5. Selector phải đếm đúng chính workload
labelSelector xác định Pod nào được đếm. Nếu selector quá rộng, workload khác làm sai skew; nếu quá hẹp hoặc không khớp label của chính Pod mới, Pod đó trở thành “ghost pod”: được đặt nhưng không làm thay đổi phép đếm như mong đợi, nên nhiều Pod có thể tiếp tục dồn vào một domain.
Kiểm tra selector của Deployment, label trong Pod template và constraint cùng một contract. Scheduler chỉ đếm matching Pod trong cùng namespace với Pod đang schedule.
6. matchLabelKeys và rolling update
matchLabelKeys lấy giá trị label từ Pod mới và ghép vào selector. Dùng pod-template-hash giúp tính skew riêng cho revision của Deployment, tránh revision cũ và mới can thiệp phép đếm theo cách khó đoán trong rollout.
Đây là lựa chọn semantics: nếu yêu cầu toàn bộ old+new Pod luôn cân bằng chung, selector/revision strategy có thể khác. Test maxSurge, maxUnavailable và rollback. Không dùng cùng một key đồng thời trong labelSelector và matchLabelKeys; xác minh support trên phiên bản cluster.
7. Eligible domain phụ thuộc node policy
Scheduler không đếm mọi zone tồn tại trên sơ đồ cloud. Eligible domain đến từ node có topology label và phù hợp với node affinity/taint policy của Pod. Field nodeAffinityPolicy và nodeTaintsPolicy cho biết các ràng buộc đó được honor hay ignore khi tính tập domain.
- Node thiếu
topologyKeybị bỏ qua và Pod không được schedule lên đó. - Label typo có thể làm cả node pool “biến mất” khỏi constraint.
- Taint không có toleration có thể làm một zone không còn eligible.
- Node affinity khóa workload vào một instance type chỉ có ở một zone.
Audit label, taint, affinity và capacity cùng nhau. Một constraint đúng cú pháp vẫn có thể bất khả thi.
8. Nhiều constraint được kết hợp bằng AND
Khi có zone constraint và hostname constraint, node ứng viên phải thỏa cả hai. Điều này mạnh hơn một danh sách preference độc lập và có thể tạo xung đột. Ví dụ zone cần nhận thêm Pod nhưng mọi node trong zone đó đều vi phạm hard hostname skew hoặc thiếu resource request.
Dùng scheduler event để tìm constraint nào chặn placement. Không sửa bằng cách tăng maxSkew ngay lập tức; kiểm tra resource, volume topology, taint, affinity, quota và image architecture trước.
9. Autoscaler không nhìn thấy domain đã scale về zero theo mọi cấu hình
Scheduler xác định topology domain từ node hiện hữu. Nếu node group của một zone đã scale về zero, domain có thể không xuất hiện để tính spread; autoscaler phải hiểu topology và node group tiềm năng mới có thể mở capacity đúng chỗ.
Test từ trạng thái zero, không chỉ scale-down từ cluster đang đủ node. Đảm bảo node template của autoscaler có label/taint tương ứng, resource request đúng và storage class có volume topology khả dụng trong zone mới.
10. Scheduler không tự tái cân bằng Pod đang chạy
Constraint được xét khi schedule Pod mới. Nếu scale-down hoặc node recovery làm phân bố lệch, scheduler không tự di chuyển Pod đang chạy chỉ để đẹp lại. Một rollout, reschedule tự nhiên hoặc Descheduler có policy phù hợp mới có thể tái cân bằng; mọi eviction phải tôn trọng disruption budget và capacity.
Đừng chạy Descheduler chỉ vì histogram không đều. Mỗi eviction làm mất cache, tạo load startup và có thể tác động SLO. Đặt ngưỡng, maintenance window và rate limit.
11. Topology spread không thay thế PDB và anti-affinity
- Topology spread điều khiển phân bố lúc placement.
- PodDisruptionBudget giới hạn voluntary disruption đồng thời, không ngăn node/zone hỏng.
- Pod anti-affinity đẩy Pod khỏi nhau theo quan hệ label, có thể rất strict và tốn scheduler hơn.
- Resource requests bảo đảm scheduler nhìn thấy nhu cầu capacity.
- Storage topology quyết định Pod stateful có thực sự chạy được ở zone khác.
High availability cần cả health probe, nhiều endpoint, load balancer, database replication và dependency đa zone. Rải frontend không giúp nếu mọi replica dùng một database single-zone.
12. Quan sát và rollout
- Inventory node label theo zone/node pool và phát hiện label thiếu.
- Chạy soft constraint trong staging, xem placement theo revision.
- Thử scale replica, rolling update và node drain.
- Mô phỏng mất zone/capacity và node group từ zero.
- Canary hard constraint, alert Pending Pod và rollout stalled.
- Đo replica theo domain, ready endpoint theo domain và traffic/error theo zone.
kubectl get nodes -L topology.kubernetes.io/zone,kubernetes.io/hostname
kubectl get pods -n payments -l app=payments-api \
-o wide
kubectl describe pod <pending-pod>
Checklist production
- Topology label phản ánh fault domain thật và có trên mọi node cần dùng.
- Pod template tự khớp
labelSelectorcủa constraint. maxSkewvàminDomainskhớp số zone/capacity thực.- Hard/soft constraint được chọn theo SLO và failure policy.
- Rolling update semantics của
matchLabelKeysđã được test. - Node affinity, taint/toleration, volume topology và resource request tương thích.
- Autoscaler đã test khi một domain scale về zero.
- PDB và rollout settings không tạo deadlock với hard spread.
- Dashboard/alert phát hiện imbalance, Pending Pod và thiếu ready endpoint theo zone.
- Đã diễn tập drain node và mất zone, không chỉ đọc placement tĩnh.
Kết luận
Topology Spread Constraints biến ý định “đừng đặt hết trứng vào một giỏ” thành policy scheduler có thể kiểm tra. Nhưng YAML chỉ đúng khi selector đếm đúng, node label phản ánh hạ tầng, cluster có capacity và đội vận hành chấp nhận rõ lúc nào Pod nên Pending. Kết hợp spread theo zone/node với PDB, autoscaling, storage topology và diễn tập failure mới tạo high availability có thể chứng minh.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.