Kubernetes 1.37 change la façon dont vos pods consomment et libèrent la mémoire. La bascule vers cgroups v2, la promotion de Memory QoS au stade Beta et l’arrivée de Node Swap en configuration activée par défaut obligent chaque équipe SRE à revoir sa gestion des ressources. Ce tutoriel vous montre comment migrer un cluster existant, activer ces fonctionnalités une à une et éviter les pièges qui provoquent des OOM Kill en cascade. Comptez environ 90 minutes pour suivre les 13 étapes, tester le résultat et valider votre configuration sur un cluster de test avant tout déploiement en production.

Pourquoi Kubernetes 1.37 redéfinit la gestion mémoire des pods

Le projet Kubernetes a publié début octobre 2026 un article de blog consacré à la bascule vers cgroups v2, signé par Paco Xu de DaoCloud. Le message est clair : cgroups v1 arrive en fin de parcours et les clusters qui tournent encore avec ce pilote accumulent une dette technique qui finira par les rattraper. Dans la même version 1.37, trois fonctionnalités liées à la mémoire franchissent un palier important. Memory QoS passe au stade Beta et devient activé par défaut. Les gestionnaires de ressources au niveau du pod atteignent également le stade Beta, même s’ils restent désactivés par défaut pour l’instant. Et le suivi `PersistentVolumeClaimUnusedSinceTime` sur les volumes persistants passe lui aussi en Beta avec activation par défaut.

Ces changements ne sont pas cosmétiques. Memory QoS s’appuie directement sur les contrôleurs cgroups v2 pour appliquer des seuils de régulation mémoire plus fins qu’un simple kill du processus lorsque la limite est atteinte. Node Swap, de son côté, permet désormais à un nœud de swapper une partie de la mémoire d’un pod plutôt que de le tuer immédiatement, ce qui change la tolérance aux pics de charge. Côté adoption cloud, Oracle a annoncé le 8 octobre 2026 une préversion de Kubernetes Engine supportant la version 1.37.0 pour les tests et la validation anticipée, un signal que les fournisseurs gérés commencent tout juste à absorber cette version. Du côté d’AWS, la feuille de route de l’automne 2026 évoquée dans le point hebdomadaire de fin septembre met l’accent sur Amazon ECS Express Mode, le mode EKS Auto et une disponibilité visée à 99,99 % sur le plan de contrôle EKS Provisioned, ce qui montre que la gestion fine des ressources de calcul reste un sujet brûlant pour tous les hyperscalers en 2026.

Pour une équipe plateforme, l’enjeu concret tient en une phrase : réduire les redémarrages intempestifs de pods tout en gardant un isolement mémoire strict. Ce guide couvre la totalité du chemin, du diagnostic de votre version de noyau jusqu’au manifeste de production complet, en passant par les erreurs qui coûtent le plus cher en astreinte de nuit.

Prérequis : versions et outils nécessaires

Avant de commencer, vérifiez que votre environnement répond à ces exigences minimales. Elles conditionnent la réussite de chaque étape qui suit.

  • Kubernetes 1.37.x sur le plan de contrôle et sur chaque nœud (kubelet et kube-proxy alignés sur la même version mineure)
  • Un noyau Linux 5.8 ou supérieur sur les nœuds, condition nécessaire pour un support complet de cgroups v2 unifié
  • containerd 1.7 ou version supérieure, ou CRI-O 1.29 ou version supérieure, compilés avec le support cgroups v2
  • kubectl 1.37.x côté client, pour éviter les écarts de schéma sur les nouvelles API bêta
  • systemd 244 ou supérieur si vous utilisez le pilote cgroupfs via systemd (recommandé en production)
  • Un cluster de test jetable : kind, minikube ou un cluster géré (GKE, AKS, EKS, OKE) avec droits d’administration sur les nœuds
  • Accès SSH ou équivalent (DaemonSet privilégié) sur les nœuds pour vérifier et modifier la configuration cgroups
  • Helm 3.x, facultatif, si vous déployez le projet complet via un chart plutôt qu’en manifestes bruts

