Parece que ya ha probado muchas de las cosas que le habría sugerido al principio (ajustar la configuración de intercambio, cambiar los programadores de E/S, etc.).
Aparte de lo que ya ha intentado cambiar, le sugiero que busque cambiar los valores predeterminados algo inertes para el comportamiento de reescritura de VM. Esto es administrado por los siguientes seis valores sysctl:
vm.dirty_ratio
:controla la cantidad de escrituras que deben estar pendientes para la reescritura antes de que se active. Maneja la reescritura en primer plano (por proceso) y se expresa como un porcentaje entero de RAM. El valor predeterminado es el 10 % de RAMvm.dirty_background_ratio
:controla la cantidad de escrituras que deben estar pendientes para la reescritura antes de que se active. Maneja la reescritura en segundo plano (en todo el sistema) y se expresa como un porcentaje entero de RAM. El valor predeterminado es el 20 % de RAMvm.dirty_bytes
:Igual quevm.dirty_ratio
, excepto que se exprese como un número total de bytes. O esto ovm.dirty_ratio
se utilizará, cualquiera que haya sido escrito para durar.vm.dirty_background_bytes
:Igual quevm.dirty_background_ratio
, excepto que se exprese como un número total de bytes. O esto ovm.dirty_background_ratio
se utilizará, cualquiera que haya sido escrito para durar.vm.dirty_expire_centisecs
:Cuántas centésimas de segundo deben pasar antes de que comience la reescritura pendiente cuando los cuatro valores de sysctl anteriores aún no la activarían. El valor predeterminado es 100 (un segundo).vm.dirty_writeback_centisecs
:Con qué frecuencia (en centésimas de segundo) el kernel evaluará las páginas sucias para reescritura. El valor predeterminado es 10 (una décima de segundo).
Entonces, con los valores predeterminados, cada décima de segundo, el núcleo hará lo siguiente:
- Escriba las páginas modificadas en el almacenamiento persistente si se modificaron por última vez hace más de un segundo.
- Escribe todas las páginas modificadas de un proceso si la cantidad total de memoria modificada que no se ha escrito supera el 10 % de la RAM.
- Escribe todas las páginas modificadas del sistema si la cantidad total de memoria modificada que no se ha escrito supera el 20 % de la RAM.
Por lo tanto, debería ser bastante fácil ver por qué los valores predeterminados pueden causarle problemas, ya que su sistema podría estar intentando escribir hasta 4 gigabytes de datos al almacenamiento persistente cada décimo de un segundo.
El consenso general en estos días es ajustar vm.dirty_ratio
para ser el 1% de RAM, y vm.dirty_background_ratio
sea del 2 %, lo que para sistemas con menos de 64 GB de RAM da como resultado un comportamiento equivalente al que se pretendía originalmente.
Algunas otras cosas para investigar:
- Intenta aumentar el
vm.vfs_cache_pressure
sysctl un poco. Esto controla la agresividad con la que el kernel reclama memoria del caché del sistema de archivos cuando necesita RAM. El valor predeterminado es 100, no lo baje a menos de 50 (usted lo hará obtenga un comportamiento realmente malo si va por debajo de 50, incluidas las condiciones OOM), y no lo eleve mucho más de 200 (mucho más alto, y el kernel perderá el tiempo tratando de recuperar la memoria que realmente no puede). Descubrí que aumentarlo a 150 en realidad mejora visiblemente la capacidad de respuesta si tiene un almacenamiento razonablemente rápido. - Intente cambiar el modo de sobreasignación de memoria. Esto se puede hacer alterando el valor de
vm.overcommit_memory
sist. De forma predeterminada, el kernel utilizará un enfoque heurístico para intentar predecir cuánta RAM puede permitirse comprometer. Establecer esto en 1 desactiva la heurística y le dice al kernel que actúe como si tuviera memoria infinita. Establecer esto en 2 le dice al kernel que no comprometa más memoria que la cantidad total de espacio de intercambio en el sistema más un porcentaje de RAM real (controlado porvm.overcommit_ratio
). - Intenta ajustar el
vm.page-cluster
sist. Esto controla cuántas páginas se intercambian dentro o fuera a la vez (es un valor logarítmico de base 2, por lo que el valor predeterminado de 3 se traduce en 8 páginas). Si realmente está intercambiando, esto puede ayudar a mejorar el rendimiento del intercambio de páginas de entrada y salida.
¡Se ha encontrado el problema!
Resulta que es un problema de rendimiento en el recuperador de memoria de Linux cuando hay una gran cantidad de contenedores/grupos de memoria. (Descargo de responsabilidad:mi explicación puede ser defectuosa, no soy un desarrollador de kernel). El problema se solucionó en 4.19-rc1+ en este conjunto de parches:
Este conjunto de parches soluciona el problema de la lentitud de la reducción_slab() en las máquinas que tienen muchos reductores y grupos de memoria (es decir, con muchos contenedores). El problema es que la complejidad de shrink_slab() es O(n^2) y crece demasiado rápido con el crecimiento del número de contenedores.
Tengamos 200 contenedores, y cada contenedor tiene 10 montajes y 10cgroups. Todas las tareas de contenedor están aisladas y no tocan los montajes de contenedores extranjeros.
En caso de recuperación global, una tarea tiene que iterar en todos los memcgs y llamar a todos los reductores conscientes de memcg para todos ellos. Esto significa que la tarea tiene que visitar 200 * 10 =2000 reductores por cada memcg, y dado que hay 2000 memcg, el total de llamadas de do_shrink_slab() es 2000 * 2000 =4000000.
Mi sistema se vio particularmente afectado, ya que ejecuto una buena cantidad de contenedores, lo que probablemente fue lo que provocó que apareciera el problema.
Mis pasos de solución de problemas, en caso de que sean útiles para cualquier persona que enfrente problemas similares:
- Aviso
kswapd0
usando una tonelada de CPU cuando mi computadora tartamudea - Intente detener los contenedores de Docker y vuelva a llenar la memoria → ¡la computadora no tartamudea!
- Ejecutar
ftrace
(siguiendo la magnífica explicación del blog de Julia Evan) para obtener un seguimiento, ver quekswapd0
tiende a atascarse enshrink_slab
,super_cache_count
ylist_lru_count_one
. - Google
shrink_slab lru slow
, encuentra el conjunto de parches! - Cambie a Linux 4.19-rc3 y verifique que el problema esté solucionado.