Au moment du scheduling d’un Pod, le scheduler s’appuie sur plusieurs prédicats et métriques pour placer le pod dans un noeud (capacité des noeuds, les requests ..). La notion de préemption a été introduite en alpha à partir de la version 1.9 et passée en bêta à partir de la 1.11. Il s’agit d’un poid qu’on associe à un Pod au moment du scheduling :
Dans les versions antérieures à la 1.9 le scheduler traitait les demandes de scheduling en suivant une logique de premier arrivé premier servi (FIFO). Cela voudrait dire que des charges de travail moins prioritaires pouvaient bloquer le traitement d’autres plus importantes qui arriveraient après.
On peut imaginer par exemple le cas où la taille du cluster est fixée que ce soit parce qu’on est sur une infrastructure on-premise ou parce qu’on est chez un cloud provider mais pour des raisons de coût ou parce qu'on veut scaler rapidement notre application lors d’un pic de charge par exemple.
Assigner un poids élevé à ces Pods au moment du scheduling garantit la fiabilité du service en les priorisant par rapport à d’autres Pods moins critiques qui leur laisseront la place.
Il s’agit de deux niveaux de SLA différents, l'Éviction est déclenchée en cas de pression sur les ressources du cluster (CPU, Mémoire), ainsi le kubelet intervient pour terminer les Pods les moins prioritaires du point de vue QoS (BestEffort < Burstable < Guaranteed). Quant à la préemption elle intervient au moment du scheduling.
Se baser uniquement sur les QoS ne garantit pas la stabilité désirée d’un service critique lors d’un pic de charge par exemple, en effet, même si on ne déploie que des Pods Burstable et Guaranteed, on n’a pas la garantie que c’est le service le plus critique (par exemple un service destiné à servir des clients en temps réel ) qui aura la priorité de scaler et de poper de nouveaux Pods. En effet, du point de vue fonctionnel, tous les services Guaranteed et Burstable n’auront pas forcément le même SLA.
Ceci n’empêche que ces deux concepts peuvent se croiser au moment de l'éviction causée par une surcharge de ressources, le kubelet classe les Pods candidats à l’éviction en évaluant dans l’ordre ces conditions :
Créer un cluster avec 3 noeuds disposant d’une unité de cpu.
Créer une classe de priorité pour des services critiques :
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
#pour que cette classe de priorité ne soit pas assignéé par défaut aux pods sans priorité
globalDefault: false
Créer un premier déploiement sans aucune priorité définie :
apiVersion: apps/v1
kind: Deployment
metadata:
name: no-priority
spec:
replicas: 3
selector:
matchLabels:
app: no-priority
template:
metadata:
labels:
app: no-priority
spec:
containers:
- name: no-priority
image: nginxdemos/hello
resources:
requests:
cpu: 1
memory: 128Mi
limits:
cpu: 1
memory: 128Mi
Observer que les Pods sont bien créés :
kubectl get pods
NAME READY STATUS RESTARTS AGE
no-priority-6b69478db5-8rkk7 1/1 Running 0 9s
no-priority-6b69478db5-kdznn 1/1 Running 0 9s
no-priority-6b69478db5-nh6fj 1/1 Running 0 9s
Déployer un Pod associé à la classe de priorité déjà définie :
apiVersion: v1
kind: Pod
metadata:
name: high-priority
spec:
containers:
- name: high-priority
image: nginxdemos/hello
resources:
requests:
cpu:1
memory: 128Mi
limits:
cpu: 1
memory: 128Mi
priorityClassName: high-priority
Le Pod sans priorité a été terminé ensuite passé en statut pending (en attente dans la file du scheduler) :
kubectl get pods
NAME READY STATUS RESTARTS AGE
high-priority 0/1 Pending 0 2s
no-priority-6b69478db5-65wcg 0/1 Pending 0 2s
no-priority-6b69478db5-8rkk7 1/1 Running 0 2m
no-priority-6b69478db5-kdznn 0/1 Terminating 0 2m
no-priority-6b69478db5-nh6fj 1/1 Running 0 2m
Observer que comme le total des requests a atteint la capacité maximale du cluster, le premier Pod est passé en statut pending et que le Pod le plus prioritaire a bien été schedulé et déployé dans le noeud :
kubectl get pods
NAME READY STATUS RESTARTS AGE
high-priority 0/1 ContainerCreating 0 11s
no-priority-6b69478db5-65wcg 0/1 Pending 0 11s
no-priority-6b69478db5-8rkk7 1/1 Running 0 2m
no-priority-6b69478db5-nh6fj 1/1 Running 0 2m
kubectl get pods
NAME READY STATUS RESTARTS AGE
high-priority 1/1 Running 0 14s
no-priority-6b69478db5-65wcg 0/1 Pending 0 14s
no-priority-6b69478db5-8rkk7 1/1 Running 0 2m
no-priority-6b69478db5-nh6fj 1/1 Running 0 2m
On a bien vu que les priorités des Pods permettent d’influencer la décision du scheduler et ainsi de garantir que les charges de travail les plus critiques puissent toujours être exécutées par rapport aux charges moins critiques, quand les limites de ressources du cluster sont atteintes, particulièrement quand la taille du cluster est fixe.
Combinés avec les QoS, on a une meilleure maîtrise sur les SLA de nos déploiements et sur le cycle de vie des Pods dans le cluster qui devient plus prédictible.
Lien vers la documentation K8S