Plánování: taints, tolerations, affinity

Plánování: taints, tolerations, affinity

Scheduler standardně umístí pod tam, kde se vejde. Tři mechanismy to umí změnit a fungují proti sobě.

graph LR N[Uzel] -->|taint: odpuzuje| P1[Pod bez tolerance – odmítnut] N -->|taint tolerován| P2[Pod s tolerancí – povolen] P3[Pod] -->|nodeSelector / affinity: přitahuje| N
MechanismusSedí naŘíká
Taintuzlu„drž pody dál, pokud tohle netolerují“
Tolerationpodu„tenhle taint mi nevadí“
nodeSelector / node affinitypodu„chci běžet na uzlu tohohle typu“

Ta dvojice je záměrně nesymetrická. Tolerance povoluje, ale nepřitahuje: pod, který taint toleruje, může stejně přistát kdekoli jinde. Chcete-li ho na těch uzlech, potřebujete k tomu ještě affinity.

Taints

kubectl taint node worker03 disk=ssd:NoSchedule       # přidat
kubectl taint node worker03 disk=ssd:NoSchedule-      # odebrat (pomlčka na konci)
kubectl describe node control01 | grep -i taint

Tři efekty:

EfektVýznam
NoScheduležádný nový pod bez tolerance; běžící zůstávají
PreferNoSchedulescheduler se uzlu vyhýbá, pokud může – měkká nápověda
NoExecutetotéž co výše, a navíc vystěhuje běžící pody bez tolerance

S taintem jste se už potkali: kubectl cordon přidá node.kubernetes.io/unschedulable:NoSchedule a uzly control plane nesou node-role.kubernetes.io/control-plane:NoSchedule. Tainty nejsou exotika – je to mašinerie pod příkazy, které používáte celou dobu.

Tolerations

tolerations:
  - key: disk
    operator: Equal
    value: ssd
    effect: NoSchedule

operator: Exists bez hodnoty toleruje jakoukoli hodnotu daného klíče. Vynechte i klíč a pod toleruje všechno – takhle běží některé monitorovací DaemonSety na každém uzlu bez ohledu na cokoli.

U NoExecute můžete přidat tolerationSeconds ve smyslu „vystěhuj mě, ale dej mi pět minut“.

nodeSelector

Jednoduchá varianta: přesná shoda labelů, všech.

kubectl label node worker01 disktype=ssd
spec:
  nodeSelector:
    disktype: ssd

Node affinity

Bohatší varianta – operátory (In, NotIn, Exists, Gt, Lt) a hlavně měkká varianta.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:     # tvrdé: musí
      nodeSelectorTerms:
        - matchExpressions:
            - key: disktype
              operator: In
              values: ["ssd", "nvme"]
    preferredDuringSchedulingIgnoredDuringExecution:    # měkké: zkus
      - weight: 100
        preference:
          matchExpressions:
            - key: topology.kubernetes.io/zone
              operator: In
              values: ["zone-a"]

Ty dlouhé názvy čtěte doslova:

  • requiredDuringScheduling – když nic neodpovídá, pod zůstane Pending.
  • preferredDuringScheduling – scheduler uzly boduje a shody preferuje, ale pod umístí tak jako tak.
  • IgnoredDuringExecution – jakmile pod běží, změna labelů uzlu s ním nepohne. Běžící pod vystěhuje jen taint s NoExecute.

Pod affinity a anti-affinity

Umístění vůči jiným podům, ne vůči uzlům. Běžnější je anti-affinity: drž moje repliky mimo stejný uzel.

affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels: { app: web }
          topologyKey: kubernetes.io/hostname

topologyKey definuje, co znamená „stejné místo“ – stejný uzel, stejná zóna, stejný rack.

Tip

Pro prostý případ „rozprostři repliky rovnoměrně“ použijte raději topologySpreadConstraints – je jednodušší a skutečně vyvažuje, kdežto anti-affinity jen odpuzuje.

Co si vybrat?

CílPoužijte
Vyhradit uzly pro určitou zátěžtaint na uzly + tolerance + affinity na podu
Nepustit tam nikoho dalšíhojen taint
Poslat pod na konkrétní hardwarenodeSelector nebo node affinity
Odstavit uzel na údržbukubectl drain (pod kapotou taint)
Rozprostřít repliky po uzlechtopologySpreadConstraints

Reference