Ne testez jamais cette migration directement sur un cluster de production. Clonez la topologie sur un cluster de staging, ou isolez un pool de nœuds dédié avec des taints, avant de toucher au pilote cgroups ou au swap.

Comprendre cgroups v2, Memory QoS et Node Swap en 2026

cgroups v2 unifie les anciens contrôleurs séparés de cgroups v1 (mémoire, CPU, I/O) sous une hiérarchie unique, avec une API plus cohérente et surtout une meilleure granularité pour la pression mémoire. C’est ce socle qui permet à Memory QoS d’exister : la fonctionnalité traduit les requêtes et limites mémoire définies dans vos manifestes de pods en seuils cgroups v2 (`memory.min`, `memory.high`, `memory.max`), au lieu de se contenter d’un kill brutal une fois la limite dépassée. Concrètement, un pod qui dépasse légèrement sa requête mémoire sera freiné (throttling progressif) avant d’être tué, ce qui réduit les redémarrages pour des pics de charge courts.

Node Swap change une autre règle historique de Kubernetes : jusqu’ici, le swap devait être désactivé sur tous les nœuds, sans exception. Désormais, un administrateur peut autoriser le swap pour certains pods, avec un comportement configurable via le champ `memorySwap.swapBehavior` de la configuration kubelet. Deux comportements existent : `LimitedSwap`, qui plafonne le swap disponible en fonction de la QoS du pod, et `NoSwap`, qui conserve l’ancien comportement par défaut. Le troisième chantier, le suivi `PersistentVolumeClaimUnusedSinceTime`, ajoute une condition `Unused` sur les PVC inactifs, ce qui permet d’automatiser le nettoyage du stockage orphelin plutôt que de le découvrir six mois plus tard sur la facture cloud.

Ces trois mécanismes sont indépendants mais complémentaires. Vous pouvez activer cgroups v2 sans Memory QoS, activer Memory QoS sans swap, ou combiner les trois. La suite de ce tutoriel les traite dans l’ordre logique de dépendance : cgroups v2 d’abord, puisque Memory QoS et Node Swap en dépendent toutes les deux.

Il faut aussi distinguer ce qui relève du noyau de ce qui relève de Kubernetes lui-même. cgroups v2 est une fonctionnalité du noyau Linux, exposée par systemd et consommée par le runtime de conteneurs (containerd ou CRI-O). Kubernetes ne fait que s’y adosser via le kubelet, qui traduit vos manifestes en appels vers cette hiérarchie. C’est pour cette raison qu’une mauvaise version de noyau ou de runtime bloque tout le reste de la chaîne, même si votre plan de contrôle affiche fièrement la version 1.37.0 dans `kubectl version`. Gardez cette dépendance en tête chaque fois qu’un comportement vous semble incohérent entre deux nœuds du même cluster : la cause est presque toujours une différence de version de noyau ou de runtime, pas un bug applicatif.

Étape 1 : vérifier la version du cluster et du noyau Linux

Commencez par confirmer que votre plan de contrôle et vos nœuds tournent bien en 1.37.x, puis vérifiez quel pilote cgroups chaque nœud utilise réellement. Beaucoup de clusters censés être « à jour » tournent encore sur cgroups v1 sans que personne ne l’ait remarqué.

kubectl version --short
kubectl get nodes -o wide

# Sur chaque nœud, vérifier le pilote cgroups actif
stat -fc %T /sys/fs/cgroup/
# Résultat attendu pour cgroups v2 : "cgroup2fs"
# Résultat pour cgroups v1 (à migrer) : "tmpfs"

# Vérifier la version du noyau
uname -r

Si `stat` renvoie `tmpfs`, le nœud utilise encore cgroups v1 (ou un mode hybride) et doit être migré avant d’aller plus loin. Si votre distribution date d’avant 2021 (noyau inférieur à 5.8), planifiez une mise à niveau du système avant de continuer : forcer cgroups v2 sur un noyau trop ancien provoque des comportements erratiques du kubelet, parfois des redémarrages en boucle.

