Veröffentlicht am

Kubernetes Taints und Tolerations: Workloads verteilen

Teilen:
Authors

TL;DR

  • Taints stoessen Pods von Nodes ab, Tolerations erlauben einzelnen Pods trotzdem dort zu laufen -- zusammen steuern sie, welche Workloads wo landen.
  • Drei Effekte: NoSchedule (neue Pods blockieren), PreferNoSchedule (weiche Praeferenz), NoExecute (laufende Pods evakuieren).
  • Taints allein reichen nicht -- kombiniert sie immer mit Node Affinity oder Node Selectors, um Pods aktiv auf die richtigen Nodes zu ziehen.
  • Typische Use Cases: GPU-Isolation, Produktions-Trennung, Wartungsfenster, lizenzpflichtige Software auf dedizierten Nodes.
  • Startet mit einem Pilot-Node in der Staging-Umgebung, bevor ihr Taints in Produktion ausrollt.

Was Taints und Tolerations tun

Der Kubernetes Scheduler entscheidet, auf welchem Node ein Pod laeuft. Standardmaessig verteilt er Pods gleichmaessig ueber alle verfuegbaren Nodes. Das ist fuer allgemeine Workloads in Ordnung, aber es gibt Faelle, in denen man Kontrolle braucht.

Taints sind Markierungen auf Nodes, die sagen: "Hier darf nicht jeder hin." Tolerations sind Gegenmarkierungen auf Pods, die sagen: "Ich darf trotzdem."

Das ist ein Push-Mechanismus. Der Node wehrt sich aktiv gegen Pods, die nicht passen. Im Gegensatz dazu sind Node Selectors und Node Affinity Pull-Mechanismen -- sie ziehen Pods zu bestimmten Nodes hin.

In der Praxis braucht man beides zusammen. Ein Taint allein verhindert nur, dass falsche Pods landen. Aber er garantiert nicht, dass die richtigen Pods auch wirklich dort landen. Dafuer braucht man Node Affinity.

Die drei Effekte im Detail

EffektNeue PodsLaufende PodsTypischer Einsatz
NoScheduleBlockiert (ohne Toleration)BleibenGPU-Nodes, dedizierte Workloads
PreferNoScheduleVermieden, aber moeglichBleibenWeiche Praeferenzen
NoExecuteBlockiertWerden evakuiertWartung, Node-Drain

NoSchedule ist der Standardfall fuer die meisten Szenarien. NoExecute ist aggressiver -- es entfernt auch bereits laufende Pods. Das braucht man fuer Wartungsfenster oder wenn ein Node ein Problem hat.

PreferNoSchedule nutzt man selten. Es ist eine weiche Empfehlung an den Scheduler, die er ignoriert, wenn kein anderer Node verfuegbar ist.

Praxis-Beispiel: GPU-Nodes isolieren

GPU-Nodes sind teuer. Eine NVIDIA A100 kostet in der Cloud 2-3 EUR pro Stunde. Wenn ein allgemeiner Web-Pod dort landet und die Ressourcen blockiert, verbrennt man Geld. Taints verhindern das.

Schritt 1: Node tainten

# GPU-Node reservieren
kubectl taint nodes gpu-node-01 nvidia.com/gpu=present:NoSchedule

# Pruefen, ob der Taint gesetzt ist
kubectl describe node gpu-node-01 | grep -A 5 Taints

Schritt 2: GPU-Workload mit Toleration deployen

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ml-inference
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ml-inference
  template:
    metadata:
      labels:
        app: ml-inference
    spec:
      containers:
      - name: model-server
        image: registry.example.com/ml-model:v2.1
        resources:
          limits:
            nvidia.com/gpu: 1
          requests:
            cpu: "2"
            memory: "8Gi"
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Equal"
        value: "present"
        effect: "NoSchedule"
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-type
                operator: In
                values:
                - gpu

Beachtet die Kombination: Die Toleration erlaubt dem Pod, auf dem getainteten Node zu laufen. Die Node Affinity stellt sicher, dass er auch wirklich dort landet und nicht auf einem zufaelligen anderen Node.

Schritt 3: Verifizieren

