Introduction
La gestion de la mémoire dans Kubernetes se concentre souvent sur les requêtes (requests) et les limites (limits), mais la mémoire d'échange (swap) est un élément tout aussi critique. L'espace d'échange permet à un nœud de déplacer les pages inactives de la RAM vers le disque lorsque la pression mémoire augmente. Historiquement, Kubernetes exigeait que le swap soit désactivé sur les nœuds, mais cela a changé avec les versions récentes et le support de cgroup v2. Une mauvaise configuration du swap peut entraîner des pods instables, des évictions inattendues et une dégradation des performances.
Cet article fournit un guide avancé et pratique de la gestion de la mémoire d'échange Kubernetes pour les développeurs, les ingénieurs DevOps, les SRE et les équipes techniques de startups. Nous couvrirons l'architecture sous-jacente, les options de configuration de kubelet, les contrôles au niveau des nœuds à l'aide de cgroup v2 et des scénarios concrets. Vous apprendrez à observer l'utilisation du swap, à configurer le swap en toute sécurité, à vérifier la configuration et à récupérer des modes de défaillance courants.
La sécurité opérationnelle est un thème central : observez avant d'apporter des modifications, limitez le rayon d'impact, évitez d'exposer des informations sensibles dans les commandes, vérifiez les résultats avec des signaux concrets et documentez toujours un chemin de retour à un état connu et fonctionnel.
Inventaire de la version et de l'environnement
Avant de toucher à la configuration du swap, vous devez comprendre votre environnement. Cela inclut la version de Kubernetes, le runtime de conteneurs, le système d'exploitation du nœud, si cgroup v2 est activé et l'état actuel du swap. Commencez par des observations en lecture seule.
Identifiez votre version de Kubernetes :
kubectl version --short
La sortie attendue affiche les versions client et serveur, par exemple :
Client Version: v1.28.2
Server Version: v1.28.2
Le support du swap dans kubelet dépend de la version. À partir de Kubernetes 1.22, le support du swap est en alpha et nécessite l'activation de la porte de fonctionnalité (feature gate) NodeSwap. En 1.28, il est toujours en alpha mais plus stable. Vérifiez les portes de fonctionnalité de votre kubelet :
ps aux | grep kubelet | grep feature-gates
Si NodeSwap=true est présent, le support du swap est activé. Sinon, vous devez l'ajouter.
Vérifiez l'état du swap sur le nœud :
swapon --show
Si le swap est actif, vous verrez une sortie comme :
NAME TYPE SIZE USED PRIO
/dev/sda2 partition 2G 0B -2
Si aucune sortie, le swap est désactivé. Vérifiez également la mémoire totale et le swap avec free -h.
Vérifiez la version de cgroup :
stat -fc %T /sys/fs/cgroup/
Si la sortie est cgroup2fs, vous avez cgroup v2. Si c'est tmpfs, vous êtes sur cgroup v1. La gestion du swap via kubelet nécessite cgroup v2.
Rassemblez cet inventaire avant de modifier quoi que ce soit. Documentez les valeurs et les horodatages. Cela établit une base de référence pour la comparaison et la récupération.
Chemin de configuration sécurisé
Une fois l'environnement compris, vous pouvez procéder à la configuration du swap. L'objectif est d'activer le swap sur le nœud et d'indiquer à kubelet comment le gérer. Cela implique des changements au niveau du nœud (systemd, fstab) et des drapeaux kubelet.
Étape 1 : Activer le swap sur le nœud
Si le swap n'est pas actif, créez un fichier ou une partition de swap. Ici, nous utilisons un fichier de swap de 2 Go.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Pour le rendre persistant aux redémarrages, ajoutez à /etc/fstab :
/swapfile none swap sw 0 0
Vérifiez avec swapon --show et free -h.
Étape 2 : Configurer kubelet pour autoriser le swap
Kubelet possède plusieurs drapeaux liés au swap :
--fail-swap-on=true|false: Si vrai (par défaut), kubelet échoue si le swap est activé sur le nœud. Mettez àfalsepour autoriser le swap.--memory-swap=swapBehavior: Spécifie comment gérer le swap.swapBehaviorpeut être :UnlimitedSwap: par défaut lorsque la porte de fonctionnalité NodeSwap est activée ; aucune limite sur l'utilisation du swap.LimitedSwap: utilisation du swap limitée par la requête mémoire du conteneur (ou la limite mémoire si définie). La limite est calculée commecontainerMemoryRequest * (1 + 100/100)par défaut, où le pourcentage est défini par--memory-swap-swappiness? En fait, la limite est définie par--swap-limit-percentageou--container-swap-limit-percentage. Dans la configuration de kubelet, c'estmemorySwap.swapBehavioretmemorySwap.limitedSwapavecswapLimitPercentage.
Par exemple, pour définir memorySwap dans le fichier de configuration de kubelet :
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
memorySwap:
swapBehavior: LimitedSwap
limitedSwap:
swapLimitPercentage: 50
failSwapOn: false
featureGates:
NodeSwap: true
Enregistrez ceci sous /var/lib/kubelet/config.yaml et assurez-vous que kubelet est démarré avec --config=/var/lib/kubelet/config.yaml. Redémarrez kubelet :
sudo systemctl restart kubelet
Vérifiez que kubelet fonctionne sans erreur :
sudo journalctl -u kubelet -f
Recherchez des lignes indiquant que le swap est autorisé.
Étape 3 : Tester avec un pod
Créez un pod qui demande de la mémoire et observez la limite de swap du cgroup.
pod-swap-test.yaml :
apiVersion: v1
kind: Pod
metadata:
name: swap-test
spec:
containers:
- name: app
image: alpine
command: ["sh", "-c", "sleep 3600"]
resources:
requests:
memory: "500Mi"
Appliquez-le :
kubectl apply -f pod-swap-test.yaml
Trouvez le nœud sur lequel le pod s'exécute et inspectez les limites de mémoire et de swap du conteneur dans le cgroup. D'abord, obtenez l'ID du conteneur :
kubectl get pod swap-test -o jsonpath='{.status.containerStatuses[0].containerID}'
Ensuite, sur le nœud, vérifiez les fichiers du cgroup (ajustez le chemin si nécessaire) :
# En supposant cgroup v2 et un ID de conteneur comme docker://<id>
# Pour containerd, le chemin du cgroup est /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod<uid>.slice/cri-containerd-<id>.scope
# Pour simplifier, cherchez le cgroup du pod
find /sys/fs/cgroup -name '*swap-test*' -type d
Une fois le répertoire du cgroup localisé, vérifiez le fichier memory.swap.max :
cat /sys/fs/cgroup/.../memory.swap.max
Avec LimitedSwap et swapLimitPercentage: 50, et une requête mémoire de 500Mi, la limite de swap devrait être de 250Mi (500 * 0,5). La sortie pourrait être 262144000 (octets). Vérifiez que cela correspond à vos attentes.
Si tout correspond, la configuration est réussie.
Vérification et diagnostics
Après la configuration, une vérification continue est nécessaire. Utilisez des commandes en lecture seule pour surveiller l'utilisation du swap par pod et au niveau du nœud.
Utilisation du swap au niveau du nœud :
free -h
swapon --show
Utilisation du swap par pod ou conteneur :
Comme cgroup v2 expose memory.swap.current, vous pouvez le lire depuis le cgroup du conteneur. Automatisez avec un script qui itère sur les pods d'un nœud.
Exemple utilisant kubectl et l'accès au nœud :
# Sur le nœud, pour chaque cgroup dans kubepods, lire memory.swap.current
for cg in /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod*.slice; do
echo "$cg: $(cat $cg/memory.swap.current)"
done
Pour une vue plus conviviale, utilisez la commande kubectl top (nécessite metrics-server) :
kubectl top pod
Notez que kubectl top pod affiche l'utilisation de la mémoire mais ne montre pas directement le swap. Pour obtenir des informations sur le swap, vous devrez peut-être utiliser les métriques de cAdvisor ou node exporter.
Vérifiez les journaux de kubelet pour les messages liés au swap :
journalctl -u kubelet --since "1 hour ago" | grep -i swap
Recherchez des avertissements comme « le swap est activé mais kubelet n'est pas configuré pour l'utiliser » ou des erreurs sur les limites de mémoire.
Validez le comportement de planification et d'éviction des pods :
Si le swap est activé avec LimitedSwap, kubelet prend en compte le swap pour déterminer si un pod peut être planifié sur un nœud. Testez en créant un déploiement avec des requêtes mémoire élevées et voyez si les pods sont planifiés.
La vérification doit inclure à la fois des tests positifs et négatifs : confirmez que les pods autorisés s'exécutent et que les configurations non autorisées sont rejetées.
Modes de défaillance et récupération
Même avec une configuration soignée, des problèmes peuvent survenir. Voici les modes de défaillance courants et comment récupérer.
Défaillance 1 : kubelet ne démarre pas après l'activation du swap
Symptôme : le service kubelet plante avec une erreur comme :
failed to run Kubelet: failed to create cgroup manager: swap is enabled but cgroup v2 not available
Ou :
invalid configuration: memorySwap is set but NodeSwap feature gate is disabled
Récupération :
- Vérifiez la version de cgroup :
stat -fc %T /sys/fs/cgroup/. Si ce n'est pas cgroup2fs, vous ne pouvez pas utiliser memorySwap. Soit passez à cgroup v2, soit définissezfailSwapOn: true(par défaut) et désactivez le swap. - Si cgroup v2 est disponible mais que la porte de fonctionnalité est manquante, ajoutez
--feature-gates=NodeSwap=trueà la ligne de commande de kubelet ou ajoutezfeatureGates: {NodeSwap: true}dans le fichier de configuration. - Annulez le changement de configuration de kubelet en restaurant la configuration précédente et redémarrez kubelet.
Défaillance 2 : Les pods sont évincés malgré les requêtes mémoire
Symptôme : Les pods sont tués avec le statut Evicted même si le nœud a de la mémoire libre mais que le swap est fortement utilisé.
Récupération :
- Vérifiez les seuils d'éviction de kubelet :
--eviction-hard=memory.available<100Miou similaire. Si le swap est activé, kubelet peut inclure le swap dans le calcul de la mémoire disponible. Ajustez les seuils ou définissez--eviction-softde manière appropriée. - Envisagez de définir
--memory-swap=UnlimitedSwapsi vous voulez vous fier uniquement au swap sans éviction, mais soyez conscient des implications en termes de performances. - Surveillez l'utilisation de la mémoire et du swap ; si le swap provoque une saturation des E/S disque, réduisez l'utilisation du swap ou déplacez les charges de travail.
Défaillance 3 : La limite de swap n'est pas appliquée
Symptôme : le conteneur utilise plus de swap que la limite configurée.
Récupération :
- Vérifiez que cgroup v2 est actif.
- Confirmez que la configuration de kubelet est chargée :
kubectl get --raw /api/v1/nodes/<node>/proxy/configz | jq .(si vous utilisez le point de terminaison configz). Sinon, vérifiez les journaux de kubelet pour la configuration effective. - Vérifiez que le runtime de conteneurs prend en charge la comptabilité du swap. Docker et containerd avec runc le font si cgroup v2 est utilisé.
- Redémarrez le runtime de conteneurs et kubelet.
Défaillance 4 : Le nœud devient insensible en raison du thrashing de swap
Symptôme : E/S disque élevées, charge moyenne élevée, mais faible utilisation du CPU. Les processus échangent constamment.
Récupération :
- Réduisez ou désactivez temporairement le swap :
sudo swapoff -a. - Enquêtez sur les pods qui causent la pression mémoire : utilisez
kubectl top pods --sort-by=memory. - Ajustez les limites de mémoire pour forcer les conteneurs à rester dans la RAM physique.
- Envisagez d'utiliser
LimitedSwapavec un pourcentage plus faible ou de définir des requêtes mémoire appropriées.
Ayez toujours un plan de retour en arrière : documentez la configuration exacte et les commandes pour revenir à l'état précédent.
Liste de contrôle des opérations
Utilisez cette liste de contrôle pour assurer une exploitation sûre en production.
Avant d'activer le swap sur un nœud :
- [ ] Enregistrez le système d'exploitation du nœud, la version du noyau et la version de cgroup.
- [ ] Enregistrez la configuration et les drapeaux actuels de kubelet.
- [ ] Vérifiez la version de Kubernetes et les portes de fonctionnalité.
- [ ] Testez d'abord dans un environnement de staging.
- [ ] Créez un plan de retour en arrière : annulez la configuration de kubelet, désactivez le swap, redémarrez kubelet.
Pendant la configuration :
- [ ] Activez le swap sur le nœud et vérifiez avec
swapon --show. - [ ] Définissez
failSwapOn: falsedans la configuration de kubelet. - [ ] Si vous utilisez memorySwap, définissez
swapBehavioretswapLimitPercentageappropriés. - [ ] Ajoutez
NodeSwap: trueaux portes de fonctionnalité. - [ ] Redémarrez kubelet et vérifiez les journaux pour les erreurs.
Après la configuration :
- [ ] Créez un pod de test avec des requêtes mémoire et vérifiez la limite de swap dans le cgroup.
- [ ] Surveillez l'utilisation du swap au niveau du nœud et l'utilisation de la mémoire des pods.
- [ ] Testez la planification des pods et le comportement d'éviction.
- [ ] Documentez tous les changements et le comportement observé.
En continu :
- [ ] Auditez régulièrement l'utilisation du swap.
- [ ] Maintenez kubelet et le système d'exploitation du nœud à jour.
- [ ] Révisez le statut des portes de fonctionnalité à mesure que Kubernetes évolue (le support du swap est alpha et peut changer).
Conclusion
La gestion de la mémoire d'échange dans Kubernetes est un sujet nuancé qui exige une planification et une exécution minutieuses. Avec l'introduction de cgroup v2 et de la porte de fonctionnalité NodeSwap, il est désormais possible d'activer en toute sécurité le swap sur les nœuds et de contrôler son utilisation par conteneur. Ce guide a parcouru les étapes essentielles : comprendre votre environnement, activer le swap, configurer kubelet, vérifier la configuration et gérer les défaillances.
Commencez toujours par l'observation, apportez des modifications minimales, vérifiez chaque étape et gardez un chemin de retour. Le swap peut être un outil utile pour gérer les pics de mémoire, mais il doit être géré avec des limites claires et une surveillance pour éviter les problèmes de performances. En appliquant les pratiques décrites ici, vous pouvez gérer en toute confiance la mémoire d'échange dans vos clusters Kubernetes.
Pour approfondir, plongez dans la documentation Kubernetes sur le swap mémoire et cgroup v2, et expérimentez dans un cluster de test avant le déploiement en production.