cgroups v2 límites de recursos con systemd

11 min de lectura - 3 de junio de 2026

hero section cover
Tabla de contenidos
  • Límites de recursos de cgroups v2 con systemd
  • Habilitar cgroups v2
  • Cómo organiza systemd los cgroups
  • Límites de CPU
  • Límites de memoria con cgroups v2
  • Límites de E/S
  • Aislamiento multitenant con segmentos
  • Supervisión con systemd-cgtop y PSI
Compartir

Establecer límites de CPU, memoria y E/S con cgroups v2 y systemd. Configuración práctica para hosts Linux multi-tenant, con monitorización PSI y aislamiento de slice.

Límites de recursos de cgroups v2 con systemd

cgroups v2 es el marco de control de recursos unificado del kernel de Linux. Sustituye la jerarquía fragmentada de la v1 por un único árbol que gestiona la CPU, la memoria y las E/S de forma coherente, y sustenta el aislamiento de contenedores en Docker, Kubernetes y systemd. Esta publicación explica cómo habilitar cgroups v2, establecer límites a través de systemd y aplicarlo a escenarios reales de alojamiento multitenant.

Habilitar cgroups v2

Las distribuciones modernas incluyen cgroups v2 habilitado de forma predeterminada: Ubuntu 21.10+, Debian 11+, Fedora 31+ y RHEL/Rocky 9+. Los sistemas más antiguos pueden ejecutar una jerarquía híbrida o seguir utilizando v1 de forma predeterminada. Compruébalo con:

stat -fc %T /sys/fs/cgroup/

La salida de cgroup2fs confirma que v2 está activa. tmpfs normalmente significa v1.

Para cambiar un sistema híbrido a v2 puro, edita /etc/default/grub y añada lo siguiente a GRUB_CMDLINE_LINUX_DEFAULT:

systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all

A continuación, regenera GRUB y reinicia:

sudo update-grub
sudo reboot

Para producción, ejecute el kernel 5.2 o posterior para obtener el congelador de cgroups para v2, y systemd 244+ para una cpuset . En Rocky Linux 8 y RHEL 8, es posible que también tengas que habilitar la contabilidad explícitamente añadiendo estas líneas a /etc/systemd/system.conf:

DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes

. Vuelve a cargar con sudo systemctl daemon-reexec. Tras el reinicio, compruebe qué controladores están disponibles:

cat /sys/fs/cgroup/cgroup.controllers

Deberías ver cpu, memory, io, y pids en la lista. Estos controladores no están habilitados para los cgroups secundarios de forma predeterminada. Para activarlos, escribe en el archivo de control del subárbol raíz:

echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control

Para obtener una explicación detallada de las diferencias internas entre la v2 y la v1, la charla de Michael Kerrisk en NDC TechTown es el mejor recurso disponible:

Cómo organiza systemd los cgroups

systemd crea un cgroup para cada servicio que inicia, con el nombre de la unidad. nginx.service obtiene /sys/fs/cgroup/system.slice/nginx.service/, y todos los procesos que genera residen dentro de ese cgroup. Hay tres tipos de unidades que se corresponden directamente con la jerarquía:

Tipo de unidadFunciónDescripción
.sliceNodo internoAgrupa servicios relacionados y define límites compartidos
.serviceNodo terminalGestiona los procesos iniciados por systemd
.scopeNodo hojaRealiza un seguimiento de los procesos iniciados externamente (cargas útiles de contenedores, sesiones de inicio de sesión)

Se incluyen cuatro segmentos predeterminados de serie: -.slice (raíz), system.slice, user.slice, y machine.slice. Cualquier límite aplicado a una sección se aplica automáticamente a todos los servicios que contiene.

Una regla de la v2 que conviene recordar: los procesos solo pueden residir en nodos hoja. Un cgroup con cgroups hijos no puede alojar procesos directamente, por lo que systemd nunca coloca servicios en el tronco de una partición.

