Introduction
L'affinité de nœud dans Kubernetes vous permet de contrôler sur quels nœuds vos pods seront placés en fonction des étiquettes des nœuds. Elle est plus expressive que l'ancien champ nodeSelector et prend en charge à la fois les exigences strictes et les préférences souples. Cet article explique l'architecture de l'affinité de nœud, propose des exemples pratiques et montre comment vérifier, diagnostiquer et résoudre les problèmes de planification courants.
Vous apprendrez comment le kube-scheduler évalue les règles d'affinité de nœud, comment écrire des manifestes avec des termes obligatoires et préférés, et comment combiner l'affinité avec les teintures, les tolérances et la répartition topologique pour une planification robuste. Chaque exemple inclut les commandes et les sorties attendues afin que vous puissiez reproduire les scénarios dans votre propre cluster.
Inventaire des versions et de l'environnement
Avant d'utiliser l'affinité de nœud, vérifiez la version de votre cluster et les composants de planification impliqués. L'affinité de nœud est stable depuis Kubernetes 1.6 et est disponible dans toutes les distributions modernes. Les exemples de cet article supposent Kubernetes 1.25 ou ultérieur et un contexte kubectl fonctionnel.
Identifier la version de votre cluster
Exécutez :
kubectl version --short
Sortie attendue (exemple) :
Client Version: v1.28.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.3
Si la version de votre serveur est antérieure à 1.6, effectuez une mise à niveau avant d'utiliser l'affinité de nœud. La plupart des clusters actuels satisfont à cette exigence.
Comprendre le rôle du kube-scheduler
Le kube-scheduler est le composant qui lit les spécifications des pods et trouve un nœud approprié. Il s'exécute en tant que pod statique dans l'espace de noms kube-system sur les nœuds du plan de contrôle. Confirmez qu'il est en cours d'exécution :
kubectl get pods -n kube-system -l component=kube-scheduler
La sortie attendue montre un pod de planificateur en cours d'exécution, par exemple :
NAME READY STATUS RESTARTS AGE
kube-scheduler-control-plane-1 1/1 Running 0 12d
Le planificateur utilise un algorithme de notation qui prend en compte les règles d'affinité de nœud pendant les phases de filtrage et de notation. Les règles strictes (requiredDuringSchedulingIgnoredDuringExecution) agissent comme des filtres, éliminant les nœuds qui ne correspondent pas. Les règles souples (preferredDuringSchedulingIgnoredDuringExecution) ajoutent du poids pendant la notation, donnant aux nœuds correspondants un rang plus élevé sans exclure les autres.
Étiquetage de base des nœuds
L'affinité de nœud fait correspondre les exigences des pods aux étiquettes des nœuds. Listez les étiquettes actuelles de vos nœuds :
kubectl get nodes --show-labels
Exemple de sortie :
NAME STATUS ROLES AGE VERSION LABELS
node-1 Ready control-plane 10d v1.28.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node-1,node-role.kubernetes.io/control-plane=
node-2 Ready <none> 10d v1.28.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node-2,disktype=ssd,zone=us-west-2a
node-3 Ready <none> 10d v1.28.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node-3,disktype=hdd,zone=us-west-2b
Dans cet exemple, node-2 possède l'étiquette disktype=ssd, que nous pouvons utiliser pour l'affinité.
Chemin de configuration sûr
Commencez par une observation en lecture seule de l'état actuel de la planification. Ensuite, effectuez le plus petit changement démontrant l'affinité de nœud et vérifiez-le avant de l'étendre.
Étape 1 : Observer les pods existants et leur placement sur les nœuds
Exécutez :
kubectl get pods -o wide --all-namespaces
Cela montre où les pods sont actuellement exécutés. Si vous avez un espace de noms spécifique, utilisez -n <namespace>.
Étape 2 : Concevoir un manifeste minimal d'affinité de nœud
Créez un fichier nommé nginx-affinity.yaml :
apiVersion: v1
kind: Pod
metadata:
name: nginx-affinity
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
Ce pod exige un nœud avec l'étiquette disktype=ssd. Il ne sera planifié que sur node-2 dans notre cluster d'exemple.
Étape 3 : Appliquer le manifeste
kubectl apply -f nginx-affinity.yaml
Sortie attendue :
pod/nginx-affinity created
Étape 4 : Vérifier la planification
Vérifiez le statut du pod et le nœud :
kubectl get pod nginx-affinity -o wide
Exemple de sortie :
NAME READY STATUS RESTARTS AGE IP NODE
nginx-affinity 1/1 Running 0 15s 10.244.2.5 node-2
Le pod s'exécute sur node-2, ce qui correspond à l'étiquette. Si aucun nœud ne correspond, le pod reste à l'état Pending. Nous verrons cela dans la section sur les modes d'échec.
Étape 5 : Inspecter les événements de planification
Utilisez kubectl describe pod nginx-affinity pour voir les événements :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 25s default-scheduler Successfully assigned default/nginx-affinity to node-2
Normal Pulling 24s kubelet Pulling image "nginx:1.25"
Normal Started 23s kubelet Started container nginx
Normal Created 23s kubelet Created container nginx
L'événement Scheduled confirme que le planificateur a utilisé la règle d'affinité.
Vérification et diagnostics
Maintenant qu'une règle obligatoire de base fonctionne, étendons aux règles préférées et combinons l'affinité avec d'autres fonctionnalités de planification.
Exemple d'affinité de nœud préférée
Créez nginx-pref-affinity.yaml :
apiVersion: v1
kind: Pod
metadata:
name: nginx-pref
spec:
containers:
- name: nginx
image: nginx:1.25
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-west-2a
- weight: 20
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
Ce pod préfère les nœuds dans la zone us-west-2a avec un poids plus élevé (80) et préfère également les nœuds ssd avec un poids de 20. Si aucun nœud ne correspond, il sera quand même planifié sur n'importe quel nœud car il n'y a pas de règle obligatoire.
Appliquez et vérifiez :
kubectl apply -f nginx-pref-affinity.yaml
kubectl get pod nginx-pref -o wide
Exemple de sortie :
NAME READY STATUS RESTARTS AGE IP NODE
nginx-pref 1/1 Running 0 10s 10.244.1.7 node-2
Il s'exécute sur node-2, ce qui correspond aux deux préférences. Si node-2 était indisponible, il pourrait s'exécuter sur node-1 ou node-3.
Utiliser matchFields avec le nom du nœud
Vous pouvez cibler un nœud spécifique en utilisant son nom, bien que cela soit moins flexible que les étiquettes. Exemple :
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- node-2
Cette exigence stricte force le pod sur node-2. Utilisez-la avec parcimonie car elle réduit la résilience de la planification.
Combiner l'affinité de nœud avec les demandes de ressources
Ajoutez des demandes de ressources pour influencer la notation et éviter la surallocation. Par exemple, un pod gourmand en mémoire :
apiVersion: v1
kind: Pod
metadata:
name: mem-pod
spec:
containers:
- name: mem-container
image: polinux/stress
resources:
requests:
memory: "512Mi"
cpu: "250m"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: Exists
Ce pod exige un nœud avec l'étiquette disktype (n'importe quelle valeur) et demande des ressources. Le planificateur filtre les nœuds par étiquette, puis par capacité disponible.
Commandes de diagnostic pour le dépannage
Lorsqu'un pod avec affinité de nœud est en attente, utilisez ces commandes :
# Vérifier les événements du pod
kubectl describe pod <pod-name>
# Vérifier les étiquettes des nœuds pour voir s'il y a des correspondances
kubectl get nodes --show-labels
# Obtenir les journaux du planificateur (si vous y avez accès)
kubectl logs -n kube-system kube-scheduler-control-plane-1 | grep -i affinity
Les journaux du planificateur peuvent montrer pourquoi des nœuds ont été rejetés. Recherchez des messages comme "node(s) didn't match node selector" ou "0/3 nodes are available: 3 node(s) didn't match node affinity/selector." C'est l'événement standard pour les échecs.
Modes d'échec et récupération
L'affinité de nœud peut laisser des pods en attente si les exigences sont trop strictes ou si les étiquettes sont mal configurées. Voici des scénarios d'échec courants et comment récupérer.
Scénario 1 : Aucun nœud ne correspond à l'affinité obligatoire
Supposons que vous appliquez un pod exigeant disktype=nvme, mais qu'aucun nœud n'a cette étiquette. Le pod reste Pending.
Vérifiez :
kubectl get pod nvme-pod
kubectl describe pod nvme-pod
La sortie de describe inclut :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 12s default-scheduler 0/3 nodes are available: 3 node(s) didn't match node affinity/selector. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
Options de récupération :
- Vérifiez si l'étiquette est manquante ou mal orthographiée. Ajoutez l'étiquette à un nœud :
kubectl label node node-3 disktype=nvme
- Modifiez la spécification du pod pour utiliser une préférence plus souple ou supprimer l'exigence.
- Si le pod fait partie d'un déploiement, modifiez le déploiement pour corriger l'affinité, puis redémarrez le déploiement :
kubectl edit deployment <deployment-name>
# changer l'affinité, enregistrer et quitter
kubectl rollout status deployment/<deployment-name>
Scénario 2 : L'affinité entre en conflit avec les teintures
Un nœud peut avoir l'étiquette requise mais aussi une teinture que le pod ne tolère pas. Exemple : node-2 a la teinture dedicated=special:NoSchedule. Le pod avec affinité pour node-2 mais sans tolérance ne sera pas planifié.
Diagnostiquez :
kubectl get node node-2 -o json | jq '.spec.taints'
Exemple de sortie :
[
{
"effect": "NoSchedule",
"key": "dedicated",
"value": "special"
}
]
Récupération :
- Ajoutez une tolérance à la spécification du pod :
tolerations:
- key: dedicated
operator: Equal
value: special
effect: NoSchedule
- Ou supprimez la teinture si elle n'est pas nécessaire :
kubectl taint nodes node-2 dedicated=special:NoSchedule-
Scénario 3 : Pod bloqué en terminaison ou non planifiable après la perte d'un nœud
Si un nœud correspondant à l'affinité tombe en panne, les pods avec affinité obligatoire vers ce nœud ne seront pas replanifiés ailleurs car aucun autre nœud ne satisfait la règle. Utilisez kubectl get pods -o wide pour voir l'état du nœud. Si le nœud est NotReady, les pods peuvent être bloqués en Terminating ou Pending.
Récupération :
- Évacuez manuellement le pod :
kubectl delete pod <pod-name> --force --grace-period=0
- Ensuite, ajoutez l'étiquette manquante à un autre nœud ou changez l'affinité en règle préférée.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après avoir effectué des modifications d'affinité de nœud en production.
Avant d'appliquer les modifications
- [ ] Exécutez
kubectl get nodes --show-labelset enregistrez les étiquettes actuelles des nœuds candidats. - [ ] Exécutez
kubectl get pods -o widedans l'espace de noms cible pour voir le placement existant. - [ ] Vérifiez que le planificateur est sain :
kubectl get pods -n kube-system -l component=kube-scheduler - [ ] Examinez les teintures sur les nœuds qui doivent correspondre :
kubectl describe nodes | grep Taints - [ ] Décidez si vous avez besoin d'une affinité obligatoire ou préférée. Préférez les règles préférées pour la haute disponibilité, sauf si vous avez une exigence matérielle stricte.
- [ ] Écrivez le manifeste YAML et validez-le avec
kubectl apply --dry-run=client -f manifest.yaml
Après avoir appliqué les modifications
- [ ] Vérifiez que les pods sont planifiés :
kubectl get pods -o wide - [ ] Vérifiez les événements pour les avertissements FailedScheduling :
kubectl describe pod <pod-name> - [ ] Si vous utilisez un déploiement, confirmez le succès du déploiement :
kubectl rollout status deployment/<deployment-name> - [ ] Testez le comportement en cordonnant un nœud correspondant et en voyant si les pods se replanifient selon les règles d'affinité (si préférée) ou restent en attente (si obligatoire).
- [ ] Documentez le changement dans votre runbook avec les étiquettes exactes et les termes d'affinité utilisés.
Référence rapide des opérateurs d'affinité courants
| Opérateur | Signification | Exemple |
|---|---|---|
| In | La valeur de l'étiquette est dans la liste | disktype In [ssd, nvme] |
| NotIn | La valeur de l'étiquette n'est pas dans la liste | zone NotIn [us-west-2b] |
| Exists | La clé d'étiquette existe quelle que soit la valeur | disktype Exists |
| DoesNotExist | La clé d'étiquette n'existe pas | gpu DoesNotExist |
| Gt | La valeur de l'étiquette est supérieure à un entier (non pris en charge pour les valeurs d'étiquette) | cpu-count Gt 4 (non valide pour l'affinité) |
| Lt | La valeur de l'étiquette est inférieure à un entier | Similaire à ci-dessus |
Remarque : Les opérateurs Gt et Lt ne sont pas valides pour les matchExpressions basés sur les étiquettes dans l'affinité de nœud ; ils ne sont pas pris en charge du tout dans l'affinité de nœud. Tenez-vous-en à In, NotIn, Exists, DoesNotExist.
Conclusion
L'affinité de nœud vous donne un contrôle précis sur le placement des pods tout en maintenant la clarté de vos manifestes. En utilisant des étiquettes, des expressions de correspondance et des poids, vous pouvez construire des politiques de planification qui reflètent la topologie de votre infrastructure et les exigences de vos applications.
Commencez toujours par un petit test, vérifiez les événements de planification et préférez les règles souples à moins qu'une contrainte stricte ne soit nécessaire. Combinez l'affinité de nœud avec les tolérances, les demandes de ressources et les contraintes de répartition topologique pour créer des déploiements résilients et efficaces.
Pour approfondir votre compréhension, expérimentez avec différentes expressions de correspondance, pratiquez avec les teintures et les tolérances, et surveillez le comportement du planificateur en utilisant le point de terminaison des métriques. L'affinité de nœud est un outil fondamental du planificateur Kubernetes, et sa maîtrise vous rendra plus efficace dans la gestion des clusters de production.