Veröffentlicht am

Kubernetes Scheduling: Affinity, Taints und Tolerations

Teilen:
Authors

TL;DR

Kubernetes bietet mehrere Mechanismen für gezieltes Pod-Scheduling: nodeSelector für einfache Label-basierte Platzierung, Node Affinity für flexible Regeln mit Required/Preferred-Gewichtung, Pod Affinity/AntiAffinity für Co-Location und Spreading, sowie Taints und Tolerations zum Ausschließen von Workloads auf bestimmten Nodes. Kombiniert ermöglichen sie präzise Kontrolle über die Pod-Verteilung im Cluster.

Scheduling-Grundlagen: nodeSelector

Der einfachste Weg, Pods auf bestimmte Nodes zu lenken, ist nodeSelector. Zuerst labeln Sie Ihren Node, dann referenzieren Sie das Label im Pod-Spec.

# Node mit Label versehen
kubectl label nodes worker-gpu-01 gpu=nvidia-a100

# Labels prüfen
kubectl get nodes --show-labels | grep gpu
apiVersion: v1
kind: Pod
metadata:
  name: ml-training
spec:
  nodeSelector:
    gpu: nvidia-a100
  containers:
    - name: training
      image: pytorch/pytorch:2.1.0
      resources:
        limits:
          nvidia.com/gpu: 1

nodeSelector ist simpel, aber unflexibel. Es gibt nur Hard-Requirements - der Pod bleibt Pending, wenn kein passender Node existiert.


Node Affinity: Flexible Platzierungsregeln

Node Affinity erweitert nodeSelector um zwei Modi:

TypVerhaltenEinsatz
requiredDuringSchedulingIgnoredDuringExecutionHard-Constraint, Pod wird nur auf passende Nodes gescheduletGPU-Workloads, Compliance
preferredDuringSchedulingIgnoredDuringExecutionSoft-Constraint mit Gewichtung, Scheduler bevorzugt passende NodesPerformance-Optimierung

Required: GPU-Workloads auf dedizierte Nodes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: inference
  template:
    metadata:
      labels:
        app: inference
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: node-type
                    operator: In
                    values:
                      - gpu
                  - key: gpu-model
                    operator: In
                    values:
                      - a100
                      - h100
      containers:
        - name: inference
          image: my-registry/inference:v2
          resources:
            limits:
              nvidia.com/gpu: 1

Mehrere matchExpressions innerhalb eines nodeSelectorTerms werden mit AND verknüpft. Mehrere nodeSelectorTerms untereinander mit OR.

Preferred: Bevorzugt in bestimmter Zone

spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 80
          preference:
            matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                  - eu-central-1a
        - weight: 20
          preference:
            matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                  - eu-central-1b

Der weight-Wert (1-100) beeinflusst die Scheduler-Bewertung. Höhere Gewichtung bedeutet stärkere Präferenz, aber keine Garantie.


Pod Affinity und AntiAffinity

Während Node Affinity Pods relativ zu Nodes platziert, steuert Pod Affinity die Platzierung relativ zu anderen Pods.

Co-Location: Cache neben der Anwendung

Redis-Cache soll auf demselben Node wie die Anwendung laufen, um Netzwerk-Latenz zu minimieren:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  replicas: 3
  selector:
    matchLabels:
      app: redis-cache
  template:
    metadata:
      labels:
        app: redis-cache
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - web-frontend
              topologyKey: kubernetes.io/hostname
      containers:
        - name: redis
          image: redis:7-alpine
          resources:
            requests:
              cpu: 100m
              memory: 128Mi

Der topologyKey bestimmt die Granularität: kubernetes.io/hostname bedeutet gleicher Node, topology.kubernetes.io/zone bedeutet gleiche Availability Zone.

AntiAffinity: Pods über Zonen verteilen

Für Hochverfügbarkeit sollen Replicas auf verschiedenen Nodes in verschiedenen Zonen laufen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-frontend
  template:
    metadata:
      labels:
        app: web-frontend
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - web-frontend
                topologyKey: topology.kubernetes.io/zone
      containers:
        - name: web
          image: nginx:1.27

