Linux cgroups v2: управление ресурсами процессов — АйТи Фреш

Linux cgroups v2: управление ресурсами процессов

Linux cgroups v2: управление ресурсами процессов

Что такое cgroups и зачем они нужны

Cgroups (control groups) — механизм ядра Linux для ограничения, учёта и изоляции ресурсов процессов: CPU, памяти, I/O, сети. Без cgroups один процесс может сожрать всю память сервера и убить всё остальное. С cgroups — у каждого сервиса своя квота.

Cgroups v2 — переработанная версия, ставшая стандартом начиная с Ubuntu 22.04, Debian 12, RHEL 9. Главное отличие от v1: единая иерархия вместо отдельных иерархий для каждого контроллера. Это упростило управление и устранило ряд сложных edge cases.

На практике cgroups v2 нужны когда:

  • На одном сервере запущено несколько сервисов с разными приоритетами
  • Нужно защитить критичный сервис от OOM-kill из-за других процессов
  • Требуется ограничить потребление ресурсов контейнерами Docker/Podman
  • Нужен учёт ресурсов для биллинга или capacity planning

Systemd использует cgroups v2 прямо из коробки — каждый systemd-сервис автоматически получает свой cgroup.

Структура cgroups v2: как это работает

В cgroups v2 всё монтируется в одну точку: /sys/fs/cgroup. Каждый каталог — это cgroup. В нём файлы контроллеров: cpu.max, memory.max, io.max.

Навигация по дереву cgroups

Смотрим текущее состояние cgroups через несколько инструментов:

# Общая информация о cgroups в системе
systemctl status  # показывает cgroup-дерево

# Дерево cgroups через systemd
systemd-cgls

# Статистика использования ресурсов
systemd-cgtop

# Посмотреть cgroup конкретного процесса
cat /proc/$(pgrep nginx)/cgroup
# Пример вывода:
# 0::/system.slice/nginx.service

# Просмотр файлов cgroup nginx
ls /sys/fs/cgroup/system.slice/nginx.service/
# cpu.max  memory.current  memory.max  cgroup.procs  io.stat  ...

# Текущее использование памяти
cat /sys/fs/cgroup/system.slice/nginx.service/memory.current

Доступные контроллеры

Проверяем, какие контроллеры включены:

# Корневые контроллеры
cat /sys/fs/cgroup/cgroup.controllers
# cpu io memory pids

# Включённые субтроллеры для следующего уровня
cat /sys/fs/cgroup/cgroup.subtree_control
# cpu io memory pids

# Включить контроллер для пространства (если не включён)
echo "+cpu +memory +io" > /sys/fs/cgroup/cgroup.subtree_control
Управление ресурсами через systemd

Управление ресурсами через systemd

Самый удобный способ работать с cgroups в production — через systemd. Никаких ручных манипуляций с файловой системой, всё через unit-файлы и systemctl set-property.

Лимиты CPU

Три способа ограничить CPU для сервиса:

# 1. Через systemctl set-property (немедленно + сохраняется)
systemctl set-property nginx.service CPUQuota=50%
# 50% одного ядра. Для 2 ядер полностью: CPUQuota=200%

# 2. Через unit-файл (override)
systemctl edit nginx.service
# Добавляем в открывшийся редактор:
# [Service]
# CPUQuota=50%
# CPUWeight=80

# 3. Через drop-in файл вручную
mkdir -p /etc/systemd/system/nginx.service.d/
cat > /etc/systemd/system/nginx.service.d/limits.conf << 'EOF'
[Service]
CPUQuota=50%
CPUWeight=80
EOF
systemctl daemon-reload && systemctl restart nginx

# Проверка
cat /sys/fs/cgroup/system.slice/nginx.service/cpu.max
# 50000 100000  — 50000 мкс из 100000 мкс = 50%

systemctl show nginx.service -p CPUQuota,CPUWeight

CPUWeight (1-10000, default=100) — относительный приоритет при конкуренции. CPUQuota — жёсткий лимит. При высокой нагрузке лучше использовать оба.

Лимиты памяти

Управление памятью с мягким и жёстким лимитом:

# Мягкий лимит — начало throttling при достижении
systemctl set-property myapp.service MemoryHigh=400M

# Жёсткий лимит — OOM kill при достижении
systemctl set-property myapp.service MemoryMax=512M

# Зарезервировать память (гарантия)
systemctl set-property myapp.service MemoryMin=128M

# Отключить swap для сервиса
systemctl set-property myapp.service MemorySwapMax=0

# Проверка текущего использования
systemctl show myapp.service -p MemoryHigh,MemoryMax,MemoryCurrent

# Реальное потребление прямо из cgroup
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/memory.stat

На серверах с PostgreSQL мы выставляем MemoryHigh на 75% от shared_buffers + work_mem * max_connections, а MemoryMax на 90% от RAM. Это защищает от OOM kill других сервисов при неожиданном росте запросов.

