Tutoriel Linux

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

Débutant6 min de lecture

Un servicio web que se ralentiza, un trabajador asesinado 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 establecido 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 una medidor de recursos CPU y memoria
Cgroups y límites de recursos de un servicio Linux administrado por systemd.

Ver el cgroup de un servicio

En las distribuciones recientes, systemd coloca cada servicio en su propio cgroup. No tiene que mover PIDs a mano para diagnosticar una unidad clásica como Nginx, PostgreSQL o un trabajador de la aplicación.

Comience por leer el estado del servicio y las propiedades relacionadas con los recursos. Reemplace nginx.service con 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 igual a infinity, systemd no ha establecido un límite de memoria duro 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 igual a 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 duro. Si el cgroup alcanza este techo y el núcleo no recupera suficiente memoria, un proceso del grupo puede ser asesinado por el OOM killer.

MemoryHigh es menos brutal. El núcleo limita 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% proporciona el equivalente a un cuarto de un 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, arbitra principalmente 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 en /etc/systemd/system/nginx.service.d/ normalmente sobrescribe la unidad provista por el paquete. Evite modificar directamente un archivo en /usr/lib/systemd/system/ o /lib/systemd/system/, ya que una actualización puede sobrescribirlo.

Después revise los síntomas. Un servicio lento también puede venir de una consulta SQL lenta, de un disco saturado, de una cola llena o de un error en la 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 registro del núcleo no muestra ningún evento de memoria, busque en otra parte antes de tocar los cgroups: registros de la aplicación, 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, ejecute una unidad temporal con systemd-run. La prueba no modifica Nginx, PostgreSQL o su servicio comercial.

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

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

systemctl show ensayo-limite-memoria.service 
  -p MemoryMax -p CPUQuotaPerSecUSec -p ControlGroup -p ActiveState
systemctl cat ensayo-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 lanza un comando real.

sudo systemctl stop ensayo-limite-memoria.service

Aplicar un override de systemd

Cuando los registros y las medidas confirman que un límite tiene sentido, pase por un override de systemd. El siguiente ejemplo establece un umbral alto a 800 MB, un techo duro a 1 GB y limita el servicio al equivalente a 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, 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, elimine 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 a mantener.

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 registro del núcleo durante el pico de carga que causaba problemas.
  • midan el efecto del lado de la aplicación: tiempo de respuesta, trabajos atrasados, colas, errores de usuario.
  • Registre la razón del ajuste, la fecha, el valor retenido y el síntoma corregido en su procedimiento operativo.

Los cgroups protegen al resto de la máquina y hacen visibles los límites. No corrigen una fuga de memoria, una consulta demasiado costosa o un trabajador mal dimensionado. Si se alcanza el límite, mantenga el registro, 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 adecuado.