## Introduction Les fonctionnalités d'affinité et d'anti-affinité des pods Kubernetes contrôlent le placement des pods par rapport aux autres pods en fonction des étiquettes. Bien qu'elles soient puissantes pour optimiser les performances, garantir une haute disponibilité et imposer une localité ou une séparation des données, une mauvaise configuration peut entraîner des pods non planifiables, une distribution inégale et des problèmes opérationnels. Cet article fournit une liste de contrôle opérationnelle pratique pour gérer l'affinité et l'anti-affinité des pods. Il s'adresse aux développeurs, aux ingénieurs DevOps et aux équipes techniques de startups responsables du déploiement et de la maintenance des charges de travail Kubernetes. Nous couvrons les prérequis de l'environnement, les chemins de configuration sûrs, les techniques de vérification, les modes de défaillance et une liste de contrôle opérationnelle reproductible. L'objectif est de vous aider à éviter les pièges courants et à garder vos clusters sains. Les règles d'affinité et d'anti-affinité ne sont pas à configurer une fois pour toutes. Elles nécessitent une planification, une validation et une révision continue. Nous incluons des commandes concrètes, des extraits YAML et des scénarios pour illustrer chaque étape. Que vous soyez novice avec ces fonctionnalités ou que vous cherchiez à affiner vos pratiques opérationnelles, ce guide vous aidera à gérer les contraintes de planification en toute confiance. ## Inventaire de la version et de l'environnement Avant de modifier les règles d'affinité, établissez une image claire de votre environnement Kubernetes. Cet inventaire vous aide à comprendre le domaine de planification disponible et à éviter les erreurs de configuration dues à des différences de version ou à des étiquettes manquantes. ### 1.1 Version de Kubernetes L'affinité et l'anti-affinité sont stables depuis Kubernetes 1.6, mais les versions plus récentes ont ajouté des améliorations telles que les sélecteurs d'espaces de noms et une correspondance plus flexible. Vérifiez la version de votre cluster avec : ```bash kubectl version --short ``` Sortie attendue : ``` Client Version: v1.27.3 Server Version: v1.27.3 ``` Assurez-vous d'exécuter une version prise en charge. Au moment de la rédaction, Kubernetes 1.24 et les versions ultérieures sont couramment utilisées en production. Si vous utilisez une version plus ancienne, consultez le journal des modifications de Kubernetes pour les changements de planification. ### 1.2 Configuration de la clé de topologie La clé de topologie est l'étiquette de nœud qui définit le domaine de planification pour votre règle d'affinité. Les clés de topologie courantes incluent : - `kubernetes.io/hostname` (niveau nœud) - `topology.kubernetes.io/zone` (zone de disponibilité) - `topology.kubernetes.io/region` (région) - Étiquettes personnalisées telles que `rack`, `datacenter`, ou `failure-domain.beta.kubernetes.io/zone` (obsolète). Listez les étiquettes des nœuds pour voir ce qui est disponible : ```bash kubectl get nodes --show-labels ``` Exemple de sortie : ``` NAME STATUS ROLES AGE VERSION LABELS node-1 Ready 10d v1.27.3 kubernetes.io/hostname=node-1,topology.kubernetes.io/zone=us-east-1a node-2 Ready 10d v1.27.3 kubernetes.io/hostname=node-2,topology.kubernetes.io/zone=us-east-1b ``` Assurez-vous que les étiquettes destinées à être des clés de topologie sont présentes et cohérentes sur tous les nœuds. Des étiquettes incohérentes peuvent rendre les pods non planifiables. ### 1.3 Topologie et capacité du cluster Comprenez la taille du cluster et la distribution des nœuds. Les règles d'affinité contraignent le placement, vous avez donc besoin de suffisamment de nœuds dans chaque domaine de topologie pour satisfaire les exigences. Par exemple, si vous exigez une anti-affinité de pod avec `topologyKey: kubernetes.io/hostname` pour trois réplicas, vous avez besoin d'au moins trois nœuds. Vérifiez le nombre de nœuds et la capacité : ```bash kubectl get nodes kubectl describe nodes | grep -A 5 "Allocatable" ``` Examinez la distribution actuelle des pods pour anticiper les conflits : ```bash kubectl get pods -o wide --all-namespaces ``` ### 1.4 Prérequis pour une utilisation en production - **Accès** : Vous avez besoin de cluster-admin ou de permissions RBAC appropriées pour modifier les charges de travail et consulter les événements. - **Observabilité** : Mettez en place une surveillance (par exemple, Prometheus, Grafana) pour suivre la planification des pods et l'utilisation des ressources des nœuds. - **Sauvegarde** : Assurez-vous de pouvoir revenir en arrière facilement sur les déploiements à l'aide du contrôle de version et des outils de déploiement. - **Tests** : Utilisez un cluster de staging ou un espace de noms pour tester les changements d'affinité avant de les promouvoir en production. ## Chemin de configuration sûr L'introduction de règles d'affinité doit être un processus contrôlé et incrémental. Suivez ces étapes pour minimiser les risques. ### 2.1 Commencez par un pilote étroit et mesurable Choisissez une seule charge de travail non critique avec des exigences de placement claires. Par exemple, un service de cache qui doit s'exécuter sur le même nœud que son application dépendante, ou des réplicas qui doivent être répartis sur plusieurs zones. Définissez des métriques de succès : nombre de pods planifiés, distribution sur les domaines de topologie et impact sur les performances. ### 2.2 Définissez clairement l'intention Utilisez des étiquettes descriptives sur les pods pour rendre les règles d'affinité lisibles et maintenables. Par exemple, étiquetez les pods d'application avec `app: myapp` et `tier: cache` ou `tier: frontend`. Exemple d'attribution d'étiquettes dans une spécification de déploiement : ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: redis-cache spec: replicas: 2 selector: matchLabels: app: redis-cache template: metadata: labels: app: redis-cache tier: cache spec: containers: - name: redis image: redis:6.2 ``` Appliquez le manifeste et vérifiez les étiquettes : ```bash kubectl apply -f redis-cache.yaml kubectl get pods -l app=redis-cache --show-labels ``` ### 2.3 Choisissez le type d'affinité Il existe trois types : - `requiredDuringSchedulingIgnoredDuringExecution` : Exigence stricte. Si elle n'est pas satisfaite, le pod reste non planifié. - `preferredDuringSchedulingIgnoredDuringExecution` : Préférence souple. Le planificateur essaie de la satisfaire mais placera quand même le pod. - `requiredDuringSchedulingRequiredDuringExecution` : Prévu mais pas encore disponible. Évitez de l'utiliser. Commencez par des préférences souples (`preferred...`) lorsque c'est possible, en particulier pour les contraintes moins critiques. Réservez les exigences strictes pour les règles de placement essentielles et assurez-vous que la capacité existe pour les satisfaire. Exemple de règle d'anti-affinité de pod souple pour répartir les pods sur les nœuds : ```yaml spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - myapp topologyKey: kubernetes.io/hostname ``` ### 2.4 Utilisez des sélecteurs d'espaces de noms si nécessaire Par défaut, `podAffinityTerm` ne correspond qu'aux pods du même espace de noms. Si vous avez besoin d'une affinité entre espaces de noms, ajoutez un `namespaceSelector` au terme. Cette fonctionnalité est stable en v1.24 et nécessite la porte de fonctionnalité `podAffinityNamespaceSelector` sur les anciens clusters. Exemple avec sélecteur d'espace de noms : ```yaml podAffinityTerm: labelSelector: matchLabels: app: myapp topologyKey: kubernetes.io/hostname namespaceSelector: matchLabels: team: data ``` ### 2.5 Validez avant d'appliquer Utilisez `kubectl apply --dry-run=client` ou `--dry-run=server` pour vérifier la syntaxe et l'admission. Utilisez également `kubectl diff` pour voir les changements : ```bash kubectl apply -f deployment.yaml --dry-run=client kubectl diff -f deployment.yaml ``` ### 2.6 Déploiement progressif Appliquez les changements d'affinité à un déploiement canari ou à un sous-ensemble de réplicas. Surveillez la planification des pods et le comportement de l'application avant de passer à l'échelle. Utilisez des stratégies de déploiement comme RollingUpdate pour minimiser l'impact. ## Vérification et diagnostics Après avoir appliqué les règles d'affinité, vérifiez que les pods sont planifiés comme prévu et diagnostiquez tout problème. ### 3.1 Vérifiez le placement des pods Utilisez `kubectl get pods -o wide` pour voir sur quels nœuds les pods ont atterri : ```bash kubectl get pods -l app=myapp -o wide ``` Sortie attendue : ``` NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES myapp-6d4b7c9f5-abcde 1/1 Running 0 5m 10.244.1.5 node-1 myapp-6d4b7c9f5-fghij 1/1 Running 0 5m 10.244.2.7 node-2 ``` Si l'anti-affinité fonctionne, les réplicas doivent être sur des nœuds différents. S'ils sont sur le même nœud, vérifiez votre clé de topologie et vos étiquettes. ### 3.2 Inspectez les événements du planificateur Si un pod est en attente, décrivez-le pour voir pourquoi : ```bash kubectl describe pod ``` Recherchez des événements similaires à : ``` Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 2m default-scheduler 0/3 nodes are available: 3 node(s) didn't match pod anti-affinity rules. ``` Cela indique que votre règle est trop restrictive. ### 3.3 Utilisez kubectl get events pour une vue à l'échelle du cluster ```bash kubectl get events --sort-by=.lastTimestamp ``` Filtrez par raison : ```bash kubectl get events | grep FailedScheduling ``` ### 3.4 Validez les étiquettes sur les nœuds et les pods Un mauvais étiquetage est une cause fréquente. Vérifiez les étiquettes des nœuds : ```bash kubectl get nodes --show-labels ``` Vérifiez les étiquettes des pods : ```bash kubectl get pods -l --show-labels ``` ### 3.5 Testez le planificateur avec des pods simulés Créez un pod factice avec les mêmes règles d'affinité pour voir si la planification fonctionne sans affecter la charge de travail réelle. Utilisez une spécification de pod simple et vérifiez son statut. ### 3.6 Vérifiez la répartition topologique Utilisez un script simple pour compter les pods par nœud : ```bash kubectl get pods -l app=myapp -o wide | awk '{print $7}' | sort | uniq -c ``` Cela montrera la distribution. ## Modes de défaillance et récupération Les règles d'affinité peuvent empêcher les pods d'être planifiés, affecter la mise à l'échelle automatique du cluster ou provoquer un sur-engagement. Soyez prêt à diagnostiquer et à récupérer. ### 4.1 Pods non planifiables **Symptôme** : Pods bloqués en état Pending. **Cause** : Les exigences d'affinité strictes ne peuvent pas être satisfaites en raison d'un nombre insuffisant de nœuds ou d'étiquettes conflictuelles. **Récupération** : - Vérifiez les événements comme décrit. - Si nécessaire, assouplissez la règle en passant de `required` à `preferred`. - Ajoutez plus de nœuds avec les étiquettes appropriées. - Corrigez les étiquettes des pods pour qu'elles correspondent au sélecteur. Exemple d'assouplissement : Changez ceci : ```yaml affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - myapp topologyKey: kubernetes.io/hostname ``` en ceci : ```yaml affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - myapp topologyKey: kubernetes.io/hostname ``` Puis mettez à jour le déploiement : ```bash kubectl apply -f deployment.yaml kubectl rollout status deployment/myapp ``` ### 4.2 Clé de topologie trop large **Symptôme** : Les pods sont trop dispersés, ce qui entraîne une sous-utilisation ou une latence réseau excessive. **Cause** : Utilisation de `topologyKey: topology.kubernetes.io/zone` alors que `kubernetes.io/hostname` serait préférable, ou vice versa. **Récupération** : Ajustez la clé de topologie pour correspondre au domaine de défaillance prévu. Surveillez les performances de l'application après le changement. ### 4.3 Conflits entre affinité et sélecteurs de nœuds Combiner des sélecteurs de nœuds avec une affinité de pod peut créer des contraintes impossibles. Par exemple, un sélecteur de nœud exige `disktype: ssd` mais l'affinité exige une colocalisation avec des pods sur des nœuds étiquetés `disktype: hdd`. **Récupération** : Examinez toutes les contraintes ensemble. Simplifiez en supprimant les règles conflictuelles ou en les combinant en une seule affinité de nœud. ### 4.4 Interférence avec le Cluster Autoscaler Une anti-affinité stricte peut empêcher le cluster autoscaler de réduire les nœuds si les pods ne peuvent pas être déplacés. Inversement, des règles souples peuvent permettre un sur-remplissage, provoquant une pression sur les ressources. **Récupération** : Utilisez des budgets de perturbation de pods appropriés et envisagez une affinité de nœud en conjonction avec une anti-affinité de pod pour guider l'autoscaler. Testez les scénarios de mise à l'échelle automatique en staging. ### 4.5 Stratégie de retour en arrière Maintenez toujours des manifestes versionnés. Si une nouvelle configuration d'affinité cause des problèmes, revenez à la version précédente : ```bash kubectl rollout undo deployment/myapp ``` Ou appliquez le manifeste précédent : ```bash kubectl apply -f deployment-previous.yaml ``` Après le retour en arrière, vérifiez que la distribution des pods revient à l'état attendu. ## Liste de contrôle opérationnelle Utilisez cette liste de contrôle reproductible chaque fois que vous prévoyez de modifier les règles d'affinité ou lors de revues périodiques. ### 5.1 Liste de contrôle pré-déploiement - [ ] Confirmez que la version de Kubernetes prend en charge les fonctionnalités requises (sélecteurs d'espaces de noms, etc.). - [ ] Vérifiez que les étiquettes de nœud pour les clés de topologie sont cohérentes et correctes. - [ ] Assurez-vous qu'il y a suffisamment de nœuds dans chaque domaine de topologie pour satisfaire les exigences strictes. - [ ] Identifiez les charges de travail affectées et leur placement actuel. - [ ] Définissez le placement souhaité en termes d'étiquettes et de clés de topologie. - [ ] Choisissez le type d'affinité (strict vs préférentiel) en fonction de la criticité. - [ ] Rédigez ou mettez à jour les manifestes avec des commentaires clairs. - [ ] Validez les manifestes avec `kubectl apply --dry-run=client` et `kubectl diff`. - [ ] Préparez un plan de retour en arrière et ayez accès aux manifestes précédents. - [ ] Planifiez le changement pendant une fenêtre de maintenance si le risque est élevé. ### 5.2 Liste de contrôle de déploiement - [ ] Appliquez les changements à un canari ou à un seul réplica d'abord. - [ ] Surveillez le statut des pods et les événements immédiatement après l'application. - [ ] Vérifiez la distribution des pods sur les domaines de topologie attendus. - [ ] Vérifiez la santé et les performances de l'application (temps de réponse, taux d'erreur). - [ ] Si le canari est réussi, passez progressivement à l'échelle jusqu'à l'état souhaité complet. - [ ] Mettez à jour la documentation et les procédures avec les nouvelles règles d'affinité. ### 5.3 Vérification post-déploiement - [ ] Exécutez `kubectl get pods -o wide -l ` et confirmez le placement sur les nœuds. - [ ] Exécutez `kubectl describe pod` pour tout avertissement. - [ ] Vérifiez les événements du cluster pour les échecs de planification. - [ ] Confirmez qu'aucun nœud n'est soumis à une pression excessive sur les ressources en raison des changements de placement. - [ ] Validez que la mise à l'échelle automatique (si utilisée) se comporte comme prévu. ### 5.4 Maintenance continue - [ ] Révisez périodiquement les règles d'affinité par rapport à la topologie actuelle du cluster et aux changements de charge de travail. - [ ] Mettez à jour les étiquettes des nœuds lors de l'ajout ou de la suppression de nœuds pour maintenir la cohérence. - [ ] Surveillez les pods non planifiables à l'aide d'alertes sur les événements `FailedScheduling`. - [ ] Gardez les manifestes sous contrôle de version et appliquez un processus de revue. - [ ] Testez les scénarios de reprise après sinistre : si une zone tombe en panne, les pods peuvent-ils être replanifiés avec les règles actuelles ? ## Pièges courants et comment les éviter Au-delà des modes de défaillance, plusieurs pièges subtils peuvent piéger même les opérateurs expérimentés. ### 6.1 Ignorer la sémantique de la clé de topologie **Piège** : Utiliser `topologyKey: kubernetes.io/hostname` pour l'anti-affinité alors que vous avez besoin d'une répartition au niveau des zones. Cela peut conduire tous les réplicas à atterrir dans la même zone si plusieurs nœuds se trouvent dans cette zone. **Pourquoi cela arrive** : Une mauvaise compréhension de ce que représente la clé de topologie. La clé définit la portée de la règle. **Comment éviter** : Documentez le domaine de défaillance prévu pour chaque charge de travail. Pour une haute disponibilité entre les zones, utilisez `topology.kubernetes.io/zone` comme clé. Pour une séparation au niveau des nœuds, utilisez `kubernetes.io/hostname`. Examinez les étiquettes réelles des nœuds avant de définir la clé. ### 6.2 Mélanger incorrectement l'affinité et l'anti-affinité de pod **Piège** : Utiliser à la fois l'affinité et l'anti-affinité de pod sur le même pod sans considérer leur interaction. Par exemple, exiger une affinité avec un pod de cache et une anti-affinité des autres réplicas de la même application peut être impossible si le pod de cache s'exécute sur un nœud déjà occupé par un réplica. **Pourquoi cela arrive** : Les opérateurs peuvent ajouter des règles de manière incrémentale sans vue holistique. **Comment éviter** : Dessinez un diagramme du placement souhaité et testez avec un petit nombre de pods. Utilisez `kubectl describe pod` pour voir les décisions du planificateur. ### 6.3 Oublier de mettre à jour les étiquettes **Piège** : Modifier les étiquettes d'application (par exemple, renommer `tier: backend` en `tier: api`) sans mettre à jour les règles d'affinité existantes qui référencent l'ancienne étiquette. **Pourquoi cela arrive** : Les étiquettes sont souvent modifiées lors de refactorisations ou de nouveaux déploiements. **Comment éviter** : Utilisez une politique de gestion des étiquettes. Lorsque vous modifiez des étiquettes, recherchez les règles d'affinité qui les référencent à l'aide de `grep -r "old-label" manifests/`. Mettez à jour les règles et les pods simultanément. ### 6.4 Dépendre uniquement des exigences strictes **Piège** : Définir toutes les règles d'affinité comme `requiredDuringSchedulingIgnoredDuringExecution` sans planification de capacité. Cela peut rendre les pods non planifiables si le nombre de nœuds diminue ou si les étiquettes dérivent. **Pourquoi cela arrive** : Un désir de contrôle strict, mais sans considérer les changements dynamiques du cluster. **Comment éviter** : Préférez les règles souples pour les contraintes non critiques. Si des règles strictes sont nécessaires, assurez-vous que la mise à l'échelle automatique du cluster peut ajouter des nœuds avec des étiquettes correspondantes et qu'il existe une alerte de surveillance pour les pods non planifiables. ### 6.5 Négliger de tester avec des charges de travail réalistes **Piège** : Tester les règles d'affinité avec des pods factices ayant des demandes de ressources minimales, pour découvrir que les pods réels avec des demandes plus importantes ne peuvent pas être planifiés en raison de contraintes de ressources combinées aux règles de placement. **Pourquoi cela arrive** : Les pods factices peuvent ne pas refléter la consommation réelle de ressources. **Comment éviter** : Utilisez des demandes et des limites de ressources représentatives dans les pods de test ou testez dans un environnement de staging avec des charges de travail réalistes. Envisagez d'utiliser `kubectl apply --dry-run=server` avec les spécifications réelles des pods pour vérifier l'admission. ## Conclusion L'affinité et l'anti-affinité des pods sont essentielles pour contrôler le placement des charges de travail dans Kubernetes, mais elles nécessitent une planification et une exploitation minutieuses. En suivant la liste de contrôle de cet article, vous pouvez éviter les pièges courants tels que les pods non planifiables, une distribution inégale et l'interférence avec la mise à l'échelle automatique du cluster. N'oubliez pas de commencer par un pilote étroit et mesurable, de valider les configurations dans un environnement sûr et d'avoir toujours un plan de retour en arrière. Révisez régulièrement les règles d'affinité au fur et à mesure que votre cluster évolue pour vous assurer qu'elles continuent de répondre aux exigences opérationnelles et commerciales. Intégrez ces pratiques dans les opérations quotidiennes pour atteindre une plus grande fiabilité et efficacité dans les déploiements Kubernetes. Avec une approche disciplinée, vous pouvez tirer parti de l'affinité et de l'anti-affinité pour optimiser les performances et la disponibilité sans introduire de risques inutiles.