Démystifier l'ordonnancement Kubernetes : une plongée approfondie dans les Prédicats et les Priorités

Démystifier l'ordonnancement Kubernetes : une plongée approfondie dans les Prédicats et les Priorités
Table des matières

L’article d’aujourd’hui présente principalement les étapes du processus d’ordonnancement où les stratégies d’ordonnancement des Prédicats et des Priorités entrent en jeu.

Prédicats

Commençons d’abord par les Prédicats. Le rôle des Prédicats dans le processus d’ordonnancement peut être compris comme un Filtre, c’est-à-dire : il « filtre » une série de nœuds répondant aux conditions parmi tous les nœuds du cluster actuel selon la politique d’ordonnancement. Ces nœuds sont tous des hôtes potentiels pour le Pod en attente d’être ordonnancé. Dans Kubernetes, il existe quatre politiques d’ordonnancement par défaut.

Le premier type s’appelle GeneralPredicates.

Comme son nom l’indique, cet ensemble de règles de filtrage est responsable des politiques d’ordonnancement les plus basiques. Par exemple, PodFitsResources calcule si les ressources CPU et mémoire de l’hôte sont suffisantes. Bien sûr, comme je l’ai mentionné précédemment, PodFitsResources ne vérifie que le champ requests du Pod. Il est à noter que le planificateur Kubernetes ne définit pas de types de ressources spécifiques pour les ressources matérielles comme les GPU, mais utilise plutôt un format Key-Value appelé Extended Resource pour les décrire. Par exemple :

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo
spec:
  containers:
  - name: extended-resource-demo-ctr
    image: nginx
    resources:
      requests:
        alpha.kubernetes.io/nvidia-gpu: "2"
      limits:
        alpha.kubernetes.io/nvidia-gpu: "2"

On voit que notre Pod déclare l’utilisation de deux GPU de type NVIDIA avec la définition alpha.kubernetes.io/nvidia-gpu=2. Dans PodFitsResources, le planificateur ne sait pas réellement que la signification de ce champ Key est GPU, mais utilise directement la Value suivante pour le calcul. Bien sûr, dans le champ Capacity du nœud, vous devez également ajouter le nombre total de GPU sur cet hôte, par exemple : alpha.kubernetes.io/nvidia-gpu=4. Je présenterai ces processus en détail plus tard lors de l’explication de Device Plugin.

PodFitsHost vérifie si le nom de l’hôte correspond au spec.nodeName du Pod. PodFitsHostPorts vérifie si les ports de l’hôte (spec.nodePort) demandés par le Pod entrent en conflit avec des ports déjà utilisés. PodMatchNodeSelector vérifie si les nœuds spécifiés par nodeSelector ou nodeAffinity du Pod correspondent au nœud examiné, etc.

On voit donc qu’un tel ensemble de GeneralPredicates est la condition de filtrage la plus basique pour que Kubernetes vérifie si un Pod peut s’exécuter sur un nœud. Par conséquent, les GeneralPredicates sont également directement appelés par d’autres composants (comme kubelet).

Le deuxième type est constitué des règles de filtrage liées au Volume.

Cet ensemble de règles de filtrage est responsable de la politique d’ordonnancement relative au Volume persistant du conteneur. Parmi elles, NoDiskConflict vérifie s’il y a un conflit entre les Volumes persistants déclarés par plusieurs Pods. Par exemple, les Volumes de type AWS EBS ne peuvent pas être utilisés par deux Pods en même temps. Ainsi, lorsqu’un Volume EBS nommé A est déjà monté sur un certain nœud, un autre Pod qui déclare également utiliser ce Volume A ne peut pas être ordonnancé sur ce nœud.

MaxPDVolumeCountPredicate vérifie si un certain type de Volume persistant sur un nœud a dépassé un certain nombre. Si c’est le cas, le Pod qui déclare utiliser ce type de Volume persistant ne peut plus être ordonnancé sur ce nœud. VolumeZonePredicate vérifie si l’étiquette Zone (domaine de haute disponibilité) du Volume persistant correspond à l’étiquette Zone du nœud examiné.

