Kenapa Penempatan Itu Penting
Penjadwal berusaha menyebarkan pod, tetapi tanpa aturan eksplisit ia bisa saja menempatkan ketiga replika Anda di node yang sama. Satu node mati, layanan Anda mati — padahal Anda mengira sudah punya tiga replika. Ini penemuan yang menyakitkan saat insiden pertama.
Topology Spread Constraints
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: { app: layanan-pesanan }
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway # lebih longgar
labelSelector:
matchLabels: { app: layanan-pesanan }
| Field | Arti |
|---|---|
maxSkew | Selisih maksimum jumlah pod antar domain topologi |
topologyKey | Label node yang menjadi pembeda domain (zona, hostname, rak) |
DoNotSchedule | Aturan keras — pod tetap Pending bila tak terpenuhi |
ScheduleAnyway | Aturan lunak — diusahakan, tapi tetap dijadwalkan |
Gunakan DoNotSchedule untuk penyebaran antar zona (ini yang benar-benar melindungi dari kegagalan besar) dan ScheduleAnyway untuk penyebaran antar node. Dua aturan keras sekaligus sering membuat pod tidak bisa dijadwalkan sama sekali di kluster kecil.
Node Affinity
affinity:
nodeAffinity:
# Syarat KERAS
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values: [m6i.xlarge, m6i.2xlarge]
# PREFERENSI lunak
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: [ap-southeast-1a]
Nama panjang itu bermakna: aturan hanya diperiksa saat penjadwalan. Bila label node berubah setelah pod berjalan, pod tidak akan dipindahkan. Kubernetes tidak melakukan penjadwalan ulang secara otomatis untuk pod yang sudah berjalan.
Pod Anti-Affinity
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels: { app: layanan-pesanan }
Artinya: "usahakan jangan menempatkan pod ini di node yang sudah punya pod dengan label yang sama". Untuk penyebaran sederhana, topology spread constraints lebih dianjurkan karena lebih murah secara komputasi — anti-affinity harus membandingkan setiap pod dengan setiap node dan menjadi lambat di kluster besar.
Taint dan Toleration
Affinity adalah pod yang memilih node. Taint adalah kebalikannya: node yang menolak pod.
# Menandai node khusus GPU
kubectl taint nodes node-gpu-1 khusus=gpu:NoSchedule
# Hanya pod dengan toleration ini yang boleh masuk
tolerations:
- key: khusus
operator: Equal
value: gpu
effect: NoSchedule
| Effect | Perilaku |
|---|---|
NoSchedule | Pod baru tanpa toleration ditolak |
PreferNoSchedule | Diusahakan dihindari, tapi tidak dipaksakan |
NoExecute | Pod yang sudah berjalan pun diusir |
Taint saja hanya mencegah pod lain masuk — ia tidak menarik pod GPU Anda ke sana. Untuk node khusus, gunakan keduanya: taint agar beban kerja umum menjauh, dan node affinity agar beban kerja GPU memang mengarah ke sana.
PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: pdb-pesanan
spec:
minAvailable: 2 # atau maxUnavailable: 1
selector:
matchLabels: { app: layanan-pesanan }
Saat memutakhirkan node, kubectl drain mengusir semua pod dari node tersebut. Bila ketiga replika Anda kebetulan berada di node itu, semuanya diusir sekaligus dan layanan mati sampai pod terjadwal ulang di tempat lain. PDB memberi tahu Kubernetes berapa banyak yang wajib tetap hidup, sehingga proses drain menunggu alih-alih mengusir semuanya.
PDB melindungi dari drain, penskalaan node, dan operasi pemeliharaan lainnya. Ia tidak melindungi dari node yang mati mendadak, kehabisan memori, atau kegagalan perangkat keras. Untuk itu, andalkan jumlah replika yang memadai dan penyebaran antar zona.
Perhatikan juga: PDB dengan minAvailable sama dengan jumlah replika akan memblokir drain selamanya — pastikan selalu ada ruang gerak.
kubectl get pods -o wide --sort-by=.spec.nodeName kubectl get pdb -A kubectl describe node node-1 | grep -A5 Taints kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data kubectl uncordon node-1