Il semble que vous ayez déjà essayé bon nombre des choses que j'aurais suggérées au départ (modifier la configuration d'échange, modifier les planificateurs d'E/S, etc.).
Outre ce que vous avez déjà essayé de changer, je suggérerais d'envisager de modifier les valeurs par défaut quelque peu mortelles pour le comportement d'écriture différée de la machine virtuelle. Ceci est géré par les six valeurs sysctl suivantes :
vm.dirty_ratio
:contrôle le nombre d'écritures en attente pour la réécriture avant qu'elle ne soit déclenchée. Gère l'écriture différée de premier plan (par processus) et est exprimée sous la forme d'un pourcentage entier de RAM. Par défaut, 10 % de RAMvm.dirty_background_ratio
:contrôle le nombre d'écritures en attente pour la réécriture avant qu'elle ne soit déclenchée. Gère la réécriture en arrière-plan (à l'échelle du système) et est exprimée sous la forme d'un pourcentage entier de RAM. Par défaut, 20 % de RAMvm.dirty_bytes
:Identique àvm.dirty_ratio
, sauf exprimé sous la forme d'un nombre total d'octets. Soit ceci ouvm.dirty_ratio
sera utilisé, celui qui a été écrit pour durer.vm.dirty_background_bytes
:Identique àvm.dirty_background_ratio
, sauf exprimé sous la forme d'un nombre total d'octets. Soit ceci ouvm.dirty_background_ratio
sera utilisé, celui qui a été écrit pour durer.vm.dirty_expire_centisecs
:Combien de centièmes de seconde doivent s'écouler avant que l'écriture différée en attente ne commence lorsque les quatre valeurs sysctl ci-dessus ne le déclencheraient pas déjà. La valeur par défaut est 100 (une seconde).vm.dirty_writeback_centisecs
:Combien de fois (en centièmes de seconde) le noyau évaluera les pages modifiées pour l'écriture différée. La valeur par défaut est 10 (un dixième de seconde).
Ainsi, avec les valeurs par défaut, tous les dixièmes de seconde, le noyau fera ce qui suit :
- Écrivez toutes les pages modifiées dans le stockage persistant si elles ont été modifiées pour la dernière fois il y a plus d'une seconde.
- Écrivez toutes les pages modifiées d'un processus si la quantité totale de mémoire modifiée qui n'a pas été écrite dépasse 10 % de la RAM.
- Écrivez toutes les pages modifiées dans le système si la quantité totale de mémoire modifiée qui n'a pas été écrite dépasse 20 % de la RAM.
Ainsi, il devrait être assez facile de voir pourquoi les valeurs par défaut peuvent vous causer des problèmes, car votre système peut essayer d'écrire jusqu'à 4 gigaoctets de données vers un stockage persistant tous les dixièmes d'une seconde.
Le consensus général ces jours-ci est d'ajuster vm.dirty_ratio
à 1 % de la RAM, et vm.dirty_background_ratio
à 2 %, ce qui, pour les systèmes disposant de moins de 64 Go de RAM environ, se traduit par un comportement équivalent à ce qui était prévu à l'origine.
Quelques autres choses à examiner :
- Essayez d'augmenter le
vm.vfs_cache_pressure
sysctl un peu. Cela contrôle l'agressivité avec laquelle le noyau récupère la mémoire du cache du système de fichiers lorsqu'il a besoin de RAM. La valeur par défaut est 100, ne la réduisez pas en dessous de 50 (vous allez obtenez un très mauvais comportement si vous descendez en dessous de 50, y compris les conditions OOM), et ne l'augmentez pas à beaucoup plus d'environ 200 (beaucoup plus élevé, et le noyau perdra du temps à essayer de récupérer de la mémoire qu'il ne peut vraiment pas). J'ai constaté que le fait de le faire monter jusqu'à 150 améliore visiblement la réactivité si vous disposez d'un stockage raisonnablement rapide. - Essayez de changer le mode de surcharge de la mémoire. Cela peut être fait en modifiant la valeur du
vm.overcommit_memory
sysctl. Par défaut, le noyau utilisera une approche heuristique pour essayer de prédire la quantité de RAM qu'il peut réellement se permettre de valider. Définir ceci sur 1 désactive l'heuristique et indique au noyau d'agir comme s'il avait une mémoire infinie. Le régler sur 2 indique au noyau de ne pas s'engager sur plus de mémoire que la quantité totale d'espace d'échange sur le système plus un pourcentage de RAM réelle (contrôlé parvm.overcommit_ratio
). - Essayez de modifier le
vm.page-cluster
sysctl. Cela contrôle le nombre de pages échangées à la fois (il s'agit d'une valeur logarithmique de base 2, donc la valeur par défaut de 3 se traduit par 8 pages). Si vous échangez réellement, cela peut aider à améliorer les performances d'échange de pages entrantes et sortantes.
Le problème a été trouvé !
Il s'avère qu'il s'agit d'un problème de performances dans le récupérateur de mémoire de Linux lorsqu'il existe un grand nombre de conteneurs/groupes de contrôle de mémoire. (Avis de non-responsabilité :mon explication est peut-être erronée, je ne suis pas un développeur du noyau.) Le problème a été résolu dans la version 4.19-rc1+ dans cet ensemble de correctifs :
Ce patchset résout le problème de la lenteur de la fonction shrink_slab() sur les machines ayant de nombreux rétrécisseurs et groupes de contrôle de mémoire (c'est-à-dire avec de nombreux conteneurs). Le problème est que la complexité de shrink_slab() est O(n^2) et qu'elle croît trop vite avec la croissance du nombre de conteneurs.
Prenons 200 conteneurs, et chaque conteneur contient 10 montures et 10 groupes. Toutes les tâches de conteneur sont isolées et ne touchent pas les montages de conteneurs étrangers.
En cas de récupération globale, une tâche doit itérer sur tous les memcgs et appeler tous les rétrécissements compatibles avec les memcgs pour chacun d'eux. Cela signifie que la tâche doit visiter 200 * 10 =2 000 rétrécissements pour chaque memcg, et puisqu'il y a 2 000 memcgs, le nombre total d'appels de do_shrink_slab() est de 2 000 * 2 000 =4 000 000.
Mon système a été particulièrement touché, car j'exécute un bon nombre de conteneurs, ce qui est probablement à l'origine du problème.
Mes étapes de dépannage, au cas où elles seraient utiles à toute personne confrontée à des problèmes similaires :
- Avis
kswapd0
utiliser une tonne de CPU quand mon ordinateur bégaie - Essayez d'arrêter les conteneurs Docker et de remplir à nouveau la mémoire → l'ordinateur ne bégaie pas !
- Exécutez
ftrace
(suite au magnifique blog d'explications de Julia Evan) pour avoir une trace, voyez cekswapd0
a tendance à rester bloqué enshrink_slab
,super_cache_count
, etlist_lru_count_one
. - Google
shrink_slab lru slow
, trouvez le patchset ! - Passez à Linux 4.19-rc3 et vérifiez que le problème est résolu.