Étape 2 : activer les feature gates nécessaires

Memory QoS et Node Swap sont des fonctionnalités Beta en 1.37, donc activées par défaut sur un cluster neuf. Mais sur un cluster migré depuis une version antérieure, les feature gates peuvent avoir été figés manuellement lors d’une précédente mise à niveau. Vérifiez et forcez-les explicitement dans la configuration du kubelet.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  MemoryQoS: true
  NodeSwap: true
  PersistentVolumeClaimUnusedSinceTime: true
cgroupDriver: systemd

Appliquez cette configuration via votre outil de provisioning (kubeadm, un DaemonSet de configuration, ou l’interface de votre fournisseur managé) puis redémarrez le service kubelet nœud par nœud, jamais tous en même temps. Laissez chaque nœud repasser `Ready` avant de passer au suivant.

Étape 3 : basculer les nœuds sur cgroups v2

Sur la plupart des distributions récentes (Ubuntu 22.04 et plus, Debian 11 et plus, Flatcar récent), cgroups v2 est déjà le défaut du système. Le travail consiste alors surtout à vérifier que containerd ou CRI-O pointe vers le bon pilote, et pas à réellement « activer » quoi que ce soit au niveau noyau.

# Vérifier la configuration containerd
sudo cat /etc/containerd/config.toml | grep -A2 "SystemdCgroup"
# Doit afficher : SystemdCgroup = true

# Si besoin, forcer le pilote systemd pour containerd
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl restart kubelet

Sur un système où cgroups v2 n’est pas le défaut (noyaux plus anciens, certaines images cloud figées), vous devrez ajouter `systemd.unified_cgroup_hierarchy=1` aux paramètres de boot du noyau, puis redémarrer la machine complète. Cette opération draine le nœud de ses pods, donc planifiez-la en dehors des heures de forte charge et vidangez le nœud au préalable avec `kubectl drain`.

Étape 4 à 6 : configurer Memory QoS sur vos pods

Une fois cgroups v2 confirmé et le feature gate actif, Memory QoS traduit automatiquement vos requêtes et limites mémoire en seuils cgroups. Vous n’avez rien à ajouter dans le manifeste du pod pour en bénéficier, à condition que vos requêtes et limites soient correctement définies. C’est là que la plupart des équipes découvrent que leurs manifestes historiques étaient mal calibrés.

apiVersion: v1
kind: Pod
metadata:
  name: api-backend
spec:
  containers:
    - name: api
      image: registre.interne/api-backend:1.12.0
      resources:
        requests:
          memory: "256Mi"
          cpu: "250m"
        limits:
          memory: "512Mi"
          cpu: "500m"

Avec Memory QoS actif, ce pod obtient un `memory.min` proche de la requête (256 Mi), un `memory.high` calculé comme un pourcentage configurable de la limite, et un `memory.max` aligné sur la limite (512 Mi). Résultat concret : entre la requête et la limite haute, le pod est ralenti plutôt que tué. Il n’est tué (OOM Kill) que s’il dépasse réellement 512 Mi de façon durable. Étape 5, vérifiez le comportement avec un test de charge mémoire contrôlé (par exemple via `stress-ng –vm 1 –vm-bytes 400M`) pour confirmer que le pod ralentit avant d’être tué. Étape 6, ajustez le ratio `memory.high` si votre application a besoin de marge supplémentaire via l’annotation de nœud dédiée, disponible dans la configuration kubelet.

Étape 7 à 9 : activer et sécuriser le swap avec Node Swap

Le swap reste un sujet sensible : mal configuré, il masque des fuites mémoire et dégrade sévèrement la latence. Node Swap de Kubernetes 1.37 limite ce risque en ne l’activant que pour les pods explicitement marqués, et uniquement en QoS `Burstable`.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
memorySwap:
  swapBehavior: LimitedSwap
featureGates:
  NodeSwap: true