# Pod-Platzierung pruefen
kubectl get pods -l app=ml-inference -o wide

# Auslastung des GPU-Nodes pruefen
kubectl top node gpu-node-01

Praxis-Beispiel: Produktions-Isolation

Entwicklungs- und Produktions-Workloads auf dem gleichen Cluster, aber auf getrennten Nodes:

# Produktions-Nodes tainten
kubectl taint nodes prod-node-01 env=production:NoExecute
kubectl taint nodes prod-node-02 env=production:NoExecute

# Labels setzen (fuer Node Affinity)
kubectl label nodes prod-node-01 environment=production
kubectl label nodes prod-node-02 environment=production

Das Deployment fuer einen Produktions-Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-gateway
  template:
    metadata:
      labels:
        app: api-gateway
    spec:
      containers:
      - name: api-gateway
        image: registry.example.com/api-gateway:v4.2
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
      tolerations:
      - key: "env"
        operator: "Equal"
        value: "production"
        effect: "NoExecute"
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: environment
                operator: In
                values:
                - production

NoExecute sorgt dafuer, dass ein Dev-Pod, der versehentlich auf einem Prod-Node landet, sofort entfernt wird. Nicht nur blockiert, sondern aktiv evakuiert.

Praxis-Beispiel: Wartungsfenster

Nodes muessen regelmaessig gepatcht werden. Mit Taints kann man das kontrolliert machen:

# Node fuer neue Pods sperren
kubectl taint nodes worker-03 maintenance=scheduled:NoSchedule

# Laufende Pods sauber evakuieren (nutzt intern NoExecute)
kubectl drain worker-03 --ignore-daemonsets --delete-emptydir-data

# Nach der Wartung: Taint entfernen
kubectl taint nodes worker-03 maintenance=scheduled:NoSchedule-

# Node wieder freigeben
kubectl uncordon worker-03

kubectl drain setzt automatisch einen NoExecute Taint und respektiert dabei PodDisruptionBudgets. Es wartet, bis Pods sauber heruntergefahren sind, bevor es den Node leert.

Taint-Nomenklatur: Konventionen festlegen

Ohne klare Namenskonventionen wird es schnell unuebersichtlich. Hier ein bewaehertes Schema:

Taint KeyValueEffektZweck
node-type/gpunvidia-a100NoScheduleGPU-Isolation
node-type/high-memorytrueNoScheduleMemory-intensive Workloads
envproductionNoExecuteProd-Trennung
envstagingNoScheduleStaging-Trennung
maintenancescheduledNoScheduleGeplante Wartung
compliance/data-classsensitiveNoExecuteDSGVO-relevante Daten

Das Format domain/purpose=value:effect ist etabliert und skaliert gut. Dokumentiert eure Konventionen und prueft sie regelmaessig.

Haeufige Fehler und wie man sie vermeidet

Fehler 1: Taint ohne Node Affinity

Ein Taint allein verhindert nur, dass falsche Pods kommen. Er zieht die richtigen Pods nicht an. Ohne Node Affinity kann der gewuenschte Pod auf einem beliebigen anderen Node landen.

Fehler 2: NoExecute ohne PodDisruptionBudget

Wenn man NoExecute setzt und kein PodDisruptionBudget existiert, werden alle Pods gleichzeitig evakuiert. Das kann zu Downtime fuehren.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-gateway-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api-gateway

Fehler 3: Zu viele verschiedene Taints

Jeder neue Taint erhoet die Komplexitaet exponentiell. Pods brauchen Tolerations fuer jeden Taint auf einem Node. Haltet die Anzahl so gering wie moeglich -- typischerweise reichen 2-3 verschiedene Taint-Keys pro Cluster.

Fehler 4: System-Pods vergessen

DaemonSets wie kube-proxy, calico-node oder Monitoring-Agents muessen auf allen Nodes laufen -- auch auf getainteten. Prueft, dass eure DaemonSets die richtigen Tolerations haben:

tolerations:
- operator: "Exists"

Diese "wildcard" Toleration erlaubt dem Pod, auf jedem Node zu laufen, egal welcher Taint gesetzt ist.

Taints per Terraform automatisieren

