El kernel Linux tiene un sistema mediante el cual cuando el sistema está a punto de quedarse sin memoria disponible empieza a matar procesos para evitar el colapso del sistema. Esta función la realiza el OOM killer (Out Of Memory Killer).
Es una buena medida para evitar que el sistema se cuelgue por falta de memoria, pero también es un problema serio cuando tenemos aplicaciones críticas en ejecución y el OOM killer tiene la posibilidad de matarlas en cualquier momento. Sabremos que un proceso ha sido parado por el OOM Killer cuando veamos en el syslog/messages el siguiente mensaje:
host kernel: Out of Memory: Killed process 11355 (usuario).
Los valores máximos y mínimos de memoria (a través del comando free o /proc/meminfo) nos son de utilidad para tener una visión a tiempo real del estado de máximos y mínimos de memoria en uso:
~# free -lm total used free shared buffers cached Mem: 2004 1750 254 0 84 848 Low: 853 614 239 High: 1150 1135 14 -/+ buffers/cache: 818 1186 Swap: 2035 0 2035
Lo bueno es que podemos configurar el OOM Killer para que actúe de una forma u otra según el proceso. Este control se aplica a nivel independiente de cada PID a través del filesystem /proc. La puntuación que asignemos al proceso indicará al OOM killer como actuar con él en caso de problemas. Básicamente, a mayor score mayor prioridad para ser matado en caso de problemas.
Esta puntuación se asigna, para kernels con versión superior a 2.6.29 manualmente a través de:
/proc/[PID]/oom_score_adj
El rango de puntuación en este caso va de -1000 a 1000, siendo «-1000» (OOM_SCORE_ADJ_MIN) el valor que indica que el proceso nunca deberá ser matado por el OOM Killer y «1000» (OOM_SCORE_ADJ_MAX) el que indica que será el primero a matar en caso de problemas.
Y para kernels con versión inferior a 2.6.29:
/proc/[PID]/oom_adj
En este caso, el rango de puntuación va de «-17», valor que indica que el proceso nunca deberá ser matado por el OOM Killer y «15″ el que indica que será el primero a matar en caso de problemas.
En ambos casos, esto sucederá cuando el sistema esté con un porcentaje muy bajo de memoria disponible.
Si por ejemplo, quisieramos evitar que OOM killer matara el proceso ssh podríamos hacerlo del siguiente modo:
~# PID=$(pgrep sshd); echo "-1000" > /proc/${PID}/oom_score_adj ~# cat /proc/${PID}/oom_score_adj -1000
Podríamos (o deberíamos) automatizarlo vía cron para que en caso de que el PID de SSH cambie o tras un reinicio la política se siguiera aplicando.
Además de por proceso, al ser una funcionalidad del Kernel también podemos definir a nivel global su comportamiento. Por ejemplo, los siguientes parámetros de sysctl definen que en caso de que se genere un OOM, el sistema genere un panic y se reinicie pasados 3 segundos:
sysctl -w vm.panic_on_oom=1 sysctl -w kernel.panic=3
Hay muchos parámetros de configuración y tuning para el OOM Killer, podéis verlos a continuación. Es importante el primero, «vm.overcommit_memory«, que define si vamos a permitir que el sistema o los procesos puedan reservar más memoria de la que realmente hay disponible en el sistema.
- 0: valor por defecto en el que se rechazan peticiones de memoria superiores a la que el sistema tiene disponible. Se puede utilizar el 100% de la memoria física.
- 1: se permiten peticiones de reserva de memoria superiores a la capacidad total.
- 2: no se permiten peticiones de reserva de memoria superiores a la capacidad total. En este modo, a diferencia de en el «0», podemos asignar manualmente la memoria a utilizar a partir de la swap + el overcomit_ratio
# sysctl -a | grep "vm\." vm.overcommit_memory = 0 vm.panic_on_oom = 0 vm.oom_kill_allocating_task = 0 vm.oom_dump_tasks = 1 vm.overcommit_ratio = 50 vm.page-cluster = 3 vm.dirty_background_ratio = 5 vm.dirty_background_bytes = 0 vm.dirty_ratio = 10 vm.dirty_bytes = 0 vm.dirty_writeback_centisecs = 500 vm.dirty_expire_centisecs = 3000 vm.nr_pdflush_threads = 0 vm.swappiness = 60 vm.nr_hugepages = 0 vm.hugetlb_shm_group = 0 vm.hugepages_treat_as_movable = 0 vm.nr_overcommit_hugepages = 0 vm.lowmem_reserve_ratio = 256 32 32 vm.drop_caches = 0 vm.extfrag_threshold = 500 vm.min_free_kbytes = 44800 vm.percpu_pagelist_fraction = 0 vm.max_map_count = 65530 vm.laptop_mode = 0 vm.block_dump = 0 vm.vfs_cache_pressure = 100 vm.legacy_va_layout = 0 vm.stat_interval = 1 vm.mmap_min_addr = 65536 vm.vdso_enabled = 1 vm.highmem_is_dirtyable = 0 vm.scan_unevictable_pages = 0 vm.memory_failure_early_kill = 0 vm.memory_failure_recovery = 1