Étape 7, provisionnez un fichier swap dédié sur chaque nœud concerné (par exemple 2 Go via `fallocate` et `mkswap`), jamais le swap système par défaut. Étape 8, appliquez `swapBehavior: LimitedSwap` uniquement sur un pool de nœuds dédié aux charges tolérantes à la latence (jobs batch, files d’attente), pas sur vos nœuds qui hébergent des API synchrones. Étape 9, surveillez `node_memory_SwapUsed_bytes` dans vos métriques Prometheus existantes : un swap qui monte en continu, sans jamais redescendre, signale une fuite mémoire applicative qu’aucune configuration kubelet ne corrigera.

Étape 10 à 11 : durcir le stockage éphémère

Kubernetes 1.37 ajoute des options de permissions pour `emptyDir` et des options de bind mount supplémentaires, deux leviers de durcissement souvent ignorés parce qu’ils ne cassent rien quand on les laisse par défaut, mais qui laissent une porte ouverte en cas de compromission d’un conteneur.

apiVersion: v1
kind: Pod
metadata:
  name: worker-cache
spec:
  containers:
    - name: worker
      image: registre.interne/worker-cache:2.4.0
      volumeMounts:
        - name: cache-tmp
          mountPath: /var/cache/app
  volumes:
    - name: cache-tmp
      emptyDir:
        sizeLimit: "1Gi"

Étape 10, imposez systématiquement un `sizeLimit` sur chaque `emptyDir` : sans cette borne, un conteneur compromis ou buggé peut remplir le disque du nœud et faire tomber tous les pods qui s’y trouvent, pas seulement le sien. Étape 11, combinez ce durcissement avec les restrictions de bind mount au niveau du runtime (containerd ou CRI-O) pour empêcher un conteneur de monter des chemins hôtes sensibles, même en cas de mauvaise configuration de `securityContext`.

Étape 12 à 13 : suivre les PVC inutilisés et automatiser le nettoyage

Le suivi `PersistentVolumeClaimUnusedSinceTime` ajoute une condition `Unused` directement visible sur l’objet PVC, portée par le contrôleur de protection des PVC déjà présent dans le plan de contrôle. Plus besoin de script externe pour détecter le stockage orphelin.

# Étape 12 : lister les PVC avec la condition Unused
kubectl get pvc --all-namespaces -o json | \
  jq -r '.items[] | select(.status.conditions[]?.type=="Unused") | .metadata.namespace + "/" + .metadata.name'

# Étape 13 : croiser avec la date d'inactivité avant suppression manuelle
kubectl describe pvc NOM_DU_PVC -n NAMESPACE | grep -A3 "Conditions"

Ne branchez jamais une suppression automatique directe sur cette condition dès le premier déploiement. Laissez tourner le suivi pendant au moins deux à trois semaines, validez manuellement la liste des PVC concernés, puis seulement ensuite envisagez une routine CronJob de nettoyage avec une fenêtre de grâce confortable (30 jours minimum) et une notification préalable aux équipes propriétaires.

Projet complet : manifeste de production prêt à l’emploi

Voici un manifeste combinant les trois mécanismes vus plus haut sur un déploiement réaliste : Memory QoS implicite via des requêtes et limites correctement dimensionnées, un `emptyDir` borné, et une tolérance explicite pour un pool de nœuds avec swap activé.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: traitement-batch
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: traitement-batch
  template:
    metadata:
      labels:
        app: traitement-batch
    spec:
      nodeSelector:
        pool: swap-tolerant
      tolerations:
        - key: "swap-tolerant"
          operator: "Exists"
          effect: "NoSchedule"
      containers:
        - name: worker
          image: registre.interne/traitement-batch:3.1.0
          resources:
            requests:
              memory: "512Mi"
              cpu: "500m"
            limits:
              memory: "1Gi"
              cpu: "1"
          volumeMounts:
            - name: scratch
              mountPath: /tmp/scratch
      volumes:
        - name: scratch
          emptyDir:
            sizeLimit: "2Gi"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: traitement-batch-data
  namespace: production
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: "10Gi"

