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

Publié le 03/12/2024 par Ali Sanhaji

Affinity et anti-affinity

Dans l’article précédent, nous avons vu les nodeSelectors qui permettent de communiquer via des labels au scheduler sur quels nœuds nous voudrions qu’un pod soit déployé. L’affinity et l’anti-affinity sont des concepts qui étendent les nodeSelectors pour pouvoir communiquer des choses plus évoluées au scheduler. Et ils ne s’appliquent pas uniquement au scheduling des pods par rapport aux nœuds, mais aussi dans le scheduling des pods entre eux.

Comme leur nom l’indique, on peut dire quels pods et quels nœuds ont des affinités ou ont l’inverse de l’affinité, c’est-à-dire du rejet, entre eux. On peut la même chose sur des pods entre eux, lesquels ont des affinités entre eux, et lesquels se rejettent. Lorsqu’on a affinité, le scheduler va mettre ensemble (pod sur nœud, ou pod avec autre pod sur le même nœud). Lorsqu’on a rejet, le scheduler ne va pas mettre ensemble (pod sur un autre nœud que celui rejeté, et pod dans un autre endroit que le pod rejeté).

Un exemple d’utilisation de l’affinity: on voudrait que des pods soient déployés sur le même nœud car on a besoin de performance alors ils doivent être les plus proches les uns des autres.

Un exemple d’utilisation de l’anti-affinity: on voudrait que des pods ne soient pas déployés sur les mêmes nœuds, ou même data centers pour avoir une plus grande disponibilité de l’application au cas où il y a un problème avec un des nœuds ou même un des data centers.

En plus, nous pouvons dire au scheduler si l’affinité est stricte ou pas: requiredDuringSchedulingIgnoredDuringExecution: veut dire que lorsque le scheduler cherche à déployer le pod, il est obligé de respecter l’affinity ou l’anti-affinity (requiredDuringScheduling) preferredDuringSchedulingIgnoredDuringExecution: veut dire que lorsque le scheduler cherche à déployer le pod, il peut faire au mieux pour respecter l’affinity ou l’anti-affinity mais qu’il n’est pas obligé s’il ne trouve pas (preferredDuringScheduling)

La partie IgnoredDuringExecution veut dire que si le pod tourne déjà sur un noeud et qu’on vient de mettre un label sur le noeud qui correspond à une affinity ou anti-affinity qui auraient interféré avec le scheduling, alors on ne touche pas au pod. On ignore la partie affinity durant l’exécution du pod, jusqu’au moment où il faudrait le rescheduler pour une autre raison, et là on respecte l’ordre d’affinity.

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 rester 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

Conclusion

L’affinity et l’anti-affinity permettent de faire des choses très évoluées en termes de scheduling.

Nous pouvons dire au scheduler sur quels nœuds nous voudrions qu’il déploie des pods, encore faut-il qu’il y ait de la place ! Dans le prochain article, nous allons voir comment les nœuds peuvent être occupés jusqu’à saturation grâce à la réservation de ressources.