Tipp: Verwenden Sie preferred statt required für AntiAffinity bei Zone-Spreading. Bei required bleibt der Pod Pending, wenn alle Zonen bereits einen Pod haben.


Taints und Tolerations

Taints funktionieren umgekehrt zu Affinity: Statt Pods anzuziehen, stoßen Nodes Pods ab. Nur Pods mit passender Toleration werden auf einen Tainted Node geschedulet.

Taint-Effekte

EffektVerhalten
NoScheduleNeue Pods ohne Toleration werden nicht geschedulet
PreferNoScheduleScheduler vermeidet den Node, nutzt ihn aber als Fallback
NoExecuteBestehende Pods ohne Toleration werden evicted

Praxis: GPU-Nodes exklusiv für ML-Workloads

# Taint setzen - nur ML-Pods dürfen auf GPU-Nodes
kubectl taint nodes worker-gpu-01 workload=gpu:NoSchedule
kubectl taint nodes worker-gpu-02 workload=gpu:NoSchedule

# Taint entfernen (mit Minus am Ende)
kubectl taint nodes worker-gpu-01 workload=gpu:NoSchedule-

Die passende Toleration im Pod-Spec:

apiVersion: v1
kind: Pod
metadata:
  name: ml-job
spec:
  tolerations:
    - key: workload
      operator: Equal
      value: gpu
      effect: NoSchedule
  nodeSelector:
    gpu: nvidia-a100
  containers:
    - name: training
      image: pytorch/pytorch:2.1.0

Wichtig: Eine Toleration allein platziert den Pod nicht auf dem Tainted Node - sie erlaubt es nur. Kombinieren Sie Tolerations mit Node Affinity oder nodeSelector für gezielte Platzierung.

Kombiniertes Beispiel: Dedizierte Monitoring-Nodes

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      tolerations:
        - operator: Exists
      containers:
        - name: exporter
          image: prom/node-exporter:v1.8.0

operator: Exists ohne key toleriert alle Taints. Das ist typisch für DaemonSets wie Node-Exporter, die auf jedem Node laufen müssen.


Debugging: Scheduling-Probleme lösen

Wenn ein Pod im Status Pending bleibt:

# Events prüfen - zeigt Scheduling-Fehler
kubectl describe pod <pod-name> | grep -A 5 Events

# Typische Meldungen:
# "0/5 nodes are available: 2 node(s) had taint {workload: gpu}"
# "0/5 nodes are available: 5 node(s) didn't match Pod's node affinity"

# Node-Taints anzeigen
kubectl get nodes -o custom-columns=\
NAME:.metadata.name,TAINTS:.spec.taints

# Verfügbare Labels prüfen
kubectl get nodes --show-labels

FAQ

Was ist der Unterschied zwischen nodeSelector und Node Affinity?

nodeSelector unterstützt nur exakte Label-Matches. Node Affinity bietet Operatoren wie In, NotIn, Exists, Gt, Lt und unterscheidet zwischen Hard- und Soft-Constraints.

Kann ein Pod mehrere Tolerations haben?

Ja, ein Pod kann beliebig viele Tolerations definieren. Jede Toleration wird unabhängig gegen die Taints eines Nodes geprüft. Der Pod wird nur geschedulet, wenn alle Taints des Nodes toleriert werden.

Wann sollte ich PreferNoSchedule statt NoSchedule verwenden?

PreferNoSchedule eignet sich, wenn der Node als Fallback dienen soll - etwa für Burst-Workloads wenn dedizierte Nodes ausgelastet sind. NoSchedule für strikte Isolation wie GPU- oder Compliance-Nodes.

Wie verteile ich Pods gleichmäßig über Availability Zones?

Nutzen Sie podAntiAffinity mit topologyKey: topology.kubernetes.io/zone als Preferred-Regel. Ab Kubernetes 1.19 bieten Topology Spread Constraints noch feinere Kontrolle mit maxSkew.

Kubernetes ohne DevOps-Overhead?

Wir betreiben Ihre Kubernetes-Cluster 24/7 – Sie fokussieren auf Ihr Kerngeschäft. Ab 4.000€/Monat, DSGVO-konform, mit deutschem Support.

24/7 SupportDeutsche RechenzentrenDSGVO-konform

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen