Préparation à la CKA - 16 - Affinity & Anti-Affinity

Publié le 03/12/2024 par Ali Sanhaji

Affinity et anti-affinity

Kubernetes propose des mécanismes avancés pour contrôler finement le placement des Pods sur les nœuds du cluster.
Alors que le nodeSelector permet uniquement des correspondances exactes clé=valeur, les mécanismes d’affinity et d’anti-affinity offrent des règles beaucoup plus expressives.

L’affinity permet d’attirer un Pod vers certains nœuds ou vers d’autres Pods.
L’anti-affinity permet au contraire d’éviter certains nœuds ou certains Pods.

Ces règles peuvent s’appliquer :

  • Entre Pods et nœuds (node affinity)
  • Entre Pods eux-mêmes (pod affinity / pod anti-affinity)

Par exemple :

  • On peut vouloir que des Pods soient placés sur des nœuds spécifiques pour des raisons de performance (node affinity).
  • On peut vouloir que des Pods d’une même application soient répartis sur différents nœuds ou différentes zones pour améliorer la disponibilité (pod anti-affinity).

Les règles d’affinity peuvent être :

  • requiredDuringSchedulingIgnoredDuringExecution
    → La règle doit impérativement être respectée au moment du scheduling.
  • preferredDuringSchedulingIgnoredDuringExecution
    → La règle est souhaitée mais non obligatoire.

La partie IgnoredDuringExecution signifie que si les conditions changent après le scheduling (par exemple si un label est modifié), le Pod déjà en cours d’exécution ne sera pas déplacé automatiquement.

Exemple Nous repartons du pod qu’on avait déployé dans l’article précédent sur le noeud 1 de notre cluster grâce à un nodeSelector:

kubectl get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP            NODE
podsel   1/1     Running   0          4s    10.244.1.24   k8s-node-1

Maintenant, essayons de lancer un pod qui va avoir une anti-affinity avec le pod déjà déployé. C’est-à-dire qu’on voudrait qu’il soit déployé sur un nœud différent que celui-là.

pod-antiaffinity.yaml:

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: podaff
  name: podaff
spec:
  containers:
  - image: busybox:latest
    name: podaff
    args:
      - sleep
      - "3600"
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: run
                operator: In
                values:
                  - podsel
          topologyKey: kubernetes.io/hostname

Expliquons tout ça:

  • podAntiAffinity: ici on est en train de parler de règle de scheduling entre pods
  • requiredDuringSchedulingIgnoredDuringExecution: le scheduler est obligé de respecter l’anti-affinity
  • labelSelector: sur quel label on veut sélectionner les pods à éviter
  • matchExpressions: ici on dit que le pod qu’on cherche à éviter a comme label run=podsel
  • topologyKey: sur quel label de noeud on veut que l’anti-affinity s’applique. Ici on dit que ça s’applique sur le hostname du nœud. Donc si le premier pod (qui a comme label run=podsel) est schedulé sur le noeud numéro 1, on voudrait que notre nouveau pod soit déployé sur un autre nœud que celui-là.

Lançons notre nouveau pod, et nous pourrons voir qu’il va être lancé sur le noeud numéro 2:

kubectl apply -f pod-antiaffinity.yaml

kubectl get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE    IP            NODE
podaff   1/1     Running   0          7s     10.244.2.24   k8s-node-2
podsel   1/1     Running   0          2m3s   10.244.1.24   k8s-node-1

Contrairement au nodeSelector, l’affinity permet d’utiliser des opérateurs comme In, NotIn, Exists ou DoesNotExist, ce qui offre une grande flexibilité dans la définition des règles de placement.

Conclusion

Conclusion

Les mécanismes d’affinity et d’anti-affinity offrent un contrôle avancé du placement des Pods dans un cluster Kubernetes.

Contrairement au nodeSelector, qui ne permet que des correspondances exactes clé=valeur, l’affinity permet d’exprimer des règles plus complexes et plus flexibles, que ce soit vis-à-vis des nœuds (node affinity) ou des autres Pods (pod affinity / pod anti-affinity).

Ces mécanismes sont particulièrement utiles pour :

  • optimiser les performances (regrouper des Pods proches les uns des autres),
  • améliorer la disponibilité (répartir des Pods sur différents nœuds ou zones),
  • structurer proprement des architectures distribuées.

Le placement des Pods reste toutefois conditionné par la disponibilité des ressources du cluster, ce qui introduit la notion de réservation et de gestion des ressources.