Gestión de Recursos con cgroups v2 en Linux
cgroups (control groups) v2 es la interfaz del kernel Linux para organizar procesos en grupos jerárquicos y aplicar límites de recursos—CPU, memoria, I/O y procesos—a cada grupo. La versión 2 unifica la jerarquía de v1, simplifica la delegación y mejora la gestión de memoria. Es la base de contenedores Docker y Kubernetes, y systemd la usa internamente para todos los servicios. Esta guía cubre la configuración directa de cgroups v2, su integración con systemd y el monitoreo de recursos.
Requisitos Previos
- Kernel Linux 5.2+ (preferiblemente 5.10+ para soporte completo de cgroups v2)
- Ubuntu 21.04+, Fedora 31+, Rocky Linux 9+ (cgroups v2 por defecto)
- Acceso root
# Verificar versión del kernel
uname -r
# Verificar si cgroups v2 está disponible
cat /proc/filesystems | grep cgroup2
Verificar y Habilitar cgroups v2
# Verificar si el sistema usa cgroups v2 (unified hierarchy)
mount | grep cgroup2
# Si aparece: cgroup2 on /sys/fs/cgroup type cgroup2 -> cgroups v2 activo
# Verificar la versión actual
cat /sys/fs/cgroup/cgroup.controllers
# En sistemas con modo híbrido o solo v1, habilitar v2 completamente
# Añadir a los parámetros del kernel en GRUB:
# systemd.unified_cgroup_hierarchy=1
# Ubuntu (añadir parámetro al kernel)
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=1"/' \
/etc/default/grub
update-grub
# Reiniciar para aplicar: reboot
# Verificar controladores disponibles
cat /sys/fs/cgroup/cgroup.controllers
# Debe mostrar: cpuset cpu io memory pids
Estructura y Jerarquía de cgroups v2
# La jerarquía de cgroups v2 está en /sys/fs/cgroup/
ls /sys/fs/cgroup/
# Archivos clave en la raíz
cat /sys/fs/cgroup/cgroup.controllers # Controladores disponibles
cat /sys/fs/cgroup/cgroup.subtree_control # Controladores habilitados para hijos
# Ver jerarquía de systemd
ls /sys/fs/cgroup/system.slice/
ls /sys/fs/cgroup/user.slice/
# Crear un cgroup manualmente
mkdir /sys/fs/cgroup/mi-grupo
# Verificar archivos creados automáticamente
ls /sys/fs/cgroup/mi-grupo/
# cgroup.controllers cgroup.events cgroup.freeze cgroup.max.depth
# cgroup.max.descendants cgroup.procs cgroup.stat cgroup.subtree_control
# cgroup.threads cgroup.type
# Habilitar controladores en el cgroup hijo
echo "+cpu +memory +io" > /sys/fs/cgroup/cgroup.subtree_control
echo "+cpu +memory +io" > /sys/fs/cgroup/mi-grupo/cgroup.subtree_control
Control de CPU
CPU Weight (Prioridad Relativa)
# El peso de CPU determina la prioridad relativa entre grupos (rango: 1-10000, defecto: 100)
mkdir /sys/fs/cgroup/alta-prioridad
mkdir /sys/fs/cgroup/baja-prioridad
# Habilitar controlador CPU
echo "+cpu" > /sys/fs/cgroup/cgroup.subtree_control
# Asignar pesos (alta-prioridad recibe 10x más CPU que baja-prioridad)
echo 1000 > /sys/fs/cgroup/alta-prioridad/cpu.weight
echo 100 > /sys/fs/cgroup/baja-prioridad/cpu.weight
# Verificar el peso asignado
cat /sys/fs/cgroup/alta-prioridad/cpu.weight
CPU Max (Límite Absoluto de CPU)
# Formato: max_cuota periodo (en microsegundos)
# Limitar al 25% de un núcleo (250000 de 1000000 microsegundos)
echo "250000 1000000" > /sys/fs/cgroup/mi-grupo/cpu.max
# Limitar al 200% (2 núcleos completos)
echo "200000 100000" > /sys/fs/cgroup/mi-grupo/cpu.max
# Sin límite
echo "max 100000" > /sys/fs/cgroup/mi-grupo/cpu.max
# Añadir proceso al cgroup y verificar
echo $$ > /sys/fs/cgroup/mi-grupo/cgroup.procs
cat /sys/fs/cgroup/mi-grupo/cpu.stat
Asignar Núcleos Específicos con cpuset
# Habilitar controlador cpuset
echo "+cpuset" > /sys/fs/cgroup/cgroup.subtree_control
mkdir /sys/fs/cgroup/grupo-cpuset
# Asignar solo los núcleos 2 y 3 al grupo
echo "2-3" > /sys/fs/cgroup/grupo-cpuset/cpuset.cpus
echo "0" > /sys/fs/cgroup/grupo-cpuset/cpuset.mems # Nodo NUMA 0
# Añadir un proceso al grupo con cpuset
echo $(pidof mi-proceso) > /sys/fs/cgroup/grupo-cpuset/cgroup.procs
Control de Memoria
# Habilitar controlador de memoria
echo "+memory" > /sys/fs/cgroup/cgroup.subtree_control
mkdir /sys/fs/cgroup/grupo-memoria
# Límite de memoria RAM (hard limit - proceso recibe OOM kill si lo supera)
echo "512M" > /sys/fs/cgroup/grupo-memoria/memory.max
# Límite suave (el kernel intentará mantener el uso por debajo, pero no mata procesos)
echo "256M" > /sys/fs/cgroup/grupo-memoria/memory.high
# Deshabilitar swap para este grupo
echo "0" > /sys/fs/cgroup/grupo-memoria/memory.swap.max
# Añadir proceso al grupo con límites de memoria
echo $$ > /sys/fs/cgroup/grupo-memoria/cgroup.procs
# Monitorear uso de memoria del grupo
cat /sys/fs/cgroup/grupo-memoria/memory.current # Uso actual en bytes
cat /sys/fs/cgroup/grupo-memoria/memory.stat # Estadísticas detalladas
# Habilitar notificaciones de presión de memoria
cat /sys/fs/cgroup/grupo-memoria/memory.pressure
Script de Límite de Memoria con cgroups v2
cat > /usr/local/bin/run-with-memlimit.sh << 'EOF'
#!/bin/bash
# Ejecutar un proceso con límite de memoria usando cgroups v2
LIMITE=${1:-256M}
shift
COMANDO="$@"
GRUPO="memlimit-$$"
# Crear cgroup temporal
mkdir /sys/fs/cgroup/$GRUPO
# Habilitar controladores
echo "+memory +cpu" > /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null
echo "0" > /sys/fs/cgroup/$GRUPO/memory.swap.max
echo "$LIMITE" > /sys/fs/cgroup/$GRUPO/memory.max
# Ejecutar el proceso en el cgroup
echo $$ > /sys/fs/cgroup/$GRUPO/cgroup.procs
exec $COMANDO
# Limpieza (se ejecuta tras el comando)
rmdir /sys/fs/cgroup/$GRUPO 2>/dev/null
EOF
chmod +x /usr/local/bin/run-with-memlimit.sh
# Uso: run-with-memlimit.sh 128M python3 script-intensivo.py
Control de I/O
# Habilitar controlador I/O
echo "+io" > /sys/fs/cgroup/cgroup.subtree_control
mkdir /sys/fs/cgroup/grupo-io
# Obtener el número de dispositivo del disco
ls -la /dev/sda
# Ejemplo: 8:0 (major:minor)
# Limitar ancho de banda de lectura (10 MB/s)
echo "8:0 rbps=10485760" > /sys/fs/cgroup/grupo-io/io.max
# Limitar ancho de banda de escritura (5 MB/s)
echo "8:0 wbps=5242880" > /sys/fs/cgroup/grupo-io/io.max
# Limitar IOPS (operaciones de I/O por segundo)
echo "8:0 riops=1000 wiops=500" > /sys/fs/cgroup/grupo-io/io.max
# Prioridad de I/O (peso relativo, rango 1-10000)
echo "8:0 100" > /sys/fs/cgroup/grupo-io/io.weight
# Ver estadísticas de I/O del grupo
cat /sys/fs/cgroup/grupo-io/io.stat
Integración con systemd
systemd gestiona cgroups v2 automáticamente. Para configurar límites de recursos en servicios:
# Configurar límites directamente en el unit file
cat > /etc/systemd/system/app-con-limites.service << 'EOF'
[Unit]
Description=Aplicación con límites de recursos cgroups v2
[Service]
Type=simple
ExecStart=/opt/app/servidor
# Límites de CPU
CPUQuota=50% # 50% de un núcleo (equivale a cpu.max 500ms/1s)
CPUWeight=200 # Doble de prioridad que el defecto (100)
# Límites de memoria
MemoryMax=1G # Límite estricto
MemoryHigh=768M # Límite suave (throttle)
MemorySwapMax=0 # Sin swap
# Límites de I/O
IOWeight=50 # Menor prioridad de I/O
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 20M
# Límite de procesos/hilos
TasksMax=100
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start app-con-limites.service
# Modificar límites en caliente (sin reiniciar)
systemctl set-property app-con-limites.service MemoryMax=2G
systemctl set-property app-con-limites.service CPUQuota=75%
# Ver los límites actuales en el cgroup de systemd
cat /sys/fs/cgroup/system.slice/app-con-limites.service/cpu.max
cat /sys/fs/cgroup/system.slice/app-con-limites.service/memory.max
Gestión de Recursos por Slice
# Limitar recursos de todos los servicios del usuario
mkdir -p /etc/systemd/system/user.slice.d/
cat > /etc/systemd/system/user.slice.d/resource-limits.conf << 'EOF'
[Slice]
# Limitar toda la slice de usuarios a 40% CPU y 2GB RAM
CPUQuota=400%
MemoryMax=2G
EOF
# Límites para servicios del sistema
mkdir -p /etc/systemd/system/system.slice.d/
cat > /etc/systemd/system/system.slice.d/resource-limits.conf << 'EOF'
[Slice]
CPUWeight=500
MemoryMax=4G
EOF
systemctl daemon-reload
Monitoreo de Recursos
# Monitor en tiempo real de cgroups (incluido en systemd)
systemd-cgtop
# Ver uso de recursos de un servicio específico
systemctl status mi-servicio.service
# Ver estadísticas de cgroup directamente
CGROUP_PATH="/sys/fs/cgroup/system.slice/mi-servicio.service"
echo "=== Uso de CPU ==="
cat $CGROUP_PATH/cpu.stat
echo "=== Uso de Memoria ==="
echo "Actual: $(cat $CGROUP_PATH/memory.current) bytes"
echo "Máximo configurado: $(cat $CGROUP_PATH/memory.max)"
echo "Alto configurado: $(cat $CGROUP_PATH/memory.high)"
echo "=== Estadísticas de I/O ==="
cat $CGROUP_PATH/io.stat
# Script de monitoreo continuo
cat > /usr/local/bin/cgroup-monitor.sh << 'EOF'
#!/bin/bash
# Monitor de uso de recursos por cgroup
SERVICIO=${1:-"mi-servicio.service"}
CGROUP="/sys/fs/cgroup/system.slice/$SERVICIO"
while true; do
TIMESTAMP=$(date +"%H:%M:%S")
MEM_ACTUAL=$(cat "$CGROUP/memory.current" 2>/dev/null || echo 0)
MEM_MB=$((MEM_ACTUAL / 1024 / 1024))
CPU_USAGE=$(cat "$CGROUP/cpu.stat" 2>/dev/null | grep "^usage_usec" | awk '{print $2}')
echo "$TIMESTAMP - Memoria: ${MEM_MB}MB - CPU total: ${CPU_USAGE}µs"
sleep 5
done
EOF
chmod +x /usr/local/bin/cgroup-monitor.sh
Solución de Problemas
Error "No space left on device" al crear cgroups:
# El sistema tiene límite de cgroups simultáneos
cat /sys/fs/cgroup/cgroup.max.descendants
# Aumentar el límite
echo 1000 > /sys/fs/cgroup/cgroup.max.descendants
Los límites de memoria no funcionan:
# Verificar que el controlador memory está habilitado en el cgroup padre
cat /sys/fs/cgroup/cgroup.subtree_control | grep memory
# Si no está, habilitarlo
echo "+memory" > /sys/fs/cgroup/cgroup.subtree_control
Los cambios con systemctl set-property no persisten tras reinicio:
# Los cambios con set-property son por defecto persistentes en /etc/systemd/system/*.service.d/
ls /etc/systemd/system/mi-servicio.service.d/
cat /etc/systemd/system/mi-servicio.service.d/50-CPUQuota.conf
# Si quieres que sea temporal (solo para la sesión actual)
systemctl set-property --runtime mi-servicio.service CPUQuota=50%
El sistema no soporta cgroups v2:
# Verificar si la distribución usa v1
mount | grep "type cgroup " # cgroup v1
mount | grep "type cgroup2" # cgroup v2
# Verificar soporte en el kernel
grep CONFIG_CGROUP /boot/config-$(uname -r) | grep -v "^#"
Conclusión
cgroups v2 es una herramienta fundamental para el control de recursos en Linux moderno, ofreciendo una jerarquía unificada y mayor precisión en la limitación de CPU, memoria e I/O. La integración directa con systemd hace que configurar límites de recursos por servicio sea sencillo mediante directivas como CPUQuota, MemoryMax e IOWeight. Para cargas de trabajo en producción, definir límites de recursos apropiados previene que un servicio monopolice el sistema y garantiza la calidad de servicio para todas las aplicaciones.