In Infrastructure-as-Code Umgebungen sollten Taints nicht manuell gesetzt werden. Hier ein Terraform-Beispiel fuer einen AKS Node Pool:

resource "azurerm_kubernetes_cluster_node_pool" "gpu" {
  name                  = "gpupool"
  kubernetes_cluster_id = azurerm_kubernetes_cluster.main.id
  vm_size               = "Standard_NC6s_v3"
  node_count            = 2

  node_labels = {
    "node-type" = "gpu"
  }

  node_taints = [
    "nvidia.com/gpu=present:NoSchedule"
  ]
}

resource "azurerm_kubernetes_cluster_node_pool" "prod" {
  name                  = "prodpool"
  kubernetes_cluster_id = azurerm_kubernetes_cluster.main.id
  vm_size               = "Standard_D4s_v5"
  node_count            = 3
  min_count             = 3
  max_count             = 10
  auto_scaling_enabled  = true

  node_labels = {
    "environment" = "production"
  }

  node_taints = [
    "env=production:NoExecute"
  ]
}

Damit sind Taints deklarativ, versioniert und reproduzierbar. Kein manuelles kubectl taint mehr in der Produktion. Wer seine Cluster-Infrastruktur per Code verwaltet, findet weitere Patterns unter Kubernetes Cluster Autoscaler.

Monitoring: Sind die Pods da, wo sie sein sollen?

Nach dem Rollout von Taints muss man pruefen, ob die Workload-Verteilung stimmt. Ein paar nuetzliche Befehle:

# Alle Taints im Cluster auflisten
kubectl get nodes -o custom-columns=\
  NAME:.metadata.name,\
  TAINTS:.spec.taints

# Pods auf einem bestimmten Node anzeigen
kubectl get pods --all-namespaces --field-selector spec.nodeName=gpu-node-01

# Pending Pods finden (koennen auf Taint-Probleme hinweisen)
kubectl get pods --all-namespaces --field-selector status.phase=Pending

Pending Pods sind das erste Warnsignal. Wenn ein Pod nicht schedulbar ist, liegt es oft an fehlenden Tolerations. kubectl describe pod <name> zeigt die Scheduling-Events und den Grund.

Fuer langfristiges Monitoring empfiehlt sich ein Dashboard, das die Node-Auslastung pro Taint-Gruppe visualisiert. Details dazu unter Kubernetes Observability Stack.

Zusammenspiel mit anderen Scheduling-Mechanismen

Taints und Tolerations sind nur ein Werkzeug im Scheduling-Baukasten. Hier ein Ueberblick:

MechanismusRichtungSteuertTypischer Einsatz
Taints/TolerationsPush (Node wehrt ab)Welche Pods duerfen nichtGPU-Isolation, Wartung
Node SelectorPull (Pod waehlt)Welcher Node genauEinfache Zuweisung
Node AffinityPull (Pod waehlt)Welche Node-GruppeFlexible Regeln
Pod AffinityPull (Pod zu Pod)Welche Pods zusammenCo-Location
Pod Anti-AffinityPush (Pod von Pod)Welche Pods getrenntHigh Availability
Topology SpreadVerteilungGleichmaessig ueber ZonenZone-Awareness

In der Praxis kombiniert man fast immer Taints mit Node Affinity. Fuer Multi-Tenancy-Szenarien kommt zusaetzlich die Namespace-Isolation dazu -- mehr dazu unter Kubernetes Multi-Tenancy.

Weiter gedacht

Taints und Tolerations sind ein fundamentaler Baustein fuer jeden Cluster, der ueber ein einfaches Dev-Setup hinausgeht. Sie bringen Ordnung in die Workload-Verteilung und sind die Basis fuer Kostenoptimierung, Sicherheitsisolation und stabile Wartungsprozesse.

Wer sich tiefer in das Thema Cluster-Hardening einarbeiten will, findet unter Kubernetes Network Policies und Kubernetes RBAC die naechsten logischen Schritte.

Braucht ihr Hilfe bei der Scheduling-Strategie fuer euren Cluster? Schreibt uns unter /kontakt -- wir schauen uns eure Workloads an und helfen bei der Planung.

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