Introduction
L'affinité et l'anti-affinité des pods sont des fonctionnalités de planification de Kubernetes qui vous permettent de contrôler sur quels nœuds vos pods atterrissent, en fonction des étiquettes des autres pods déjà en cours d'exécution sur ces nœuds. Elles sont puissantes pour la performance, la haute disponibilité et la conformité, mais elles introduisent également des risques de sécurité si elles sont mal configurées. Par exemple, une règle d'anti-affinité trop large peut empêcher des pods critiques d'être planifiés, provoquant un déni de service. Une règle d'affinité trop permissive peut involontairement colocaliser des charges de travail sensibles avec des pods moins fiables, augmentant le rayon d'impact d'une compromission.
Ce guide s'adresse aux ingénieurs de plateforme, aux consultants DevOps et aux équipes techniques de startups qui doivent durcir les configurations d'affinité et d'anti-affinité des pods Kubernetes sans casser leurs clusters. Nous nous concentrons sur des étapes pratiques et vérifiables : observer avant de changer, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier les résultats et documenter les chemins de récupération.
À la fin de cet article, vous serez capable de :
- Inventorier votre version et votre environnement Kubernetes actuels pour connaître les fonctionnalités d'affinité disponibles.
- Appliquer des changements de configuration en toute sécurité à l'aide de manifestes avec des règles explicites et minimales.
- Vérifier que vos paramètres d'affinité et d'anti-affinité fonctionnent comme prévu à l'aide des commandes
kubectl. - Diagnostiquer et récupérer des modes de défaillance courants, tels que les pods non planifiables.
- Suivre une liste de contrôle opérationnelle pour maintenir la sécurité au fil du temps.
Tout au long, nous utilisons des commandes concrètes, des extraits de manifestes et des sorties attendues afin que vous puissiez suivre sur votre propre cluster.
Inventaire de la version et de l'environnement
Avant de durcir l'affinité et l'anti-affinité des pods, vous devez connaître votre version de Kubernetes et les spécificités de votre environnement. L'affinité et l'anti-affinité sont stables depuis Kubernetes 1.14, mais les versions antérieures peuvent manquer de certains champs (par exemple, namespaceSelector pour l'affinité inter-namespaces). Vérifiez également si vous utilisez un service Kubernetes géré (EKS, GKE, AKS) ou un cluster auto-géré, car cela affecte la manière dont vous appliquez les changements et le contrôle d'accès.
Prérequis clés pour ce guide :
- Cluster Kubernetes version 1.19 ou ultérieure (pour utiliser tous les champs d'affinité en toute sécurité).
kubectlinstallé et configuré avec les autorisations appropriées (au moinsget,describe,logsetapplysur les pods, déploiements et namespaces).- Un namespace de test (par exemple,
affinity-test) où vous pouvez expérimenter en toute sécurité sans affecter la production.
Étape 1 : Vérifier la version du cluster
Exécutez :
kubectl version --short
Sortie attendue similaire à :
Client Version: v1.27.3
Server Version: v1.27.3
Si la version du serveur est inférieure à 1.14, vous devez effectuer une mise à niveau avant d'utiliser les fonctionnalités d'affinité avancées.
Étape 2 : Observer l'état actuel de la planification
Avant d'effectuer des changements, capturez l'état actuel. Par exemple, listez tous les pods avec leurs nœuds et leur statut :
kubectl get pods -A -o wide
Cela affiche NODE, STATUS et AGE. Notez tous les pods qui utilisent déjà des règles d'affinité :
kubectl get pods -A -o json | jq '.items[] | select(.spec.affinity != null) | .metadata.name'
Cette commande en lecture seule révèle l'utilisation existante de l'affinité sans rien modifier.
Étape 3 : Protéger les informations d'identification et le matériel privé
Lorsque vous travaillez avec des manifestes, évitez d'incorporer des secrets. Utilisez des espaces réservés et des variables d'environnement. Nous discuterons de la gestion des secrets plus tard.
Étape 4 : Changement le plus petit justifié
Pour votre première tâche de durcissement, choisissez un seul déploiement et une seule règle d'affinité. Ne réécrivez pas toutes les politiques de planification en une seule fois.
Vérification pratique :
kubectl get pods -n <namespace> -o wide
kubectl describe pod <pod-name> -n <namespace>
Dans la sortie de describe, cherchez la section Events. Si un pod n'est pas planifiable en raison de l'affinité, vous verrez un message comme :
0/3 nodes are available: 3 node(s) didn't match pod affinity/anti-affinity rules.
C'est un signal clair que votre règle est trop restrictive.
Exemple d'inventaire d'environnement :
Créons un namespace de test et un déploiement simple à utiliser tout au long de ce guide.
apiVersion: v1
kind: Namespace
metadata:
name: affinity-test
Appliquez-le :
kubectl apply -f namespace.yaml
Créez un déploiement nginx simple avec trois réplicas :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-baseline
namespace: affinity-test
spec:
replicas: 3
selector:
matchLabels:
app: nginx-baseline
template:
metadata:
labels:
app: nginx-baseline
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
Appliquez et vérifiez :
kubectl apply -f nginx-baseline.yaml
kubectl rollout status deployment/nginx-baseline -n affinity-test
Attendez :
deployment "nginx-baseline" successfully rolled out
Vous avez maintenant une base de référence pour la comparaison.
Chemin de configuration sûr
Maintenant, nous durcissons le déploiement en ajoutant des règles d'affinité et d'anti-affinité, mais de manière contrôlée. Le chemin sûr implique :
- Définir des objectifs clairs (par exemple, répartir les réplicas sur les nœuds, garder les pods sensibles à l'écart des pods généraux).
- Utiliser des étiquettes pour cibler les pods de manière appropriée.
- Appliquer un manifeste à la fois et vérifier chaque changement.
- Garder le rayon d'impact petit en commençant par un seul namespace et un nombre limité de réplicas.
Étape 1 : Comprendre les types d'affinité
- Affinité de nœud : concerne les nœuds sur lesquels un pod peut être planifié en fonction des étiquettes de nœud. (Pas l'objet principal ici, mais souvent utilisée ensemble.)
- Affinité de pod : attire les pods les uns vers les autres en fonction des étiquettes de pod.
- Anti-affinité de pod : repousse les pods les uns des autres.
Pour le durcissement de la sécurité, l'anti-affinité est souvent plus critique car elle empêche la colocalisation de charges de travail sensibles. Par exemple, vous pourriez ne pas vouloir que vos pods de traitement des paiements soient sur le même nœud que vos pods Web publics.
Étape 2 : Ajouter de l'anti-affinité au déploiement de base
Nous allons modifier le déploiement nginx pour répartir ses pods sur les nœuds en utilisant podAntiAffinity avec preferredDuringSchedulingIgnoredDuringExecution. C'est une règle douce ; elle essaiera de placer les pods sur différents nœuds, mais planifiera quand même si c'est impossible.
Créez un nouveau manifeste nginx-anti-affinity.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-spread
namespace: affinity-test
spec:
replicas: 3
selector:
matchLabels:
app: nginx-spread
template:
metadata:
labels:
app: nginx-spread
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx-spread
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
Explication :
preferredDuringSchedulingIgnoredDuringExecutionest doux ; le poids (100) signifie qu'il est fortement préféré.- Le
podAffinityTermsélectionne les pods avec l'étiquetteapp=nginx-spread. topologyKey: kubernetes.io/hostnamesignifie que la règle est par nœud (nom d'hôte).
Appliquez :
kubectl apply -f nginx-anti-affinity.yaml
kubectl rollout status deployment/nginx-spread -n affinity-test
Étape 3 : Vérifier le placement
Vérifiez sur quels nœuds les pods se trouvent :
kubectl get pods -n affinity-test -l app=nginx-spread -o wide
Regardez la colonne NODE. Sur un cluster multi-nœuds, vous devriez voir chaque pod sur un nœud différent. Si votre cluster n'a qu'un seul nœud, ils seront tous sur le même nœud, mais la règle douce le permet.
Étape 4 : Durcir avec une anti-affinité requise
Pour une sécurité plus stricte, utilisez requiredDuringSchedulingIgnoredDuringExecution. C'est une règle dure ; si elle ne peut pas être satisfaite, le pod reste non planifié. C'est utile pour les frontières de sécurité critiques, mais peut causer des problèmes de disponibilité s'il n'y a pas assez de nœuds.
Exemple : créez un déploiement avec une anti-affinité requise pour deux réplicas :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-required-spread
namespace: affinity-test
spec:
replicas: 2
selector:
matchLabels:
app: nginx-required-spread
template:
metadata:
labels:
app: nginx-required-spread
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx-required-spread
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.25
Appliquez puis essayez de passer à 4 réplicas sur un cluster à 2 nœuds :
kubectl apply -f nginx-required-spread.yaml
kubectl scale deployment nginx-required-spread --replicas=4 -n affinity-test
Vérifiez le statut des pods :
kubectl get pods -n affinity-test -l app=nginx-required-spread -o wide
Vous verrez probablement deux pods en cours d'exécution (Running) et deux en attente (Pending). Décrivez un pod en attente :
kubectl describe pod <pending-pod-name> -n affinity-test
Dans les événements, vous verrez :
0/2 nodes are available: 2 node(s) didn't match pod anti-affinity rules.
Cela démontre le compromis : l'anti-affinité stricte impose la séparation mais limite la densité.
Considérations de sécurité pour le chemin sûr :
- Utilisez toujours des étiquettes spécifiques et peu susceptibles d'entrer en collision avec d'autres charges de travail. Évitez d'utiliser des étiquettes larges comme
tier: frontendsans une étiquette d'application unique. - Limitez qui peut modifier les règles d'affinité à l'aide de RBAC. Seuls les utilisateurs avec les autorisations
patchouupdatesur les déploiements peuvent changer la planification, donc limitez cet accès. - Ne mettez jamais d'informations sensibles dans les étiquettes ; les étiquettes ne sont pas chiffrées et peuvent être lues par toute personne ayant un accès de liste de pods.
- Utilisez l'isolation par namespace : combinez l'affinité avec
namespaceSelectorpour empêcher les interférences inter-namespaces.
Exemple d'anti-affinité étendue au namespace :
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- payment-processor
namespaceSelector:
matchLabels:
environment: production
topologyKey: kubernetes.io/hostname
Cela garantit que les pods payment-processor ne colocalisent pas avec un pod dans un namespace étiqueté environment=production.
Vérification et diagnostic
Après avoir appliqué les changements d'affinité, vous devez vérifier qu'ils fonctionnent comme prévu et diagnostiquer tout problème. Cette section couvre les commandes clés et l'interprétation.
1. Vérifier les événements de planification
Décrivez toujours les pods pour voir les événements du planificateur :
kubectl describe pod <pod-name> -n <namespace>
Cherchez la section Events. Une planification réussie affiche :
Successfully assigned affinity-test/nginx-spread-xyz to node-1
Un échec dû à l'affinité affiche :
0/3 nodes are available: 3 node(s) didn't match pod affinity/anti-affinity rules.
2. Inspecter le placement des pods par rapport aux règles
Utilisez kubectl get pods -o wide pour vérifier la répartition sur les nœuds. Pour une vérification plus programmatique, utilisez jq pour voir quels pods sont sur quels nœuds :
kubectl get pods -n affinity-test -l app=nginx-spread -o json | jq -r '.items[] | .metadata.name + " -> " + .spec.nodeName'
Sortie attendue :
nginx-spread-abc -> node-1
nginx-spread-def -> node-2
nginx-spread-ghi -> node-3
3. Tester la communication pod-à-pod (si pertinent)
Si votre affinité est pour des raisons de performance (par exemple, garder les pods de cache près des pods d'application), vous pouvez tester la latence ou le débit du réseau. Utilisez kubectl exec pour exécuter des commandes dans un pod :
kubectl exec -it <app-pod> -n affinity-test -- curl http://<cache-service>
Vérifiez que la connexion est rapide ou autorisée par les politiques réseau.
4. Vérifier les journaux pour les erreurs de configuration
Si un pod est en boucle de crash en raison de problèmes liés à l'affinité (rare, mais possible si un conteneur d'initialisation dépend du placement du pod), vérifiez les journaux :
kubectl logs <pod-name> -n <namespace> --previous
5. Valider le YAML avant de l'appliquer
Utilisez kubectl apply --dry-run=client ou --dry-run=server pour valider les manifestes :
kubectl apply -f nginx-anti-affinity.yaml --dry-run=client
Cela détecte les erreurs de syntaxe sans changer le cluster.
Scénario de diagnostic : pod bloqué en état Pending
Supposons que vous ayez appliqué une règle d'anti-affinité requise qui ne peut pas être satisfaite. Le pod reste en attente. Étapes de diagnostic :
kubectl get pods -n <namespace>pour voir le statut.kubectl describe pod <pod-name>pour voir les événements.- Vérifiez le nombre de nœuds :
kubectl get nodes. - Vérifiez les étiquettes des pods existants :
kubectl get pods -n <namespace> --show-labels. - Déterminez si votre
topologyKeyest trop fine. Par exemple, utilisertopologyKey: kubernetes.io/hostnamesur un cluster à nœud unique ne permettra jamais plus d'un pod avec la même étiquette.
Récupération : soit augmenter le nombre de nœuds, passer à une anti-affinité préférée, ou assouplir le sélecteur d'étiquettes.
Modes de défaillance et récupération
Même avec une planification minutieuse, l'affinité et l'anti-affinité peuvent causer des défaillances. Cette section couvre les modes de défaillance courants et comment récupérer avec élégance.
Mode de défaillance 1 : Anti-affinité trop stricte provoque des pods non planifiables
Symptôme : Les pods restent en attente avec un message d'événement didn't match pod anti-affinity rules.
Récupération :
- Réduisez temporairement le déploiement à un nombre qui correspond à la règle :
kubectl scale deployment <name> --replicas=<lower-number> -n <namespace>
- Ou changez l'anti-affinité de requise à préférée en modifiant le déploiement :
kubectl edit deployment <name> -n <namespace>
Remplacez requiredDuringSchedulingIgnoredDuringExecution par preferredDuringSchedulingIgnoredDuringExecution et ajoutez un poids. Enregistrez et quittez ; le déploiement lance une nouvelle révision.
- En dernier recours, supprimez les pods en attente :
kubectl delete pod <pending-pod-name> -n <namespace>
Le contrôleur de déploiement les recrée, mais si la règle est toujours insatisfaisable, ils se remettront en attente.
Mode de défaillance 2 : La règle d'affinité correspond accidentellement à des pods non intentionnels
Symptôme : Les pods sont planifiés sur des nœuds avec des pods qu'ils devraient éviter, ou ils se regroupent avec des pods inattendus.
Cause : Sélecteurs d'étiquettes larges, par exemple, utiliser app: web quand plusieurs applications Web s'exécutent dans le namespace.
Récupération :
- Inspectez les étiquettes des pods en cours d'exécution :
kubectl get pods -n <namespace> --show-labels
- Mettez à jour le sélecteur d'étiquettes du déploiement pour qu'il soit plus spécifique, par exemple,
app: my-webettier: frontend. - Appliquez le manifeste mis à jour et surveillez le déploiement :
kubectl apply -f updated-deployment.yaml
kubectl rollout status deployment/<name> -n <namespace>
Mode de défaillance 3 : Interférence inter-namespaces
Symptôme : Les pods dans le namespace A affectent la planification des pods dans le namespace B en raison de namespaceSelector ou d'étiquettes larges.
Récupération :
- Utilisez
namespaceSelectorpour restreindre l'affinité à des namespaces spécifiques uniquement si nécessaire. Sinon, supprimez les sélecteurs inter-namespaces. - Assurez-vous que chaque namespace a des étiquettes distinctes.
- Envisagez d'utiliser des politiques réseau pour isoler les namespaces, mais c'est distinct de la planification.
Mode de défaillance 4 : Dégradation des performances due à une séparation excessive
Symptôme : Les applications subissent une latence élevée car les pods dépendants sont sur des nœuds différents, imposés par l'anti-affinité.
Récupération :
- Évaluez si l'anti-affinité est vraiment nécessaire. Pour les ensembles avec état où la localité des données compte, vous pourriez utiliser l'affinité de nœud à la place ou un mélange.
- Utilisez
preferredDuringSchedulingIgnoredDuringExecutionavec un poids plus faible pour permettre la colocalisation si nécessaire. - Surveillez les métriques (par exemple, la latence des requêtes) avant et après les changements.
Récupération après sinistre : annuler une mauvaise configuration
Si un déploiement cause des problèmes généralisés, vous pouvez revenir à une révision précédente :
kubectl rollout undo deployment/<name> -n <namespace>
Vérifiez le statut du déploiement :
kubectl rollout status deployment/<name> -n <namespace>
Documenter la récupération
Ayez toujours un runbook pour les déploiements critiques. Incluez :
- Comment vérifier rapidement le statut des pods.
- Commandes sûres pour mettre à l'échelle ou modifier sans temps d'arrêt.
- Procédure de retour en arrière.
- Contact pour l'escalade.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour maintenir des configurations d'affinité et d'anti-affinité sécurisées au fil du temps.
Opérations quotidiennes
- [ ] Vérifier qu'aucun pod n'est bloqué en attente en raison de l'affinité :
kubectl get pods -A | grep Pending. - [ ] Vérifier le nombre de nœuds du cluster :
kubectl get nodes(si des nœuds sont supprimés, l'anti-affinité requise peut échouer). - [ ] Examiner les événements pour les messages liés à l'affinité :
kubectl get events -A | grep -i affinity.
Audits hebdomadaires
- [ ] Lister tous les déploiements avec des règles d'affinité :
kubectl get deployments -A -o json | jq '.items[] | select(.spec.template.spec.affinity != null) | .metadata.name + " in " + .metadata.namespace'. - [ ] Examiner les sélecteurs d'étiquettes pour la spécificité : assurez-vous qu'ils incluent des identifiants d'application uniques.
- [ ] Vérifier les autorisations RBAC pour qui peut modifier les déploiements :
kubectl get rolebindings,clusterrolebindings -A -o yaml | grep -B5 -A5 'deployments'.
Gestion des changements
- [ ] Toujours tester les changements d'affinité dans un namespace non productif d'abord.
- [ ] Utiliser
kubectl apply --dry-run=serverpour valider. - [ ] Échelonner les déploiements : appliquer les changements à un réplica ou à un canari avant le déploiement complet.
- [ ] Avoir un plan de retour en arrière : noter la révision précédente avant d'appliquer (
kubectl rollout history deployment/<name>).
Rappels de durcissement de la sécurité
- [ ] Ne jamais utiliser de données sensibles dans les étiquettes.
- [ ] Éviter une anti-affinité trop large qui peut causer un déni de service auto-infligé.
- [ ] Combiner l'anti-affinité avec des politiques réseau pour une défense en profondeur.
- [ ] Examiner régulièrement les étiquettes de nœuds et les clés de topologie utilisées.
Exemple : vérifier les règles d'affinité dans le cluster
Exécutez cette commande pour obtenir un résumé des types d'affinité utilisés :
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.affinity != null) | .metadata.namespace + "/" + .metadata.name + " has affinity"' | sort
Cela vous aide à repérer une utilisation inattendue.
Conclusion
L'affinité et l'anti-affinité des pods Kubernetes sont des outils puissants pour contrôler le placement des pods, mais ils doivent être configurés avec la sécurité à l'esprit. Une règle d'anti-affinité mal configurée peut conduire à des pods non planifiables et à des pannes de service ; une règle d'affinité permissive peut compromettre l'isolation et augmenter les risques.
Dans ce guide, nous avons couvert :
- Vérifier votre version de Kubernetes et l'inventaire de l'environnement.
- Appliquer des règles d'affinité et d'anti-affinité en toute sécurité, en commençant par des règles douces et en progressant vers des règles dures.
- Vérifier les décisions de planification avec les commandes
kubectlet comprendre les messages d'événements. - Diagnostiquer et récupérer des défaillances courantes telles que les pods en attente et la colocalisation non intentionnelle.
- Suivre une liste de contrôle opérationnelle pour maintenir la sécurité et la disponibilité.
Prochaines étapes :
- Choisissez un déploiement à faible risque dans un namespace de test.
- Ajoutez une règle d'anti-affinité
preferredDuringSchedulingIgnoredDuringExecutionpour répartir les réplicas. - Surveillez le placement des pods et notez tout changement.
- Durcissez progressivement vers des règles requises le cas échéant, en ayant toujours un plan de retour en arrière.
N'oubliez pas que le durcissement de la sécurité est itératif. Observez, changez une chose, vérifiez et documentez. Avec ces pratiques, vous pouvez tirer parti de l'affinité et de l'anti-affinité des pods pour améliorer à la fois la résilience et la sécurité sans sacrifier la stabilité opérationnelle.