Establece siempre los límites a través de systemd en lugar de escribir /sys/fs/cgroup/ directamente. Las modificaciones manuales no persisten tras los reinicios y entran en conflicto con la propiedad exclusiva de la jerarquía por parte de systemd. Utiliza systemctl set-property para cambios puntuales y unidades temporales (systemctl edit nginx.service) para los permanentes.

Límites de CPU

cgroups v2 ofrece dos controles de CPU: un límite máximo (cpu.max, disponible como CPUQuota en systemd) y un peso proporcional (cpu.weight / CPUWeight).

CPUQuota es un límite máximo absoluto. CPUQuota=50% permite medio núcleo; CPUQuota=200% permite el tiempo equivalente a dos núcleos completos. El servicio se limita si intenta superar este valor, independientemente de lo inactiva que esté el resto de la CPU.

CPUWeight solo importa en caso de conflicto. El rango es de 1 a 10 000, con un valor predeterminado de 100. Tres servicios con pesos de 150, 100 y 50 reciben aproximadamente el 50 %, el 33 % y el 17 % del tiempo de CPU cuando todos lo solicitan a la vez. Cuando la CPU está inactiva por otros motivos, las ponderaciones no limitan nada.

Para cargas de trabajo sensibles a la latencia, asigne los procesos a núcleos específicos con AllowedCPUs=. Esto reduce el cambio de contexto y mantiene activa la caché por núcleo:

