Blog

Le fichier de swap manquant qui a figé nos serveurs

Le 24 septembre, l'un de nos serveurs de production a cessé de répondre. Le répartiteur de charge l'a marqué comme défaillant et notre outil d'exécution de commandes à distance ne pouvait plus l'atteindre — mais rien n'avait planté et aucun processus n'avait été tué pour excès de mémoire. Le serveur était en thrashing : sans espace de swap, et avec des conteneurs autorisés à utiliser plus de mémoire que la machine n'en avait, Linux passait son temps à évincer et relire les fichiers nécessaires à son fonctionnement, sans presque progresser. L'ajout d'un fichier de swap de 2 Go sur chaque serveur a réglé le problème. La leçon la plus intéressante concernait les contrôles de santé.

À quoi ressemblait le « blocage »

  • Les contrôles de santé du répartiteur de charge expiraient sur ce serveur.
  • Les commandes envoyées via AWS Systems Manager restaient en « Pending » sans jamais s'exécuter.
  • Aucune erreur de mémoire insuffisante dans les journaux. Aucun processus n'était mort.

Une semaine plus tôt, nous avions migré la production vers des serveurs ARM plus petits disposant chacun de 2 Go de RAM. Notre serveur de préproduction — encore plus petit — s'était figé de la même façon pendant la décompression d'une image de conteneur lors d'un déploiement.

Pourquoi aucune erreur de mémoire ?

Linux ne tue un processus que lorsqu'il manque vraiment de mémoire récupérable. Avant ce point, il récupère de la mémoire dans le cache de pages — les copies de fichiers qu'il garde en RAM, y compris le code de chaque programme en cours d'exécution.

Sur nos serveurs, les limites mémoire cumulées des conteneurs totalisaient environ 3,3 Go pour une machine disposant d'environ 1,8 Go utilisables. Normalement, elles n'atteignent pas toutes leur limite en même temps. Quand c'était le cas, le noyau évincait sans arrêt le code des programmes du cache pour faire de la place, puis devait le relire depuis le disque dès que ce code redevenait actif. Ce cycle — le thrashing — peut rendre une machine presque totalement insensible sans qu'elle manque techniquement de mémoire. Donc rien n'est tué, et rien ne se rétablit.

Sans swap, le noyau n'a nulle part où placer la mémoire allouée mais rarement utilisée, et doit donc la prendre dans le cache.

La correction

Le script d'initialisation des serveurs crée désormais un fichier de swap de 2 Go avec un réglage de swappiness faible (10) à chaque démarrage ou réparation d'un serveur. Une swappiness faible signifie que le noyau préfère toujours garder la mémoire de travail en RAM, mais que les pages rarement touchées peuvent passer sur le disque au lieu de forcer le code des programmes hors du cache. Nous l'avons appliqué à chaud sur les deux serveurs de production et sur la préproduction, et le pipeline de déploiement republie le script d'initialisation à chaque déploiement en production afin qu'un serveur remplacé le reçoive aussi.

Ce qui nous a le plus inquiétés : le serveur défaillant restait en place

Notre groupe de serveurs utilisait des contrôles de santé EC2 — « la machine tourne-t-elle ? » — et non des contrôles de santé du répartiteur de charge — « répond-elle aux requêtes ? ». Un serveur que le répartiteur avait abandonné était donc toujours considéré comme sain par le groupe. Il ne serait jamais remplacé. La production tournerait silencieusement sur un seul serveur jusqu'à ce que quelqu'un le remarque et redémarre l'autre.

Passer le groupe aux contrôles du répartiteur de charge semble être la solution évidente, mais ce n'est pas simple chez nous : pendant un déploiement, les serveurs répondent délibérément une erreur sur leur point de contrôle de santé pour que le répartiteur les vide (voir comment nous sommes arrivés à zéro erreur 502 lors des déploiements). Un groupe qui remplace les serveurs sur ce signal les remplacerait au milieu de chaque déploiement.

Nous avons donc ajouté pour l'instant une étape à notre liste de contrôle de déploiement : après chaque déploiement, vérifier la santé de chaque cible sur le répartiteur de charge, et non seulement si la tâche de déploiement a réussi. Si les commandes envoyées à un serveur restent en « Pending », il est en thrashing — le redémarrer ; il est déjà hors de la rotation.

Ce que nous en avons retenu

  • « Ne pas planter » ne veut pas dire « en bonne santé ». Le thrashing ne produit aucune erreur, seulement une lenteur si extrême que la machine cesse de répondre.
  • Les petits serveurs ont besoin de swap. Même un peu, avec une swappiness faible, donne au noyau un endroit où placer la mémoire froide.
  • Des limites mémoire qui cumulent plus que la machine sont un pari. Parfois on le perd. Sachez quand vous l'avez fait.
  • Sachez quel contrôle de santé votre autoscaling considère. Un contrôle qui demande seulement « la VM est-elle allumée ? » ne remplacera jamais un serveur qui a cessé de fonctionner.

Mythex · Pricing · Créez tout avec l'IA · Modèles de sites et d'applications · Mythex comparé · Glossaire de la création d'applications avec l'IA · Outils gratuits pour créer des applications avec l'IA · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)