101 Series: Kubernetes Affinity und Anti Affinity
Warum werden Affinity und Anti Affinity benötigt?
Innerhalb eines Kubernetes Clusters laufen typischerweise viele verschiedene Anwendungen parallel. Dabei ist es oft entscheidend, wie und wo diese Anwendungen im Cluster platziert werden. Wie ihr vermutlich alle wisst, übernimmt das platzieren der Pods der Kube Scheduler. Bereits ohne weitere Konfiguration sind viele Anwendungsfälle abgedeckt. Aber was ist, wenn ihr bestimmte Anforderungen habt?
Beispiele hierfür sind:
Eine Anwendung, die spezielle Hardware wie GPUs benötigt, sollte auf Nodes laufen, die diese Hardware bereitstellen. Schlagwort: Machine Learning
Zwei Pods, die intensiv miteinander kommunizieren, sollten auf derselben Node laufen, um die Netzwerk-Latenz zu minimieren.
Um eine hohe Verfügbarkeit sicherzustellen, sollten Replicas eines Pods auf unterschiedlichen Nodes laufen, damit ein Node-Ausfall nicht alle Replicas betrifft.
Für all diese Anwendungsfälle gibt es innerhalb von Kubernetes die Konzepte Node Affinity, Pod Affinity sowie Pod Anti Affinity.
Was sind Node Affinity, Pod Affinity & Pod Anti Affinity?
Node Affinity beschreibt die Präferenz eines Pods, auf einer bestimmten Node oder einer Gruppe von Nodes mit spezifischen Labels platziert zu werden. Zum Beispiel könnt ihr sicherstellen, dass Pods, die auf SSD-Speicher angewiesen sind, nur auf Nodes mit SSDs laufen.
Pod Affinity beschreibt die Beziehung zwischen Pods untereinander und bestimmt, dass Pods nahe beieinander oder sogar auf demselben Node platziert werden. Ein klassischer Anwendungsfall ist, dass Pods eines Frontend-Services auf demselben Node wie Pods eines Backend-Services laufen, um die Netzwerklatenz zu minimieren.
Pod Anti Affinity hingegen stellt sicher, dass bestimmte Pods auf verschiedenen Nodes laufen, um Redundanz und Ausfallsicherheit zu gewährleisten. Ein typischer Anwendungsfall ist die Verteilung von Redis-Replikas auf verschiedene Nodes.
Typen von Affinity und Anti Affinity
Affinity und Anti Affinity können auf zwei verschiedene Arten definiert werden:
PreferredDuringSchedulingIgnoredDuringExecution
Kubernetes versucht, die Pod-Platzierung basierend auf den angegebenen Regeln zu optimieren, ignoriert diese jedoch, falls sie nicht erfüllt werden können.RequiredDuringSchedulingIgnoredDuringExecution
Diese Regeln müssen strikt eingehalten werden. Wenn Kubernetes keinen passenden Node finden kann, bleibt der Pod unplatziert.
Typische Anwendungsfälle
1. High Availability und Redundanz
Um hohe Verfügbarkeit zu gewährleisten, sollte man sicherstellen, dass Replicas eines Pods nicht auf derselben Node laufen. Dies minimiert das Risiko eines kompletten Ausfalls, falls eine Node ausfällt.
Beispiel für Pod Anti Affinity:
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis-container
image: redis:latest
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: redis
topologyKey: "kubernetes.io/hostname"
Hierbei wird sichergestellt, dass keine zwei Redis-Replicas auf derselben Node platziert werden, was die Ausfallsicherheit erhöht.
2. Optimierung der Netzwerklatenz
Wenn zwei Pods miteinander kommunizieren, kann es sinnvoll sein, sie auf derselben Node zu platzieren, um die Netzwerklatenz zu minimieren.
Beispiel für Pod Affinity:
apiVersion: v1
kind: Pod
metadata:
name: backend-pod
spec:
containers:
- name: backend-container
image: backend-app:latest
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: frontend
topologyKey: "kubernetes.io/hostname"
In diesem Fall wird der Pod auf demselben Node wie ein Pod mit dem Label app=frontend
platziert.
3. Nutzung spezieller Hardware-Ressourcen
Manchmal muss sichergestellt werden, dass Pods nur auf Nodes laufen, die bestimmte Hardware-Ressourcen wie z.B. GPUs oder SSDs bereitstellen.
Beispiel für Node Affinity:
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: myapp-container
image: myapp:latest
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
Hierbei wird der Pod nur auf Nodes platziert, die das Label disktype=ssd
haben, um sicherzustellen, dass er auf der richtigen Hardware läuft.
4. Compliance und Sicherheit
In bestimmten Szenarien müssen Pods getrennt voneinander auf unterschiedlichen Nodes laufen, um Sicherheits- oder Compliance-Anforderungen zu erfüllen.
Pod Anti Affinity kann verwendet werden, um sicherzustellen, dass sensible Workloads nicht auf denselben Nodes wie weniger vertrauenswürdige Workloads laufen.
5. Workload-Isolation
In Multi-Tenant-Umgebungen kann es wichtig sein, Workloads bestimmter Nutzer oder Teams auf dedizierte Nodes zu isolieren. Dies kann mit einer weichen Node Affinity erreicht werden.
Beispiel für eine weiche Node Affinity:
apiVersion: v1
kind: Pod
metadata:
name: preferred-affinity-pod
spec:
containers:
- name: app-container
image: app:latest
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-west
In diesem Beispiel versucht Kubernetes, den Pod bevorzugt auf Nodes mit dem Label zone=us-west
zu platzieren, ignoriert dies aber, falls keine solchen Nodes verfügbar sind.
Fazit
Die Verwendung von Kubernetes Affinity und Anti Affinity ermöglicht es, Workloads im Cluster gezielt zu steuern und zu optimieren. Diese Mechanismen bieten flexible und leistungsstarke Möglichkeiten, die Platzierung von Pods zu kontrollieren und sicherzustellen, dass Anwendungen effizient, sicher und zuverlässig laufen.
Habt ihr noch Fragen oder Feedback zum Thema Affinity und Anti Affinity? Weitere Informationen findet ihr in unserer Kubernetes Einführungsschulung! Ansonsten wendet euch gerne an uns und schreibt uns eine kurze Nachricht oder nutzt den Chat auf unserer Website. Wir freuen uns auf eure Nachrichten.