[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3

Utilice una cuota fija cuando necesite un coste predecible (facturación multitenant, aislamiento de vecinos ruidosos). Utilice ponderaciones cuando desee la máxima utilización del hardware y solo necesite un orden de prioridad durante los picos.

Límites de memoria con cgroups v2

La memoria tiene dos niveles: memory.high (suave, limitación) y memory.max (duro, OOM). Para obtener información sobre el swap, la recuperación de páginas y el OOM killer del kernel, consulta nuestra entrada complementaria sobre la gestión de la memoria en Linux.

Establezca memory.high aproximadamente entre un 10 % y un 20 % por debajo memory.max. El kernel comienza a recuperar páginas y a limitar las asignaciones una vez memory.high se supera este umbral, lo que suele permitir que la carga de trabajo se recupere antes de que se active el OOM killer. Si el uso alcanza memory.max, el kernel elimina procesos en el cgroup.

Una configuración típica:

[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0

MemorySwapMax=0 desactiva el swap para este cgroup. Vale la pena hacerlo para cargas de trabajo sensibles a la latencia (bases de datos, streaming en tiempo real) en las que la E/S del swap hundiría la latencia de cola.

Para grupos de trabajadores en los que dejar hermanos huérfanos corrompería el estado compartido, escribe 1 en el archivo memory.oom.group . Cuando se termina un proceso por falta de memoria (OOM), el kernel termina todos los procesos del cgroup a la vez.

Consulte memory.events para ver con qué frecuencia se ha limitado o eliminado por OOM un servicio:

cat /sys/fs/cgroup/system.slice/nginx.service/memory.events

El high y oom_kill le indican si sus límites están bien dimensionados. Los valores persistentes distintos de cero significan que la carga de trabajo necesita más margen.

Límites de E/S

El controlador de E/S tiene el mismo diseño de dos modos: límites absolutos mediante io.max y el reparto proporcional mediante io.weight.

Los límites son por dispositivo de bloque, identificados por números mayor:menor. Encuéntralos con lsblk -o NAME,MAJ:MIN. Una configuración típica de systemd:

[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500

io.weight funciona como cpu.weight: rango de 1 a 10 000, valor predeterminado 100. Asignar 500 a un servicio de cara al cliente y 50 a una copia de seguridad nocturna evita que esta sature el disco durante las horas punta, pero le permite utilizar todo el ancho de banda cuando no hay otra cosa que lo necesite.

Los límites de E/S solo se aplican cuando se selecciona el dispositivo correcto. El kernel realiza un seguimiento de la E/S por dispositivo de bloque, por lo que un límite en /dev/sda no tiene ningún efecto sobre la E/S que va a /dev/nvme0n1. En hosts con varios discos, establezca los límites por dispositivo.

Aislamiento multitenant con segmentos

Para entornos compartidos, define una partición por inquilino. Crea /etc/systemd/system/tenant-a.slice:

[Slice]
CPUQuota=200%
CPUWeight=150
MemoryHigh=3584M
MemoryMax=4096M
MemorySwapMax=0
IOReadBandwidthMax=/dev/sda 200M
TasksMax=512

TasksMax=512 limita el número total de procesos y subprocesos, lo que evita que una bomba de bifurcación en un inquilino derribe el host. Coloca los servicios del inquilino en esta partición (a través de Slice=tenant-a.slice en sus archivos de unidad) y estos heredarán todo automáticamente.

Este patrón también sirve para separar el trabajo de fondo ruidoso de los servicios orientados al usuario. Coloca las copias de seguridad, la rotación de registros y los trabajos por lotes en una background.slice con baja CPUWeight y io.weight . Obtienen todos los recursos cuando el sistema está inactivo y se apartan cuando llega el tráfico de producción.

Para entornos de ejecución de contenedores como Docker y Podman, añade Delegate=yes a sus archivos de unidad de systemd. Esto les permite gestionar sus propios sub-cgroups sin necesidad de root, y los límites establecidos en la partición principal siguen aplicándose a todo lo que hay debajo.

Supervisión con systemd-cgtop y PSI

Para obtener una vista en tiempo real al estilo «top» de la CPU, la memoria y las E/S por cgroup, ejecute:

systemd-cgtop

Para ver la jerarquía estática y qué procesos se encuentran en cada lugar, utilice systemd-cgls.

La característica más útil de la v2 para la monitorización en producción es la Información de estancamiento por presión (PSI). PSI informa del porcentaje de tiempo que las tareas de un cgroup han estado estancadas a la espera de un recurso, y se muestra en tres archivos por cgroup:

cat /sys/fs/cgroup/tenant-a.slice/cpu.pressure
cat /sys/fs/cgroup/tenant-a.slice/memory.pressure
cat /sys/fs/cgroup/tenant-a.slice/io.pressure

Una CPU con un 100 % de utilización y un 0 % de presión está en buen estado. Todas las tareas que necesitan CPU la están obteniendo. La misma CPU con un 80 % de utilización pero un 30 % de presión significa que las tareas están haciendo cola para obtener tiempo de ejecución. Alerta sobre el PSI, no sobre la utilización: detecta conflictos que las métricas de utilización pasan por alto por completo.

Ajuste los límites en tiempo real sin reiniciar nada:

sudo systemctl set-property tenant-a.slice MemoryMax=6144M

El cambio se aplica de inmediato y persiste tras los reinicios. En combinación con las alertas basadas en PSI, esto le permite responder a los cambios de carga antes de que se conviertan en terminaciones por falta de memoria (OOM) o en una latencia descontrolada.

Si está ejecutando cargas de trabajo multitenant de alta densidad y necesita un host con el margen necesario para aplicar estas políticas de forma limpia, nuestros servidores dedicados están diseñados para ello.

Blog

Destacados de la semana

Más artículos
Por qué es importante tener un VPS potente y sin contador

Por qué es importante tener un VPS potente y sin contador

Un VPS sin contador proporciona un ancho de banda fijo a una velocidad de puerto fija. En qué se diferencia de los planes con contador, cuándo compensa y qué hay que comprobar antes de comprarlo.

7 min de lectura - 9 de mayo de 2025

Gestión de memoria en Linux: Swap, OOM Killer y Cgroups

12 min de lectura - 31 de mayo de 2026

Más artículos