Déployez ce manifeste avec `kubectl apply -f manifeste.yaml`, puis passez à la section suivante pour vérifier qu’il se comporte comme prévu avant de le généraliser à vos autres charges de travail.

Décortiquons les choix de ce manifeste. Le `nodeSelector` et la `toleration` associée garantissent que ce déploiement n’atterrit que sur des nœuds explicitement préparés pour le swap, jamais par accident sur un nœud qui héberge aussi vos API critiques. L’écart entre `requests` (512Mi) et `limits` (1Gi) laisse à Memory QoS une marge de manœuvre réelle pour freiner le conteneur avant de le tuer, un ratio d’environ 1 pour 2 qui fonctionne bien pour la majorité des charges batch. Le `sizeLimit` de 2Gi sur le volume `scratch` protège le nœud contre une fuite de fichiers temporaires, un scénario fréquent sur les workers de traitement qui écrivent des fichiers intermédiaires sans les nettoyer en cas de crash. Le PVC séparé, avec son propre cycle de vie, bénéficie automatiquement du suivi `PersistentVolumeClaimUnusedSinceTime` dès qu’il cesse d’être monté par un pod actif.

Vérifier le résultat : logs, métriques et exemples de sortie

Une migration réussie se vérifie par trois signaux concordants : l’absence de hausse des redémarrages de pods, une consommation swap stable sur les nœuds concernés, et une condition `Unused` qui remonte bien sur les PVC dormants.

kubectl describe node NOM_DU_NOEUD | grep -A5 "Allocated resources"

# Exemple de sortie attendue après activation de Memory QoS
#  Resource           Requests     Limits
#  --------           --------     ------
#  cpu                3100m (77%)  6 (150%)
#  memory             6Gi (65%)    11Gi (119%)

kubectl get events --field-selector reason=OOMKilling -A
# Une migration réussie montre une baisse de ce compteur sur 7 jours

Comparez le nombre d’événements `OOMKilling` sur les sept jours précédant et suivant la migration. Une baisse confirme que Memory QoS absorbe désormais les pics courts qui déclenchaient auparavant un kill immédiat. Si le chiffre reste stable ou augmente, revenez à l’étape 4 et vérifiez que les requêtes mémoire de vos pods ne sont pas sous-dimensionnées par rapport à leur usage réel.

Construire un tableau de bord de supervision Prometheus et Grafana

Un tableau de bord dédié vous évite de courir après les symptômes nœud par nœud. Trois courbes suffisent pour couvrir l’essentiel de cette migration : la pression mémoire cgroups v2, l’utilisation du swap par nœud, et le taux d’événements OOM rapporté au nombre total de pods actifs.

# Pression mémoire par nœud (cgroups v2)
rate(container_memory_failcnt{job="kubelet"}[5m])

# Swap utilisé par nœud, en Mo
node_memory_SwapUsed_bytes / 1024 / 1024

# Taux de pods tués pour cause d'OOM sur 24h
sum(increase(kube_pod_container_status_terminated_reason{reason="OOMKilled"}[24h]))
  / sum(kube_pod_info) * 100

Configurez une alerte sur la deuxième requête dès que le swap dépasse 70 % de la capacité allouée sur un nœud en continu pendant plus de quinze minutes : c’est le signal le plus fiable d’une fuite mémoire applicative plutôt que d’un pic de charge normal. Sur la troisième requête, fixez votre seuil d’alerte en comparant à votre taux historique avant migration plutôt qu’à une valeur absolue, chaque cluster ayant un profil de charge différent.

Planifier un rollback sûr en cas de régression

Toute migration de cette ampleur doit prévoir un chemin de retour testé avant le premier déploiement en production, pas inventé dans l’urgence le jour où ça tourne mal. Le rollback se joue à deux niveaux : celui du feature gate, réversible en quelques minutes, et celui du pilote cgroups au niveau système, beaucoup plus lourd à défaire.

# Rollback rapide : désactiver Memory QoS et Node Swap sans toucher au noyau
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  MemoryQoS: false
  NodeSwap: false

