Voy a explicar el Puntuación OOM y OOM Score Adjust como herramientas de control concretas en el funcionamiento del alojamiento web: permiten determinar qué procesos cierra el OOM Killer de Linux cuando hay escasez de memoria y cuáles protege. Así mantengo el control cuando RAM empieza a escasear y me aseguro de que los servicios esenciales sigan funcionando en línea.
Puntos centrales
Para facilitar la comprensión, voy a resumir brevemente las ideas más importantes.
- Prioridad En caso de escasez: el OOM Score evalúa qué proceso debe detenerse primero.
- Control fino con oom_score_adj: de -1000 (proteger) a +1000 (sacrificar).
- Dinámica En lugar de un valor fijo: la potencia nominal varía en función de la carga y la configuración.
- Prácticas de acogida: Proteger los servicios críticos y, en la medida de lo posible, cerrar los procesos no críticos.
- Causas Solucionar: comprobar los límites, los cgroups y la planificación de la RAM.
Cómo funciona el OOM-Killer de Linux
Cuando el nivel es alto presión de almacenamiento El núcleo de Linux decide qué procesos termina para mantener la capacidad de respuesta del sistema. Observo cómo el Núcleo asigna a cada proceso una especie de „nivel de gravedad“ que depende en gran medida del consumo actual de memoria. Si no hay suficiente RAM o espacio libre en el disco de intercambio, el OOM-Killer interviene y cierra el proceso con la puntuación más alta. Este mecanismo evita los bloqueos, pero no sustituye a una planificación adecuada de la capacidad a nivel de host y de servicio. Analizo la decisión en el registro OOM y determino si un servicio ha llamado la atención por un consumo excesivo de memoria o por una configuración errónea.
Entender la puntuación OOM: dinámica y escala
Compruebo el Puntuación OOM de un proceso en /proc/PID/oom_score y compruebo así cuál es su nivel de riesgo actual. La escala va, en la práctica, de 0 a 1000: cuanto más se acerque a 1000, más probable es que el proceso sea eliminado por el «killer». Este valor constituye una instantánea, ya que los picos de carga, los límites de los cgroups y los tamaños de la caché cambian constantemente. Por eso nunca evalúo la puntuación de forma aislada, sino en el contexto de la memoria RAM, el espacio de intercambio, el overcommit y los procesos paralelos. Quien observe la puntuación con regularidad detectará patrones típicos y podrá prever cuellos de botella antes de que dejen fuera de combate a los servicios.
Utilizar el OOM Score Adjust de forma específica
Con oom_score_adj Desplazo activamente la valoración de un proceso entre -1000 y +1000. Si establezco -1000, protejo el proceso por completo, mientras que los valores positivos altos lo hacen deliberadamente susceptible de ser sacrificado. Soy prudente a la hora de elegir, ya que un número excesivo de procesos protegidos limita el margen de maniobra del OOM-Killer. Los candidatos típicos para valores bajos son SSH, la monitorización, los front-ends de proxy inverso y los controladores de bases de datos sensibles. Las tareas en segundo plano, los reportes o los trabajadores de corta duración suelen recibir un ajuste más alto, para que la interfaz de usuario siga respondiendo cuando el sistema se sature.
Establecer prioridades en el alojamiento web
En entornos productivos, defino claramente Prioridades entre el frontend, la API, la base de datos y el procesamiento por lotes. Primero defino qué servicios deben mantenerse activos desde el punto de vista del usuario y les asigno un ajuste OOM adecuado. En systemd, para ello configuro OOMScoreAdjust= en el archivo de unidad del servicio y documento la finalidad de cada valor. Quien ya gestione los servicios a través de systemd puede optimizar los procesos; una guía de introducción al respecto es systemd en el alojamiento web. De este modo, me anticipo a las interrupciones en lugar de dejarlas al azar, y mantengo la experiencia del usuario en línea de forma fiable.
Cgroups, contenedores y límites
Nunca olvidaré la cgroups, ya que los contenedores y los servicios operan en entornos de recursos propios. Un proceso con una puntuación OOM moderada puede, aun así, bloquearse si su cgroup tiene un límite de memoria muy ajustado y lo supera momentáneamente. Por eso compruebo los límites en cgroup v2 y configuro límites estrictos y flexibles en función de los perfiles de carga. Quienes gestionan entornos multitenencia o alojamiento compartido se benefician de unas cuotas y una contabilidad bien definidas; para más información, consulta cgroup v2 en el alojamiento web. Si la interacción es la adecuada, el ajuste de OOM y los límites actúan como un par de tornillos de regulación bien sincronizados.
Diagnóstico y supervisión en caso de eventos OOM
Cuando hay acción, necesito instrucciones claras Señales y la repetibilidad en el análisis. Analizo dmesg, journald y /var/log/kern.log, guardo las líneas OOM y recojo el PID de la víctima junto con oom_score y oom_score_adj. Para las comprobaciones rutinarias utilizo scripts que enumeran los mayores consumidores de memoria y activan umbrales de alerta. Quien desee profundizar más, encontrará un enfoque estructurado en el Análisis del OOM-Killer. En las configuraciones permanentes, incluyo métricas como RSS, caché, swap-in/out y límites de contenedores en la supervisión, para poder detectar las tendencias a tiempo.
Guía rápida en forma de tabla para administradores
Utilizo el siguiente resumen como un resumen conciso Guía, cuando priorizo los roles y documento los ajustes. La columna „Justificación“ muestra por qué un servicio recibe protección o se le asigna un nivel de sacrificio. Adapto las cifras al proyecto, pero la orientación me ayuda a tomar decisiones rápidas. Quien utilice la tabla como punto de partida ganará en claridad en los análisis a posteriori y en las solicitudes de cambio. Lo importante es que siempre mantengo un margen de seguridad en el sistema global, para que rara vez sea necesario eliminar elementos por completo.
| Componente | Destino típico | Ejemplo: oom_score_adj | Razón |
|---|---|---|---|
| SSH-Daemon | Tiradores | de -500 a -900 | Garantizar el acceso para las intervenciones, incluso en situaciones de congestión. |
| Proxy inverso (nginx/HAProxy) | Tiradores | de -300 a -700 | Gestionar el tráfico entrante, mostrar páginas de error. |
| DB-Controlador/Instancia principal | Tiradores | de -200 a -600 | Mantener las conexiones y garantizar el acceso a los datos. |
| PHP-FPM/Workers de aplicación | De neutral a dispuesto a sacrificarse | De 0 a +300 | Se pueden eliminar muchos trabajadores en paralelo. |
| Tareas por lotes/Copias de seguridad/Informes | Dispuesto a hacer sacrificios | De +300 a +800 | Se puede posponer sin que ello afecte a los usuarios. |
| Indexador/Consumidor de colas | Dispuesto a hacer sacrificios | De +200 a +600 | Se puede pausar brevemente y recuperarse más tarde. |
Cómo limitar correctamente los trabajadores de WordPress y PHP
En WordPress, presto atención a Trabajador-Número, memory_limit y operaciones pesadas como el procesamiento de imágenes o las importaciones. Configuro PHP-FPM de manera que el número de procesos activos se adapte a la RAM y no provoque sobrecargas. En la base de datos, sumo los tamaños de los búferes y la caché, y dejo un margen para que los picos de carga no lo bloqueen todo. Superviso OpCache, la caché de objetos y el optimizador de imágenes, ya que pueden disparar rápidamente el consumo de memoria. De este modo, me aseguro de que los picos de carga breves no afecten inmediatamente a los procesos importantes del front-end.
Práctica: políticas y guías de actuación
Mantengo mi Políticas Conciso y fácil de aplicar, para que el equipo no dude en caso de emergencia. Esto incluye: definir los candidatos a protección, designar los roles de víctimas, configurar las unidades de systemd con OOMScoreAdjust= y documentar los valores en el repositorio. Compruebo el efecto con herramientas y cargas de prueba hasta que el orden de los componentes sacrificables se ajusta a los objetivos. A continuación, redacto un manual de procedimientos que describe los registros, las alertas y las medidas iniciales. De este modo, la reacción sigue siendo coherente, incluso cuando se incorporan nuevos compañeros y compañeras.
# Fragmento de ejemplo para una unidad de systemd
[Service]
OOMScoreAdjust=-400
# Recarga y reinicio:
# systemctl daemon-reload && systemctl restart nginx
# Comprobación en tiempo real:
cat /proc/$(pidof nginx)/oom_score
cat /proc/$(pidof nginx)/oom_score_adj
# Aumentar o reducir temporalmente (root):
echo 300 | sudo tee /proc//oom_score_adj
Errores frecuentes y medidas correctivas
Muchos problemas surgen porque Límites No encajan: demasiados trabajadores PHP, cachés de base de datos demasiado grandes y sin margen para picos de carga. Entonces, el OOM-Killer interviene con frecuencia, aunque bastaría con ajustar unos pocos parámetros. Primero corrijo el número de trabajadores, mido el efecto y solo aumento la RAM cuando la necesidad es evidente. Además, establecer muchos procesos en -1000 es perjudicial, ya que el núcleo necesita libertad de acción. Establezco las prioridades con sensatez, para que el sistema pueda reaccionar de forma ordenada en caso de emergencia.
Overcommit, swap y niveles de memoria
Voy a presentar mi Estrategia de sobreasignación Se configura deliberadamente, ya que determina la rapidez con la que un sistema entra en la zona OOM. Con vm.overcommit_memory=0 (heurística) suelo obtener un funcionamiento estable, ya que el núcleo estima el límite de overcommit basándose en el uso y el historial. La configuración se vuelve más estricta con vm.overcommit_memory=2 más vm.overcommit_ratio, que define la ocupación virtual máxima permitida. Quien establezca de forma general vm.overcommit_memory=1 corre el riesgo de que las reservas de memoria se realicen con éxito y luego fracasen estrepitosamente al asignarlas, lo que suele ser un caldo de cultivo para los eventos OOM bajo carga.
Calibro Intercambiar de tal forma que proporcione un margen de seguridad, pero sin convertirse en un factor que aumente la latencia. Un valor moderado de vm.swappiness mantiene libre la memoria RAM para las rutas más activas, mientras que las páginas que se utilizan con poca frecuencia se trasladan a la memoria de intercambio. Puedo utilizar zswap o zram como colchón elástico cuando la E/S es lenta; esto reduce el riesgo de OOM, pero consume recursos de la CPU. También son importantes los niveles de memoria: vm.min_free_kbytes debe ser lo suficientemente alto para que el núcleo pueda recuperar memoria a tiempo. Quien lo calcule con demasiado margen, someterá al sistema a una recuperación frenética y provocará comportamientos anómalos que desembocarán en un OOM.
# Ejemplo: overcommit conservador y swapping moderado
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=90
sysctl -w vm.swappiness=30
# Para realizar pruebas, introdúcelo de forma permanente en /etc/sysctl.d/
Opciones de systemd más allá de OOMScoreAdjust
Además de OOMScoreAdjust, utilizo systemd para Barandillas de contención aplicar directamente al servicio. Con MemoryMax= establezco un límite estricto (cgroup memory.max), MemoryHigh= frena suavemente bajo carga y MemorySwapMax= limita las operaciones de paginación. MemoryLow= y MemoryMin= dan prioridad a las partes de caché de un servicio cuando hay presión, de modo que los procesos importantes se enfríen más lentamente. Junto con OOMPolicy=, controlo lo que hace systemd en caso de OOM a nivel de unidad (por ejemplo, solo detener el servicio o terminar todas las dependencias). En «slices» agrupo roles —front-end web, batch, base de datos— y establezco reglas uniformes para que los casos aislados no desestabilicen el conjunto.
Tengo en cuenta que la protección nunca es absoluta: incluso los procesos con -1000 pueden verse obligados a ceder en situaciones sin salida. Por eso establezco valores generosos, pero realistas Mínimos (MemoryLow/Min) solo para muy pocos servicios básicos y compruebo que la suma de todas las asignaciones se mantenga por debajo de la memoria físicamente disponible. De este modo, evito que los mecanismos de protección, aunque bienintencionados, ceguen al OOM-Killer.
Kubernetes y la orquestación de contenedores
En entornos de orquestación como Kubernetes, la lógica OOM se aplica en varios niveles. Yo utilizo Solicitudes y Límites de modo que los pods se clasifiquen en la clase de QoS deseada: «Guaranteed» ofrece la mayor protección, «Burstable» actúa como amortiguador y «BestEffort» es el que más se ve afectado. El kubelet asigna automáticamente los valores de OOMScoreAdjust resultantes; por lo tanto, planifico mediante especificaciones de recursos en lugar de valores de ajuste manuales en los contenedores. Si un contenedor alcanza su «memory.limit», se cierra dentro de su cgroup incluso si el host todavía dispone de memoria libre; no se trata de un OOM clásico del host, sino de una autodefensa específica del límite.
Tengo en cuenta porcentajes de memoria nativa fuera de las configuraciones del heap (por ejemplo, en JVM/Nodo), para que los contenedores no se bloqueen inesperadamente al alcanzar el límite. Además, calculo los márgenes de los pods para hacer frente a los picos de carga y planifico el overcommit de los nodos solo de forma moderada, para que las expulsiones sean poco frecuentes. Si cgroup v2 está activo, utilizo memory.oom.group de forma selectiva para que, en caso de emergencia, todo un grupo de procesos se cierre de forma ordenada, en lugar de dejar trabajadores individuales en un pod «zombi». Esto mantiene el sistema limpio y hace que la recuperación sea predecible.
Profundidad del diagnóstico: SMaps, PSI y pruebas reproducibles
Para realizar análisis en profundidad, recurro a /proc-Información y métricas de presión. /proc/PID/status muestra VmRSS, VmSwap y subprocesos; /proc/PID/smaps_rollup resume proporciones como Anon, File y Shmem, sin que me pierda en los detalles. Así puedo detectar si la caché de páginas es engañosa o si las páginas anónimas (conjunto de datos de trabajo real) están aumentando. Con /proc/pressure/memory mido PSI-Señales que indican cuánto tiempo el sistema se ve afectado por la recuperación activa o los bloqueos. Estos valores me alertan mucho antes de que se produzca un error OOM, lo que resulta ideal para activar automáticamente medidas correctivas (limitación de rendimiento, escalado, reducción de trabajadores).
# Instantáneas relevantes
journalctl -k -g "Out of memory|oom-killer"
cat /proc/pressure/memory
grep -E "VmRSS|VmSwap|Threads" /proc//status
cat /proc//smaps_rollup
# Reproducir el error OOM (¡entorno de prueba!)
stress-ng --vm 2 --vm-bytes 80% --timeout 30s
Casos especiales: JVM, Node.js y PHP en contenedores
JVMLos servicios requieren atención, ya que, además del heap, también hay que tener en cuenta el metaspacio, las pilas de subprocesos, los búferes directos y el comportamiento del asignador nativo. Realizo una gestión orientada a los contenedores mediante MaxRAMPercentage y configuro un montón que deje margen para estas partes. En caso de alto paralelismo, limito los grupos de subprocesos, ya que muchas pilas pequeñas se acumulan de forma problemática. Para Nodo.js ajusto el parámetro –max-old-space-size al límite del contenedor para evitar cierres forzosos. Y en PHP-FPM Calculo pm.max_children a partir de la RAM, el consumo medio por solicitud y el memory_limit, más una reserva para las cachés y el servidor web. De este modo evito que se produzcan avalanchas silenciosas que solo se hacen evidentes en los picos de tráfico.
Guardo el Estrategia de asignación A tener en cuenta: glibc con muchas áreas puede fragmentar la memoria y aumentar su consumo en cargas de trabajo con muchos subprocesos. Para determinados servicios, jemalloc o tcmalloc ofrecen picos más consistentes; lo estoy probando de forma específica, documentando el efecto e implementándolo de forma controlada. Además, limito los directorios tmpfs en el contenedor para que las subidas o los archivos temporales no consuman la RAM sin que nos demos cuenta.
Tmpfs, páginas gigantes y caché de páginas
tmpfs Es algo que se suele pasar por alto: al no tener límite de tamaño, crece hasta ocupar una parte de la RAM y, de repente, falta espacio en otros lugares. Yo monto los tmpfs especificando deliberadamente el parámetro «size=», sobre todo en las rutas de compilación o de subida. Páginas transparentes enormes (THP) La fragmentación y la latencia influyen en el rendimiento; para los servicios en los que la latencia es crítica, suelo utilizar „madvise“ para que solo se beneficien las asignaciones adecuadas. KSM puede deduplicar y ahorrar memoria, pero consume recursos de la CPU; resulta útil en los servidores de desarrollo, aunque en las rutas de rendimiento compruebo su efecto y la sobrecarga.
El Caché de páginas No se trata de memoria „desperdiciada“; acelera las operaciones de E/S. Si la libero de forma demasiado agresiva o utilizo las cachés de descarte como medida permanente, estoy trasladando los costes a picos de latencia. Es mejor definir objetivos de memoria por rol e imponer una distribución equitativa mediante los mecanismos de cgroup (memory.high / memory.max). De este modo, los conjuntos activos de los servicios importantes permanecen en la RAM y las situaciones de OOM son menos frecuentes.
Resumen para la vida cotidiana
Yo utilizo el Puntuación OOM como indicador del riesgo y, mediante oom_score_adj, ajusto el orden adecuado de las sacrificias. Protejo los servicios que afectan a los usuarios, preparo para ser sacrificados los trabajos que se pueden posponer y documento cada valor de forma que se pueda rastrear. Planifico los límites de cgroup, el número de trabajadores y los tamaños de caché de forma conjunta, para que los picos de actividad no se conviertan en un problema generalizado. Los registros, la supervisión y un breve manual de procedimientos me permiten detectar rápidamente los incidentes de OOM y solucionarlos de forma específica. Con esta disciplina, el servidor se mantiene fiable y evito sorpresas desagradables durante las noches de producción.


