Cómo Configurar el Out-of-Memory Killer en Linux

Escrito el 2023-06-06 por Alberto Ferrer
Tiempo de lectura: 10 minuto(s)
Etiquetas: linux oom vmstat

Cuando un servidor que soporta una base de datos o un servidor de aplicaciones se cae, a menudo es una carrera para conseguir que los servicios críticos vuelvan a estar operativos, especialmente si se trata de un sistema de producción importante. Al intentar determinar la causa raíz después del triaje inicial, a menudo es un misterio por qué la aplicación o la base de datos dejaron de funcionar de repente. En ciertas situaciones, la causa raíz del problema puede rastrearse hasta el sistema que se quedó sin memoria y tuvo que matar un proceso importante para seguir operativo.

El kernel de Linux asigna memoria a demanda de las aplicaciones que se ejecutan en el sistema. Debido a que muchas aplicaciones asignan su memoria de antemano y a menudo no utilizan la memoria asignada, el kernel fue diseñado con la capacidad de comprometer más memoria de la que realmente tiene físicamente disponible. Este modelo de sobrecompromiso permite al kernel asignar más memoria de la que realmente tiene disponible físicamente. Si un proceso realmente utiliza la memoria que se le asignó, el kernel entonces proporciona estos recursos a la aplicación.

Cuando demasiadas aplicaciones comienzan a utilizar la memoria que se les asignó, el modelo de sobrecompromiso a veces se convierte en un problema y el kernel debe comenzar a matar procesos para seguir operativo. El mecanismo que el kernel utiliza para recuperar memoria en el sistema se conoce como el asesino de memoria insuficiente o asesino OOM por sus siglas en inglés.

Descubriendo Por Qué un Proceso Fue Terminado

Al solucionar un problema en el que una aplicación ha sido terminada por el OOM killer, hay varias pistas que podrían arrojar luz sobre cómo y por qué el proceso fue terminado. En el siguiente ejemplo, vamos a echar un vistazo a nuestro syslog para ver si podemos localizar la fuente de nuestro problema. El proceso nginx fue terminado por el OOM killer debido a una condición de falta de memoria. La K mayúscula en Killed (Terminado) indica que el proceso fue terminado con una señal -9, y esto suele ser una buena señal de que el OOM killer podría ser el culpable.

grep -i kill /var/log/messages*
host kernel: Out of Memory: Killed process 592 (nginx).

También podemos examinar el estado del uso de memoria baja y alta en un sistema. Es importante tener en cuenta que estos valores son en tiempo real y cambian dependiendo de la carga de trabajo del sistema; por lo tanto, estos deben ser vigilados frecuentemente antes de que ocurra la presión de memoria. Mirar estos valores después de que un proceso fue terminado no será muy revelador y, por lo tanto, realmente no puede ayudar en la investigación de problemas de OOM.

[root@box ~]# free -lm
             total       used       free     shared    buffers     cached
Mem:           498         93        405          0         15         32
Low:           498         93        405
High:            0          0          0
-/+ buffers/cache:         44        453
Swap:         1023          0       1023

En esta máquina virtual de prueba, tenemos 498 MB de memoria baja libre. El sistema no tiene uso de swap. El interruptor -l muestra las estadísticas de memoria alta y baja, y el interruptor -m coloca la salida en megabytes para facilitar su lectura.

[root@box ~]# egrep 'High|Low' /proc/meminfo
HighTotal:             0 kB
HighFree:              0 kB
LowTotal:         510444 kB
LowFree:          414768 kB

Los mismos datos se pueden obtener examinando /proc/memory y observando específicamente los valores altos y bajos. Sin embargo, con este método, no obtenemos información de intercambio del resultado y la salida está en kilobytes.

La memoria baja es la memoria a la que el kernel tiene acceso físico directo. La memoria alta es la memoria a la que el kernel no tiene una dirección física directa y, por lo tanto, debe mapearse a través de una dirección virtual. En sistemas de 32 bits más antiguos, verás memoria baja y memoria alta debido a la forma en que la memoria se mapea a una dirección virtual. En plataformas de 64 bits, no se necesita espacio de dirección virtual y toda la memoria del sistema se mostrará como memoria baja.

Si bien mirar /proc/memory y usar el comando free son útiles para saber "ahora mismo" cuál es nuestro uso de memoria, hay ocasiones en las que queremos ver el uso de memoria durante un período de tiempo más largo. El comando vmstat es bastante útil para esto.