# Puis redémarrer le kubelet nœud par nœud
sudo systemctl restart kubelet

Ce rollback de premier niveau suffit dans la grande majorité des cas : il coupe le comportement de throttling et de swap sans exiger un redémarrage complet de la machine. Gardez en revanche à l’esprit que revenir à cgroups v1 au niveau système nécessite de retirer le paramètre de boot `systemd.unified_cgroup_hierarchy`, de redémarrer la machine entière, et donc de vidanger le nœud au préalable comme à l’étape 3. Documentez les deux procédures séparément dans votre runbook d’astreinte, avec le temps estimé de chacune, pour que la personne de garde choisisse la bonne option sans avoir à improviser à 3 heures du matin.

Kubernetes 1.37 face aux clouds managés en Europe

Le calendrier d’adoption de Kubernetes 1.37 diffère sensiblement d’un fournisseur à l’autre, ce qui compte directement pour toute entreprise européenne qui s’appuie sur un cluster géré plutôt que sur une installation maison. Oracle Cloud Infrastructure a ouvert l’accès le plus tôt, avec une préversion de Kubernetes Engine pour la 1.37.0 réservée aux tests et à la validation anticipée. Du côté de Google Cloud, les bulletins de sécurité GKE continuent d’être publiés au fil de l’eau sur une page dédiée, un réflexe à prendre pour toute équipe qui prévoit de combiner cette migration avec une mise à jour de version mineure sur un cluster géré. Microsoft, de son côté, a fait évoluer au 5 octobre 2026 les recommandations de posture de sécurité Kubernetes (KSPM) de Defender for Cloud vers une portée alignée sur les contrôleurs de charge de travail plutôt que sur les pods individuels, un changement qui affecte la façon dont vos alertes de durcissement seront catégorisées une fois la migration terminée.

Pour une équipe française ou européenne, la leçon pratique est simple : ne présumez pas que votre fournisseur managé supporte déjà officiellement la 1.37 au moment de planifier cette migration. Vérifiez la matrice de compatibilité publiée par votre cloud, testez d’abord sur un cluster autogéré ou un pool de nœuds en préversion, et seulement ensuite basculez vos clusters de production gérés une fois le support officiel confirmé. Cette prudence évite de se retrouver coincé entre une fonctionnalité Kubernetes activée côté kubelet et un plan de contrôle managé qui ne la reconnaît pas encore pleinement.

Tableau comparatif : cgroups v1 contre cgroups v2 sous Kubernetes 1.37

Critèrecgroups v1cgroups v2 (Kubernetes 1.37)
HiérarchieContrôleurs séparés par ressourceHiérarchie unifiée unique
Gestion mémoire sous pressionKill direct à la limiteThrottling progressif via Memory QoS
Support du swap par podNon disponible nativementNode Swap avec LimitedSwap
Statut dans Kubernetes 1.37Maintenu, déprécié à termeChemin recommandé
Prérequis noyauToute version récenteLinux 5.8 ou plus recommandé

Tableau des feature gates liés à la mémoire en Kubernetes 1.37

Feature gateStatut en 1.37Activé par défaut
MemoryQoSBetaOui
NodeSwapBetaOui
PersistentVolumeClaimUnusedSinceTimeBetaOui
Gestionnaires de ressources au niveau du podBetaNon
Histogrammes natifs pour les métriquesBetaOui

Erreurs fréquentes à éviter