De plus, il existe une règle appelée VolumeBindingPredicate. Elle est chargée de vérifier si le champ nodeAffinity du PV correspondant au Pod correspond à l’étiquette d’un certain nœud. Le Volume persistant local (Local Persistent Volume) doit utiliser nodeAffinity pour se lier à un nœud spécifique. Cela signifie en fait que pendant la phase de Prédicats, Kubernetes doit être capable d’ordonnancer en fonction des attributs de Volume du Pod.

En outre, si la PVC du Pod n’est pas encore liée à un PV spécifique, le planificateur est également chargé de vérifier tous les PV à lier. Lorsqu’il y a un PV disponible et que le nodeAffinity de ce PV est cohérent avec le nœud examiné, cette règle renverra « succès ». Par exemple :

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-local-pv
spec:
  capacity:
    storage: 500Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /mnt/disks/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - my-node

On voit que le répertoire persistant correspondant à ce PV n’apparaîtra que sur l’hôte nommé my-node. Par conséquent, tout Pod utilisant ce PV via une PVC doit être ordonnancé sur my-node pour fonctionner correctement. VolumeBindingPredicate est exactement l’endroit où le planificateur prend cette décision.

Le troisième type est constitué des règles de filtrage liées à l’hôte.

Cet ensemble de règles examine principalement si le Pod à ordonnancer remplit certaines conditions du nœud lui-même. Par exemple, PodToleratesNodeTaints vérifie le mécanisme de « teinture » (taint) du nœud que nous utilisons souvent. Ce n’est que lorsque le champ Toleration du Pod correspond au champ Taint du nœud que ce Pod peut être ordonnancé sur ce nœud. NodeMemoryPressurePredicate vérifie si la mémoire du nœud actuel n’est plus suffisante. Si c’est le cas, le Pod en attente d’ordonnancement ne peut pas être ordonnancé sur ce nœud.

Le quatrième type est constitué des règles de filtrage liées au Pod.

Cet ensemble de règles chevauche la plupart des GeneralPredicates. Ce qui est spécial, c’est PodAffinityPredicate. Le rôle de cette règle est de vérifier les relations d’affinité et d’anti-affinité entre le Pod à ordonnancer et les Pods existants sur le nœud. Par exemple :

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-antiaffinity
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: docker.io/ocpqe/hello-pod

Dans cet exemple, la règle podAntiAffinity spécifie que ce Pod ne souhaite pas se trouver sur le même nœud qu’un Pod portant l’étiquette security=S2. Il est à noter que PodAffinityPredicate a une portée, comme la règle ci-dessus, qui n’est valide que pour les nœuds portant l’étiquette de clé kubernetes.io/hostname. C’est le rôle du mot-clé topologyKey.

Ces quatre types de Prédicats constituent la stratégie de base permettant au planificateur de déterminer si un nœud peut exécuter le Pod à ordonnancer. Lors de l’exécution, lorsqu’un Pod est ordonnancé, le planificateur Kubernetes lance simultanément 16 Goroutines pour calculer concurremment les Prédicats pour tous les nœuds du cluster, et renvoie finalement la liste des hôtes pouvant exécuter ce Pod.

Il convient de noter que lors de l’exécution des Prédicats pour chaque nœud, le planificateur vérifie dans un ordre fixe. Cet ordre est déterminé en fonction de la signification des Prédicats eux-mêmes. Par exemple, les Prédicats liés à l’hôte sont vérifiés plus tôt. Sinon, calculer PodAffinityPredicate sur un hôte dont les ressources sont gravement insuffisantes n’aurait pas de sens.

Priorités

Après le « filtrage » des nœuds dans la phase des Prédicats, le travail de la phase des Priorités consiste à attribuer des scores à ces nœuds. La plage de notation ici est de 0 à 10 points, et le nœud avec le score le plus élevé est le meilleur nœud auquel le Pod sera finalement lié. La règle de notation la plus couramment utilisée dans les Priorités est LeastRequestedPriority. Cet algorithme sélectionne en fait l’hôte avec le plus de ressources inactives (CPU et Mémoire).