Лимиты I/O

Ограничение дисковых операций — важно для серверов с несколькими сервисами на одном диске:

# Узнаём device number для диска
ls -la /dev/sda
# brw-rw---- 1 root disk 8, 0 ... /dev/sda
# Major:Minor = 8:0

# Ограничение чтения и записи
systemctl set-property myapp.service IOReadBandwidthMax="/dev/sda 50M"
systemctl set-property myapp.service IOWriteBandwidthMax="/dev/sda 30M"

# Относительный приоритет I/O (1-10000)
systemctl set-property myapp.service IOWeight=50  # Ниже default 100

# Для SSD — лимит IOPS
systemctl set-property myapp.service IOReadIOPSMax="/dev/sda 5000"
systemctl set-property myapp.service IOWriteIOPSMax="/dev/sda 3000"

# Статистика I/O по cgroup
cat /sys/fs/cgroup/system.slice/myapp.service/io.stat

Ручное создание cgroups

Иногда нужно создать cgroup для процессов, которые systemd не контролирует — скрипты, разовые задачи, legacy-приложения без юнитов.

Создание cgroup вручную

Создаём изолированный cgroup для процесса:

# Создаём cgroup
mkdir /sys/fs/cgroup/myapp

# Включаем контроллеры
echo "+cpu +memory" > /sys/fs/cgroup/myapp/cgroup.subtree_control

# Устанавливаем лимиты
echo "50000 100000" > /sys/fs/cgroup/myapp/cpu.max   # 50% CPU
echo $((512 * 1024 * 1024)) > /sys/fs/cgroup/myapp/memory.max  # 512 МБ
echo $((400 * 1024 * 1024)) > /sys/fs/cgroup/myapp/memory.high

# Запускаем процесс в cgroup
echo $$ > /sys/fs/cgroup/myapp/cgroup.procs  # текущий shell
./myapp  # запускается в контексте cgroup

# Или через systemd-run
systemd-run --scope -p CPUQuota=50% -p MemoryMax=512M ./myapp

# Мониторинг
cat /sys/fs/cgroup/myapp/cpu.stat
cat /sys/fs/cgroup/myapp/memory.events  # показывает OOM-события
Мониторинг ресурсов через cgroups

Мониторинг ресурсов через cgroups

Cgroups v2 предоставляют точную статистику по каждому сервису — часто точнее, чем top или htop, которые показывают мгновенные снимки.

Инструменты мониторинга

Набор команд для ежедневного мониторинга:

# Интерактивный топ по cgroups
systemd-cgtop -d 1  # обновление каждую секунду

# Потребление памяти всех сервисов
systemctl list-units --type=service --state=running \
  | awk '{print $1}' \
  | xargs -I{} sh -c 'echo -n "{}: "; systemctl show {} -p MemoryCurrent | cut -d= -f2'

# Экспорт метрик cgroups в Prometheus
# Используем node_exporter с флагом --collector.cgroups
# Метрики: node_cgroup_cpu_usage_seconds_total, node_cgroup_memory_usage_bytes

# Проверка OOM-событий
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events
# anon 12345
# file 67890
# oom 0        <- если не 0 — был OOM kill
# oom_kill 0

Показатель oom_kill в memory.events — первое, что мы проверяем при жалобах на нестабильную работу сервиса. Если там не ноль, значит процесс убивали по памяти и нужно или поднять лимит или оптимизировать приложение.

Часто задаваемые вопросы

Cgroups v1 имели иерархию для каждого контроллера отдельно. В v2 единая иерархия для всех контроллеров. Это устранило ряд race conditions и упростило управление. Ubuntu 22.04+ и Debian 12+ используют cgroups v2 по умолчанию.

Выполните: mount | grep cgroup. Если видите cgroup2 — v2, если только cgroup — v1. Также: cat /sys/fs/cgroup/cgroup.controllers покажет доступные контроллеры (только в v2).

Через systemd: в unit-файле добавьте CPUQuota=50% (50% одного ядра) или CPUWeight=50. Для разовых задач: systemd-run --scope -p CPUQuota=25% ./my-script.sh.

При memory.max процесс получает OOM kill. При memory.high cgroups начинает throttling. Рекомендуем: memory.high = 80% лимита, memory.max = 100% лимита — приложение успевает среагировать до жёсткого убийства.

Да, Docker 20.10+ поддерживает cgroups v2. При запуске контейнера с --memory=512m Docker создаёт cgroup и устанавливает memory.max автоматически.

#linux cgroups v2 #systemd cgroups #cpu limit linux #memory limit linux

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈️ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Подпишитесь на рассылку ITfresh

Каждую неделю мы выпускаем практические гайды для руководителей IT и системных администраторов. Это не просто теория! Здесь вы найдёте всё: безопасность, 1С, миграции, резервные копии и проверенные лайфхаки из наших реальных проектов.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.
Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.