- Authors

- Name
- Phillip Pham
- @ddppham
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
| Effekt | Neue Pods | Laufende Pods | Typischer Einsatz |
|---|---|---|---|
NoSchedule | Blockiert (ohne Toleration) | Bleiben | GPU-Nodes, dedizierte Workloads |
PreferNoSchedule | Vermieden, aber moeglich | Bleiben | Weiche Praeferenzen |
NoExecute | Blockiert | Werden evakuiert | Wartung, 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 Key | Value | Effekt | Zweck |
|---|---|---|---|
node-type/gpu | nvidia-a100 | NoSchedule | GPU-Isolation |
node-type/high-memory | true | NoSchedule | Memory-intensive Workloads |
env | production | NoExecute | Prod-Trennung |
env | staging | NoSchedule | Staging-Trennung |
maintenance | scheduled | NoSchedule | Geplante Wartung |
compliance/data-class | sensitive | NoExecute | DSGVO-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:
| Mechanismus | Richtung | Steuert | Typischer Einsatz |
|---|---|---|---|
| Taints/Tolerations | Push (Node wehrt ab) | Welche Pods duerfen nicht | GPU-Isolation, Wartung |
| Node Selector | Pull (Pod waehlt) | Welcher Node genau | Einfache Zuweisung |
| Node Affinity | Pull (Pod waehlt) | Welche Node-Gruppe | Flexible Regeln |
| Pod Affinity | Pull (Pod zu Pod) | Welche Pods zusammen | Co-Location |
| Pod Anti-Affinity | Push (Pod von Pod) | Welche Pods getrennt | High Availability |
| Topology Spread | Verteilung | Gleichmaessig ueber Zonen | Zone-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
Kubernetes Scheduling: Affinity, Taints und Tolerations
Kubernetes Scheduling mit Node Affinity, Taints und Tolerations steuern. Praktische Beispiele für GPU-Nodes, Zone-Spreading und Co-Location.
Taints und Tolerations erklärt: NoSchedule und NoExecute
Taints und Tolerations in Kubernetes verstehen: NoSchedule, PreferNoSchedule und NoExecute mit Praxis-Beispielen für GPU- und Infra-Node-Isolation.
CKA Scheduling: Node Affinity, Taints und Resources
Kubernetes Scheduling für die CKA-Prüfung meistern: Node Affinity, Taints, Tolerations, Resource Requests und Limits mit praktischen YAML-Beispielen.
Jobs und CronJobs: Batch-Workloads auf Kubernetes
Kubernetes Jobs und CronJobs für Batch-Verarbeitung einrichten: Job-Typen, Cron-Scheduling, Fehlerbehandlung und TTL-Cleanup mit praxisnahen YAML-Beispielen.
Kubernetes Datensouveränität: Node Affinity für DSGVO
Datensouveränität auf Kubernetes sicherstellen mit Node Affinity, Kyverno-Policies und EU-Cloud-Regionen für DSGVO-konforme Datenverarbeitung.