Le role de la QoS dans un cluster Kubernetes

Kubernetes doit manager les ressources dans le cluster, avec une allocation propre des ressources, un pod qui consomme de manière excessive de la CPU et/ou RAM impacte les performances des autres workloads.
Kubernetes préviens ce problème par l’utilisation de QoS (Quality of Service) Classes, qui vont permettre de prioriser les workloads et prévenir la contention de ressources.
Chaque pod kubernetes tourne dans un namespace avec des requêtes/limites CPU/Mémoire.

Les QoS Classes

Kubernetes utilise les classes QoS pour manager l’allocation et la priorisation des pods basée sur la CPU et Mémoire.

Kubernetes définit 3 classes (non personnalisables):

  • Guaranteed
  • Burstable
  • BestEffort

Ces classes vont influencer comment Kubernetes va évincer un/des pod(s) lorsque qu’un noeud est sous contention/pression.

Association de la QoS Class à un pod

La QoS est calculée automatiquement par Kubernetes selon les requests et limits de la CPU et mémoire.

  1. Guaranteed
    • chaque container doit avoir des requests et limits
    • requests.cpu = limits.cpu
    • requests.memory = limits.memory
    • c’est la classe la plus protégée
  2. Burstable
    • au moins un conteneur a une request ou limit de CPU ou mémoire
    • la limit CPU ou mémoire est supérieure à la request
    • peu être évincer en cas de pression
  3. BestEffort
    • aucun container n’a de request/limit
    • premier à être évincé

Comment Kubernetes choisit le/les pods à évincer

Quand un noeud arrive en contention/pression, Kubernetes va chercher à libérer des ressources sur le noeud.
Kubernetes va se baser sur :

  1. Dépassement des requests
  2. Priority (PriorityClass value)
  3. proportion de consommation mémoire plus important est évincé : (consommation réelle - requests) / requests

Note: The kubelet does not use the pod’s QoS class to determine the eviction order. You can use the QoS class to estimate the most likely pod eviction order when reclaiming resources like memory. QoS classification does not apply to EphemeralStorage requests, so the above scenario will not apply if the node is, for example, under DiskPressure.

Noeud sous pression

Les conditions pour lesquelles ne noeud est sous pression:

  • Mémoire
  • Disque (logs, images docker ….)
  • PID.

Doc Officielle pour toutes les spécificitées

Quand le node est sous pression, Kubernetes le taint le node en fonction du type de pression :

  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/pid-pressure Le scheduler regarde ces taints pour savoir s’il permet le scheduling de pod sur ce node.

Kubernetes peut se laisser un peu de temps avant de lancer l’éviction de pod, il y a une éviction soft et hard.
Version imagée:

          📊 Utilisation des ressources (mémoire, disque, inode)
                              │
                              ▼
                    🔍 Kubelet surveille en continu
                              │
              ┌───────────────┴───────────────┐
              │                               │
              ▼                               ▼
     🧊 Soft threshold atteint        💥 Hard threshold atteint
 (ex: memory < 200Mi pendant 1 min)   (ex: memory < 100Mi)
              │                               │
     ⏳ Attend (grace period)                ⚡ immédiat
              │                               │
    ❓ Toujours en dépassement ?              │
         │          │                         │
         │ non      │ oui                     │
         ▼          ▼                         ▼
   ✅ rien        🚨 Eviction            🚨 Eviction immédiate
   ne se passe     progressive              brutale
              │                               │
              └───────────────┬───────────────┘
                              ▼
                  🎯 Sélection des Pods à évincer
                              │
                              ▼
         📉 Priority Class et "~QoS"
            1. BestEffort
            2. Burstable
            3. Guaranteed
                              │
                              ▼
                 🗑️ Suppression des Pods
       (grace period respectée… sauf hard eviction)
                              │
                              ▼
                 ♻️ Ressources libérées
                              │
                              ▼
                 ✅ Node redevient stable

Visualiser les conditions d’un noeud

kubectl describe node | grep -A6 Conditions
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Tue, 28 Apr 2026 21:39:22 +0200   Fri, 24 Apr 2026 22:23:02 +0200   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Tue, 28 Apr 2026 21:39:22 +0200   Fri, 24 Apr 2026 22:23:02 +0200   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Tue, 28 Apr 2026 21:39:22 +0200   Fri, 24 Apr 2026 22:23:02 +0200   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready

ou

kubectl get nodes -o json | jq '.items[].status.conditions[] | select(.type != "Ready") | {node: .type, status: .status, reason: .reason}'
{
  "node": "MemoryPressure",
  "status": "False",
  "reason": "KubeletHasSufficientMemory"
}
{
  "node": "DiskPressure",
  "status": "False",
  "reason": "KubeletHasNoDiskPressure"
}
{
  "node": "PIDPressure",
  "status": "False",
  "reason": "KubeletHasSufficientPID"
}

ou

kubectl get nodes -o custom-columns=\
'NAME:.metadata.name,MemoryPressure:.status.conditions[?(@.type=="MemoryPressure")].status,DiskPressure:.status.conditions[?(@.type=="DiskPressure")].status,PIDPressure:.status.conditions[?(@.type=="PIDPressure")].status,Ready:.status.conditions[?(@.type=="Ready")].status'
NAME       MemoryPressure   DiskPressure   PIDPressure   Ready
minikube   False            False          False         True

Bonus

Avec les QoS Class, Kubernetes les associe avec un oom_score_adj, car en cas ou kubelet n’arrive pas à réclamer de la mémoire, le système host va utiliser son mécanisme de OOM Killer.
Car derrière cela Kubernetes utilise les cgroup.

Quality of Service oom_score_adj
Guaranteed -997
BestEffort 1000
Burstable min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999)