En el ejemplo en la Lista 1, estamos utilizando el comando vmstat para observar nuestros recursos cada 45 segundos 10 veces. El interruptor -S muestra nuestros datos en una tabla y el interruptor -M muestra la salida en megabytes para facilitar su lectura.

Como puedes ver, algo está consumiendo nuestra memoria libre, pero aún no estamos intercambiando en este ejemplo:

[root@localhost ~]# vmstat -SM 45 10
procs -----------memory-------- ---swap-- -----io-- --system-- ----cpu---------
 r  b   swpd  free  buff  cache  si   so   bi   bo   in    cs us  sy  id  wa st
 1  0      0   221   125     42   0    0    0    0   70     4  0   0  100  0  0
 2  0      0   192   133     43   0    0  192   78  432  1809  1  15   81   2 0
 2  1      0    85   161     43   0    0  624  418 1456  8407  7  73    0  21 0
 0  0      0    65   168     43   0    0  158  237  648  5655  3  28   65   4 0
 3  0      0    64   168     43   0    0    0    2 1115 13178  9  69   22   0 0
 7  0      0    60   168     43   0    0    0    5 1319 15509 13  87    0   0 0
 4  0      0    60   168     43   0    0    0    1 1387 15613 14  86    0   0 0
 7  0      0    61   168     43   0    0    0    0 1375 15574 14  86    0   0 0
 2  0      0    64   168     43   0    0    0    0 1355 15722 13  87    0   0 0
 0  0      0    71   168     43   0    0    0    6  215  1895  1   8   91   0 0

La salida de vmstat se puede redirigir a un archivo usando el siguiente comando. Incluso podemos ajustar la duración y el número de veces para monitorear durante más tiempo. Mientras el comando se está ejecutando, podemos mirar el archivo de salida en cualquier momento para ver los resultados.

En el siguiente ejemplo, estamos observando la memoria cada 120 segundos 1000 veces. El & al final de la línea nos permite ejecutar esto como un proceso y recuperar nuestra terminal.

[root@localhost ~]# vmstat -SM 120 1000 > memoryusage.out &

Para referencia, la Lista 2 muestra una sección de la página del manual de vmstat que proporciona información adicional sobre la salida que proporciona el comando. Esta es solo la información relacionada con la memoria; el comando también proporciona información sobre el disco I/O y el uso de la CPU.

Memory
       swpd: the amount of virtual memory used.
       free: the amount of idle memory.
       buff: the amount of memory used as buffers.
       cache: the amount of memory used as cache.
       inact: the amount of inactive memory. (-a option)
       active: the amount of active memory. (-a option)

   Swap
       si: Amount of memory swapped in from disk (/s).
       so: Amount of memory swapped to disk (/s).

Existen numerosas otras herramientas disponibles para monitorear la memoria y el rendimiento del sistema para investigar problemas de esta naturaleza. Herramientas como sar (Reportero de Actividad del Sistema) y dtrace (Rastreo Dinámico) son bastante útiles para recopilar datos específicos sobre el rendimiento del sistema a lo largo del tiempo. Para tener aún más visibilidad, las sondas de estabilidad de dtrace y de estabilidad de datos incluso tienen un disparador para condiciones de OOM que se activará si el kernel mata un proceso debido a una condición de OOM.

Hay varias cosas que podrían causar un evento de OOM aparte de que el sistema se quede sin RAM y espacio de intercambio disponible debido a la carga de trabajo. Es posible que el kernel no pueda utilizar el espacio de intercambio de forma óptima debido al tipo de carga de trabajo en el sistema. Las aplicaciones que utilizan mlock() o HugePages tienen memoria que no se puede intercambiar al disco cuando el sistema comienza a quedarse sin memoria física. Las estructuras de datos del kernel también pueden ocupar demasiado espacio agotando la memoria en el sistema y causando una situación de OOM.

Muchos sistemas basados en arquitectura NUMA pueden experimentar condiciones de OOM debido a que un nodo se queda sin memoria desencadenando un OOM en el kernel mientras queda mucha memoria en los nodos restantes. Si deseas conocer más sobre cómo funcionan los OOM en Linux, te recomendamos este artículo que profundiza en el tema. Haz clic aquí para leer el artículo