De plus, il existe trois autres Priorités : NodeAffinityPriority, TaintTolerationPriority et InterPodAffinityPriority. Comme leurs noms l’indiquent, leur signification et leur méthode de calcul sont similaires aux trois Prédicats précédents : PodMatchNodeSelector, PodToleratesNodeTaints et PodAffinityPredicate. Cependant, en tant que Priorité, plus un nœud satisfait aux règles ci-dessus, plus son score sera élevé.

Dans les Priorités par défaut, il existe également une stratégie appelée ImageLocalityPriority. Il s’agit d’une nouvelle règle d’ordonnancement activée dans Kubernetes v1.12, à savoir : si l’image nécessaire au Pod à ordonnancer est volumineuse et existe déjà sur certains nœuds, ces nœuds auront un score plus élevé. Bien sûr, pour éviter que l’algorithme ne provoque un empilement de l’ordonnancement (scheduling stacking), le planificateur optimisera également en fonction de la distribution de l’image lors du calcul du score, c’est-à-dire : si le nombre de nœuds où la grande image est distribuée est très faible, alors le poids de ces nœuds sera réduit, « compensant » ainsi le risque de provoquer un empilement de l’ordonnancement.

En résumé, tel est le principe de fonctionnement principal des règles d’ordonnancement par défaut dans le planificateur de Kubernetes. Dans le processus d’exécution réel, les informations sur le cluster et les Pods dans le planificateur ont été mises en cache, de sorte que le processus d’exécution de ces algorithmes est relativement rapide.

De plus, pour des algorithmes d’ordonnancement plus complexes, comme PodAffinityPredicate, ils ne se contentent pas de prêter attention au Pod à ordonnancer et au nœud examiné lors du calcul, mais doivent également surveiller les informations de l’ensemble du cluster, par exemple en parcourant tous les nœuds et en lisant leurs Labels. À ce moment-là, le planificateur Kubernetes calcule d’abord les informations du cluster nécessaires à l’algorithme avant d’exécuter l’algorithme d’ordonnancement pour chaque Pod à ordonnancer, puis les met en cache. Ainsi, lors de l’exécution réelle de l’algorithme, le planificateur n’a besoin que de lire les informations mises en cache pour effectuer le calcul, évitant ainsi d’avoir à récupérer et calculer à plusieurs reprises les informations de l’ensemble du cluster pour chaque nœud lors du calcul des Prédicats.

Résumé

En résumé, cet article a abordé les principaux algorithmes d’ordonnancement au sein du planificateur par défaut de Kubernetes. Il est important de noter qu’en plus des règles couvertes dans cet article, il existe en fait d’autres stratégies dans le planificateur Kubernetes qui ne sont pas activées par défaut. Vous pouvez spécifier un fichier de configuration pour kube-scheduler ou créer un ConfigMap pour configurer les règles à activer ou désactiver. De plus, vous pouvez contrôler le comportement du planificateur en définissant des poids pour les Priorités. Ceci conclut l’explication des mécanismes d’ordonnancement par défaut dans le planificateur de Kubernetes.

Il convient de mentionner que même si le planificateur fonctionne rapidement grâce à la mise en cache des informations du cluster et des Pods, pour des algorithmes plus complexes, le planificateur doit prendre en compte l’état de l’ensemble du cluster, pas seulement le Pod et le nœud en question. Cela inclut des calculs préliminaires et la mise en cache des informations nécessaires du cluster avant l’exécution réelle de l’algorithme d’ordonnancement, garantissant ainsi efficacité et précision dans le processus d’ordonnancement.

Novita AI est la plateforme cloud tout-en-un qui propulse vos ambitions en matière d’IA. Avec des API intégrées de manière transparente, de l’informatique sans serveur et une accélération GPU, nous fournissons les outils rentables dont vous avez besoin pour créer et développer rapidement votre activité basée sur l’IA. Éliminez les problèmes d’infrastructure et commencez gratuitement - Novita AI fait de vos rêves d’IA une réalité.

Lectures recommandées :

  1. Ce que vous devez savoir sur Docker
  2. Pourquoi nous avons besoin de Pods