La plupart des incidents observés après une migration vers cgroups v2, Memory QoS ou Node Swap ne viennent pas d’un bug dans Kubernetes 1.37 lui-même, mais d’une configuration héritée qui n’avait jamais été remise en question. Voici les six erreurs qui reviennent le plus souvent dans les retours d’expérience des équipes plateforme.

  • Migrer tous les nœuds en même temps. Basculez pool par pool, en gardant une capacité de repli sur l’ancien pilote cgroups tant que la nouvelle configuration n’est pas validée en charge réelle.
  • Activer Node Swap sur des nœuds avec des API synchrones critiques. Le swap ajoute de la latence disque imprévisible ; réservez-le aux charges batch ou asynchrones tolérantes.
  • Laisser des `emptyDir` sans `sizeLimit`. C’est la cause la plus fréquente de pannes en cascade sur un nœud partagé, un seul conteneur peut saturer le disque de tous les autres.
  • Oublier de recalibrer les requêtes mémoire avant d’activer Memory QoS. Des requêtes historiquement sous-dimensionnées produisent un throttling permanent au lieu d’un gain de stabilité.
  • Automatiser la suppression des PVC dès l’apparition de la condition `Unused`. Donnez-vous une fenêtre d’observation de plusieurs semaines avant toute suppression automatisée.
  • Ignorer la version du noyau sur des images de base anciennes. Forcer cgroups v2 sur un noyau antérieur à 5.8 provoque des comportements kubelet imprévisibles.

Dépannage : les problèmes les plus courants

Quand quelque chose casse après cette migration, commencez toujours par isoler le niveau en cause : noyau, runtime de conteneurs, kubelet ou plan de contrôle. Les huit cas suivants couvrent la quasi-totalité des tickets de support remontés sur ce type de bascule.

  • Le kubelet refuse de démarrer après activation des feature gates. Vérifiez la syntaxe YAML de `KubeletConfiguration` avec `kubectl proxy` et un linter, une virgule ou une indentation fautive bloque tout le service.
  • `stat -fc %T /sys/fs/cgroup/` renvoie toujours `tmpfs` après redémarrage. Le paramètre de boot `systemd.unified_cgroup_hierarchy=1` n’a probablement pas été pris en compte ; vérifiez `/proc/cmdline`.
  • Les pods redémarrent plus souvent après activation de Memory QoS. Les limites mémoire sont trop proches des requêtes ; augmentez l’écart entre les deux valeurs.
  • Le swap reste à zéro malgré `LimitedSwap` configuré. Confirmez qu’un fichier swap existe réellement sur le nœud via `swapon –show`, le kubelet ne le crée pas automatiquement.
  • La condition `Unused` n’apparaît jamais sur vos PVC. Vérifiez que `PersistentVolumeClaimUnusedSinceTime` est bien listé dans les feature gates du plan de contrôle, pas seulement du kubelet.
  • containerd refuse de démarrer après le changement de `SystemdCgroup`. Validez la syntaxe TOML avec `containerd config dump` avant de redémarrer le service.
  • Les métriques `node_memory_SwapUsed_bytes` sont absentes de Prometheus. Le node-exporter doit être mis à jour vers une version récente qui expose ces compteurs sous cgroups v2.
  • Un nœud entier passe `NotReady` pendant la migration. Le drain n’a probablement pas été complet avant le redémarrage ; relancez `kubectl drain` avec `–ignore-daemonsets –delete-emptydir-data` puis réessayez.

Conseils avancés pour aller plus loin

Une fois la migration stabilisée, affinez votre configuration avec trois leviers supplémentaires. Premièrement, combinez Memory QoS avec les gestionnaires de ressources au niveau du pod dès qu’ils sortiront du statut désactivé par défaut : ils permettent de raisonner en budget mémoire agrégé pour tout le pod plutôt que conteneur par conteneur, ce qui simplifie le dimensionnement des sidecars. Deuxièmement, surveillez l’écosystème de sécurité qui évolue en parallèle : Google documente ses bulletins de sécurité pour GKE sur une page dédiée mise à jour en continu, utile pour croiser vos versions de nœuds avec les correctifs disponibles avant toute migration majeure. Troisièmement, si votre cluster s’appuie sur Azure Arc pour la gestion de flottes Kubernetes en périphérie, Microsoft a annoncé la disponibilité générale d’une extension de gestion de certificats qui combine cert-manager et trust-manager pour automatiser la rotation TLS sur l’ensemble des nœuds edge, un complément logique au durcissement du stockage vu plus haut.

