Qu’est-ce que l’affinité et l’anti-affinité des pods dans Kubernetes ? – Informatique CloudSavvy
Kubernetes est un système distribué conçu pour mettre à l’échelle des répliques de vos services dans plusieurs environnements physiques. Dans de nombreux cas, cela fonctionne bien dès le départ. Le planificateur Kubernetes place automatiquement vos pods (instances de conteneur) sur des nœuds (machines de travail) qui disposent de suffisamment de ressources pour les prendre en charge.
Malgré tous ses efforts, il arrive parfois que le planificateur ne sélectionne pas un plan avec lequel vous êtes d’accord. Vous voudrez peut-être que les pods soient colocalisés s’ils communiquent régulièrement sur le réseau ; alternativement, certains pods gourmands en calculs pourraient être mieux alloués à des nœuds séparés dans la mesure du possible.
Kubernetes dispose de plusieurs mécanismes qui vous permettent de guider le processus de prise de décision du planificateur afin que les pods se retrouvent sur des nœuds particuliers. Dans cet article, nous nous concentrerons spécifiquement sur les concepts « d’affinité » et « d’anti-affinité » qui vous donnent un contrôle granulaire de la planification. Les affinités définissent des règles qui doivent ou doivent être respectées avant qu’un pod puisse être alloué à un nœud.
Sommaire
Comment fonctionne l’affinité ?
Les affinités sont utilisées pour exprimer les contraintes de planification des pods qui peuvent correspondre aux caractéristiques des nœuds candidats et des pods qui s’exécutent déjà sur ces nœuds. Un pod qui a une « affinité » avec un nœud donné est plus susceptible d’y être programmé ; à l’inverse, une « anti-affinité » rend moins probable qu’elle soit programmée. L’équilibre global de ces poids est utilisé pour déterminer le placement final de chaque pod.
Les évaluations d’affinité peuvent produire des résultats concrets ou non. Un résultat « difficile » signifie que le nœud devoir ont les caractéristiques définies par l’expression d’affinité. Les affinités « soft » agissent comme une préférence, indiquant à l’ordonnanceur qu’il doit utiliser un nœud avec les caractéristiques s’il est disponible. Un nœud qui ne remplit pas la condition sera toujours sélectionné si nécessaire.
Types de condition d’affinité
Il existe actuellement deux types d’affinité différents que vous pouvez définir :
- Affinité de nœud – Utilisé pour contraindre les nœuds pouvant recevoir un pod en faisant correspondre les étiquettes de ces nœuds. L’affinité de nœud ne peut être utilisée que pour définir des affinités positives qui attirent les pods vers le nœud.
- Affinité inter-pods – Utilisé pour contraindre les nœuds pouvant recevoir un pod en faisant correspondre les étiquettes des pods existants déjà en cours d’exécution sur chacun de ces nœuds. L’affinité inter-pod peut être soit une affinité attractive, soit une anti-affinité répulsive.
Dans l’exemple le plus simple possible, un pod qui inclut une condition d’affinité de nœud de label=value
ne sera planifié que pour les nœuds avec un label=value
étiqueter. Un pod avec la même condition mais défini comme une affinité inter-pod sera programmé sur un nœud qui héberge déjà un Cosse avec un label=value
étiqueter.
Définition des affinités de nœud
L’affinité de nœud a deux sous-types distincts :
requiredDuringSchedulingIgnoredDuringExecution
– C’est le matcher d’affinité « dur » qui a besoin le nœud respecte les contraintes que vous définissez.preferredDuringSchedulingIgnoredDuringExecution
– Il s’agit du matcher « soft » pour exprimer une préférence qui est ignorée lorsqu’elle ne peut pas être satisfaite.
le IgnoredDuringExecution
une partie de ces noms verbeux indique explicitement que l’affinité n’est prise en compte que lors de la planification des pods. Une fois qu’un pod est arrivé sur un nœud, l’affinité n’est pas réévaluée. Les modifications apportées au nœud n’entraîneront pas d’expulsion du pod en raison de la modification des valeurs d’affinité. Une future version de Kubernetes pourrait ajouter la prise en charge de ce comportement via le réservé requiredDuringSchedulingRequiredDuringExecution
phrase.
Les affinités de nœud sont attachées aux pods via leur spec.affinity.nodeAffinity
champ manifeste :
apiVersion: v1 kind: Pod metadata: name: demo-pod spec: containers: - name: demo-container # ... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-class operator: In values: - a - b - c - matchExpressions: - key: internal operator: Exists
Ce manifeste crée une règle d’affinité stricte qui programme le pod sur un nœud répondant aux critères suivants :
- Il a un
hardware-class
étiquette avec soita
,b
ouc
comme valeur. - Il a un
internal
étiquette avec n’importe quelle valeur.
Vous pouvez joindre des conditions supplémentaires en répétant matchExpressions
clause. Les opérateurs pris en charge pour les comparaisons de valeurs sont In
, NotIn
, Exists
, DoesNotExist
, Gt
(supérieur à), et Lt
(moins que).
le matchExpression
clauses regroupées sous un même nodeSelectorTerms
clause sont combinés avec un booléen AND
. Ils doivent tous correspondre à un pod pour gagner en affinité avec un nœud particulier. Vous pouvez utiliser plusieurs nodeSelectorTerms
clauses aussi; ceux-ci seront combinés comme une logique OR
opération. Vous pouvez facilement assembler des critères de planification complexes en utilisant ces deux structures.
Les préférences de planification « soft » sont configurées de la même manière. Utiliser nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
à la place ou en plus de requiredDuringSchedulingIgnoredDuringExecution
pour les configurer. Définissez chacune de vos contraintes facultatives comme un matchExpressions
clause dans un preference
champ:
apiVersion: v1 kind: Pod metadata: name: demo-pod spec: containers: - name: demo-container # ... affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: hardware-class operator: In values: - a - b - c
Les règles basées sur les préférences ont un champ supplémentaire appelé weight
qui accepte un entier de 1 à 100. Chaque nœud qui correspond à une préférence a son poids d’affinité total incrémenté du montant défini ; le nœud qui se retrouve avec le poids global le plus élevé se verra attribuer le pod.
Définition des affinités inter-pods
Les affinités inter-pods fonctionnent de manière très similaire aux affinités de nœud, mais présentent des différences importantes. Les modes « hard » et « soft » sont indiqués par le même requiredDuringSchedulingIgnoredDuringExecution
et preferredDuringSchedulingIgnoredDuringExecution
des champs. Ceux-ci doivent être imbriqués sous le spec.affinity.podAffinity
ou spec.affinity.podAntiAffinity
champs selon que vous souhaitez augmenter ou réduire l’affinité du pod en cas de correspondance réussie.
Voici un exemple simple qui démontre à la fois l’affinité et l’anti-affinité :
apiVersion: v1 kind: Pod metadata: name: demo-pod spec: containers: - name: demo-container # ... affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: hardware-class operator: In values: - a - b - c topologyKey: topology.kubernetes.io/zone podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: - labelSelector: matchExpressions: - key: app-component operator: In values: - background-worker topologyKey: topology.kubernetes.io/zone
Le format diffère légèrement de Node Affinity. Chaque matchExpressions
contrainte doit être imbriquée sous un labelSelector
. Pour les soft matches, celui-ci doit à son tour être situé dans un podAffinityTerm
. Les affinités de pod offrent également un ensemble réduit d’opérateurs de comparaison : vous pouvez utiliser In
, NotIn
, Exists
et DoesNotExist
.
Les affinités de pod ont besoin d’un topologyKey
champ. Ceci est utilisé pour limiter l’ensemble global de nœuds qui sont considérés comme éligibles pour la planification, avant la matchExpressions
sont évalués. Les règles ci-dessus programmeront le pod sur un nœud avec le topology.kubernetes.io/zone
label et un pod existant avec le hardware-class
étiquette définie sur a
, b
ou c
. Les nœuds qui ont également un pod avec le app-component=background-worker
l’étiquette recevra une affinité réduite.
Les affinités inter-pods sont un mécanisme puissant pour contrôler la colocation des pods. Cependant, ils ont un impact significatif sur les performances : Kubernetes met en garde contre leur utilisation dans des clusters de plus de quelques centaines de nœuds. Chaque nouvelle demande de planification de pod doit vérifier tous les autres pods sur tous les autres nœuds pour évaluer la compatibilité.
Autres contraintes de planification
Bien que nous nous soyons concentrés sur les affinités dans cet article, Kubernetes fournit également d’autres mécanismes de contrainte de planificateur. Il s’agit généralement d’approches plus simples mais moins automatisées qui fonctionnent bien pour les clusters et les déploiements plus petits.
La contrainte la plus fondamentale est la nodeSelector
champ. Il est défini sur les pods comme un ensemble de paires clé-valeur d’étiquette qui doivent exister sur les nœuds hébergeant le pod :
apiVersion: v1 kind: Pod metadata: name: demo spec: containers: - name: demo # ... nodeSelector: hardware-class: a internal: true
Ce manifeste indique à Kubernetes de planifier uniquement le pod vers les nœuds avec à la fois le hardware-class: a
et internal: true
Étiquettes.
Sélection de nœud avec le nodeSelector
est un bon moyen d’échafauder rapidement une configuration statique basée sur les attributs de longue durée de vos nœuds. Le système d’affinité est beaucoup plus flexible lorsque vous souhaitez exprimer des règles complexes et des préférences facultatives.
Conclusion
Les affinités et les anti-affinités sont utilisées pour configurer des contraintes de planification de pod polyvalentes dans Kubernetes. Par rapport à d’autres options comme nodeSelector
les affinités sont complexes mais vous offrent plus de moyens d’identifier les nœuds compatibles.
Les affinités peuvent agir comme des préférences souples qui signalent l’environnement « idéal » d’un pod à Kubernetes, même s’il ne peut pas être immédiatement satisfait. Le système a également la capacité unique de filtrer les nœuds en fonction de leurs charges de travail existantes afin que vous puissiez mettre en œuvre des règles de colocation de pod.
Un dernier point à noter est que l’affinité n’est pas la fin du processus de planification. Un pod avec une forte affinité calculée avec un nœud peut toujours se retrouver ailleurs en raison de l’entrée de rejets de nœud. Ce mécanisme vous permet de gérer les demandes de planification du point de vue de vos nœuds. Les souillures repoussent activement les pods entrants vers d’autres nœuds, en fait à l’opposé de l’attraction magnétique des affinités. Les sélecteurs de nœuds, les affinités, les rejets et les tolérances sont tous équilibrés pour déterminer l’emplacement final dans le cluster de chaque nouveau pod.