Tutoriel Linux

Cgroups Linux, verificar los límites de CPU y memoria de un servicio

Débutant6 min de lecture

Un servicio web que se ralentiza, un worker eliminado sin un mensaje claro o un servidor que intercambia no necesariamente ha agotado toda la RAM de la máquina. Con systemd, un límite impuesto en una unidad puede ser suficiente para explicar el síntoma.

Los cgroups permiten al núcleo de Linux contar, limitar y priorizar los recursos de un grupo de procesos. Antes de agregar memoria o reiniciar al azar, verifique lo que systemd aplica al servicio en cuestión.

Tux frente a servidores con un medidor de recursos de CPU y memoria
Cgroups y límites de recursos de un servicio Linux gestionado por systemd.

Ver el cgroup de un servicio

En las distribuciones recientes, systemd coloca cada servicio en su propio cgroup. No necesita mover PID manualmente para diagnosticar una unidad clásica como Nginx, PostgreSQL o un worker de aplicación.

Comience por leer el estado del servicio y las propiedades relacionadas con los recursos. Reemplace nginx.service por el nombre real de su unidad.

systemctl status nginx.service --no-pager
systemctl show nginx.service -p ControlGroup -p MainPID -p MemoryCurrent -p MemoryMax -p MemoryHigh -p CPUQuotaPerSecUSec

ControlGroup proporciona la ruta gestionada por systemd. MemoryCurrent muestra la memoria contabilizada para la unidad en el momento de la lectura. Si MemoryMax es infinity, systemd no ha fijado un techo de memoria estricto en este servicio.

Si comienza desde un PID, vuelva a leer su cgroup desde /proc. En cgroup v2, a menudo verá una línea del tipo 0::/system.slice/nginx.service.

pid=$(systemctl show nginx.service -p MainPID --value)
cat /proc/$pid/cgroup

Si MainPID es 0, el servicio no tiene un proceso principal activo. Regrese a systemctl status y journalctl en lugar de concluir a partir de un PID vacío.

Leer los límites de memoria y CPU

MemoryMax es un límite estricto. Si el cgroup alcanza este techo y el núcleo no recupera suficiente memoria, un proceso del grupo puede ser eliminado por el OOM killer.

MemoryHigh es menos drástico. El núcleo frena las asignaciones y trata de recuperar memoria cuando el cgroup supera este umbral. Para contener un servicio sin hacerlo caer en el primer pico, a menudo es un mejor ajuste inicial que MemoryMax.

CPUQuota limita el tiempo de CPU disponible para el servicio. CPUQuota=25% da el equivalente a un cuarto de núcleo lógico. En una máquina con múltiples núcleos, CPUQuota=200% permite hasta dos núcleos de tiempo de CPU.

systemctl show nginx.service 
  -p MemoryCurrent -p MemoryMax -p MemoryHigh 
  -p CPUUsageNSec -p CPUQuotaPerSecUSec

No mezcle cuota y prioridad. Una cuota impide que el servicio supere una cantidad de CPU. Una prioridad, con nice o CPUWeight, principalmente arbitra la competencia cuando varias tareas solicitan CPU al mismo tiempo.

Encontrar el origen de un límite

Un límite puede venir del archivo proporcionado por el paquete, de un drop-in de systemd o de un override creado localmente. Lea la configuración completa antes de modificar el servicio.

systemctl cat nginx.service
systemctl show nginx.service -p FragmentPath -p DropInPaths

Un archivo bajo /etc/systemd/system/nginx.service.d/ normalmente sobrescribe la unidad proporcionada por el paquete. Evite modificar directamente un archivo en /usr/lib/systemd/system/ o /lib/systemd/system/, ya que una actualización puede sobrescribirlo.

A continuación, verifique los síntomas. Un servicio lento también puede provenir de una consulta SQL lenta, un disco saturado, una cola llena o un error de aplicación.

journalctl -u nginx.service -b --no-pager
journalctl -k -b | grep -Ei 'oom|out of memory|killed process|memory cgroup'

Si los límites permanecen en infinity y el diario del núcleo no muestra ningún evento de memoria, busque en otro lado antes de tocar los cgroups: registros de aplicaciones, métricas de disco, latencia de red o consumo real del proceso.

Probar sin tocar el servicio

Para validar una sintaxis o medir el efecto de un límite, inicie una unidad temporal con systemd-run. La prueba no modifica Nginx, PostgreSQL o su servicio de negocio.

sudo systemd-run --unit=prueba-limite-memoria 
  --property=MemoryMax=256M 
  --property=CPUQuota=25% 
  /usr/bin/bash -c 'sleep 300'

A continuación, revise las propiedades aplicadas. Para CPUQuota=25%, systemd puede mostrar un valor equivalente a 250ms por segundo.

systemctl show prueba-limite-memoria.service 
  -p MemoryMax -p CPUQuotaPerSecUSec -p ControlGroup -p ActiveState
systemctl cat prueba-limite-memoria.service

Detenga la unidad de prueba tan pronto como haya terminado. Con sleep, el olvido no es grave, pero este reflejo evita sorpresas cuando la prueba inicia un comando real.

sudo systemctl stop prueba-limite-memoria.service

Aplicar un override de systemd

Cuando los registros y las medidas confirman que un límite tiene sentido, proceda con un override de systemd. El siguiente ejemplo establece un umbral alto de 800 MB, un techo duro de 1 GB y limita el servicio al equivalente de un núcleo de CPU.

sudo systemctl edit mi-servicio.service

Agregue solo las directivas necesarias. No copie estos valores en un servicio de producción sin haber observado su consumo normal y sus picos.

[Service]
MemoryHigh=800M
MemoryMax=1G
CPUQuota=100%

Recargue systemd, y luego reinicie el servicio solo si la interrupción es aceptable. A distancia, mantenga una segunda sesión SSH abierta cuando toque un componente crítico.

sudo systemctl daemon-reload
sudo systemctl restart mi-servicio.service
systemctl status mi-servicio.service --no-pager
systemctl show mi-servicio.service -p MemoryCurrent -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec

Si el servicio ya no arranca o se vuelve demasiado lento, retire el override con precaución. sudo systemctl revert mi-servicio.service elimina los overrides locales de la unidad. No lo utilice si el mismo directorio contiene otros ajustes que desea conservar.

Controles después del cambio

  • Verifique el estado con systemctl is-active mi-servicio.service.
  • Revise MemoryCurrent, MemoryHigh, MemoryMax y CPUQuotaPerSecUSec después del reinicio.
  • Monitoree journalctl -u mi-servicio.service -b y el diario del núcleo durante el pico de carga que causaba problemas.
  • Midar el efecto del lado de la aplicación: tiempo de respuesta, trabajos atrasados, colas, errores del usuario.
  • Anote el motivo del ajuste, la fecha, el valor establecido y el síntoma corregido en su procedimiento de operación.

Los cgroups protegen al resto de la máquina y hacen que los límites sean visibles. No corrigen una fuga de memoria, una consulta demasiado costosa o un worker mal dimensionado. Si se alcanza el techo, mantenga el diario, la carga observada y el valor configurado para elegir entre ajustar el límite y corregir la aplicación.

Para profundizar, la página systemd.resource-control(5) detalla cada directiva. La guía Nice en Linux ayuda a elegir una prioridad de CPU cuando un límite estricto no es apropiado.

sudo apt update && sudo apt upgrade