Enfin, documentez systématiquement chaque pool de nœuds migré dans votre inventaire d’infrastructure, avec la date de bascule, le pilote cgroups actif et le comportement swap appliqué. Cette traçabilité évite les mauvaises surprises six mois plus tard quand une nouvelle recrue déploie une charge de travail sans connaître l’historique du cluster.

Checklist récapitulative des 13 étapes

Gardez cette liste à portée de main pendant la migration, ou collez-la directement dans votre ticket de changement pour que la revue de pairs retrouve chaque étape sans relire l’article en entier.

  1. Vérifier la version du cluster, du kubectl et du noyau Linux sur chaque nœud
  2. Activer explicitement les feature gates MemoryQoS, NodeSwap et PersistentVolumeClaimUnusedSinceTime
  3. Basculer le pilote cgroups de chaque nœud vers systemd et cgroups v2, nœud par nœud
  4. Vérifier que vos pods ont des requêtes mémoire réalistes avant d’activer Memory QoS
  5. Tester le comportement de throttling avec une charge mémoire contrôlée
  6. Ajuster le ratio memory.high si nécessaire pour laisser de la marge aux applications
  7. Provisionner un fichier swap dédié sur un pool de nœuds restreint
  8. Appliquer LimitedSwap uniquement sur les charges tolérantes à la latence
  9. Surveiller en continu l’utilisation swap pour détecter les fuites mémoire
  10. Imposer un sizeLimit sur tous les emptyDir du cluster
  11. Durcir les options de bind mount au niveau du runtime de conteneurs
  12. Activer le suivi des PVC inutilisés et observer sans suppression automatique immédiate
  13. Documenter la configuration finale de chaque pool de nœuds dans l’inventaire d’infrastructure

Questions fréquentes

Faut-il migrer vers cgroups v2 avant de mettre à jour vers Kubernetes 1.37 ?
Non, les deux opérations sont indépendantes. Vous pouvez monter en version 1.37 en gardant cgroups v1 temporairement, mais vous ne bénéficierez alors ni de Memory QoS ni de Node Swap tant que la bascule n’est pas faite.

Memory QoS remplace-t-il les limites de ressources classiques ?
Non, il s’appuie dessus. Vos champs `requests` et `limits` restent la seule source de vérité ; Memory QoS change uniquement la façon dont le noyau applique ces valeurs via cgroups v2.

Le swap est-il sûr en production ?
Oui, à condition de le réserver à des pools de nœuds dédiés et à des charges tolérantes à la latence, avec `LimitedSwap` et une surveillance active de l’utilisation swap par nœud.

Que se passe-t-il si mon runtime de conteneurs ne supporte pas cgroups v2 ?
Le kubelet refusera de démarrer correctement ou retombera sur un comportement dégradé. Mettez à jour containerd vers 1.7 minimum, ou CRI-O vers 1.29 minimum, avant toute tentative.

Comment savoir si mes pods bénéficient déjà de Memory QoS ?
Inspectez les fichiers cgroups du conteneur sur le nœud (`memory.min`, `memory.high`, `memory.max` sous `/sys/fs/cgroup/`) ou vérifiez simplement que le feature gate `MemoryQoS` apparaît actif dans la configuration du kubelet.

La condition `Unused` sur un PVC déclenche-t-elle une suppression automatique ?
Non. Elle ajoute uniquement une information visible sur l’objet. La suppression reste une action manuelle ou pilotée par votre propre automatisation, jamais un comportement automatique du contrôleur.

Les fournisseurs cloud gérés supportent-ils déjà Kubernetes 1.37 ?
Le support démarre tout juste. Oracle propose une préversion de Kubernetes Engine pour la 1.37.0 depuis le 8 octobre 2026, réservée aux tests. Vérifiez la disponibilité officielle auprès de votre fournisseur avant de planifier une migration de production.

Dois-je activer les trois fonctionnalités en même temps ?
Non, et ce n’est pas recommandé. Activez cgroups v2 seul, validez sur plusieurs jours, puis Memory QoS, puis enfin Node Swap sur un pool restreint. Chaque étape isolée facilite le diagnostic en cas de régression.