ограничение ресурсов cgroups v2 с помощью systemd
11 мин чтения - 3 июня 2026 г.

Установка ограничений на процессор, память и ввод-вывод с помощью cgroups v2 и systemd. Практическая конфигурация для многопользовательских Linux-хостов с мониторингом PSI и изоляцией срезов.
Ограничения ресурсов cgroups v2 с systemd
cgroups v2 — это унифицированная структура управления ресурсами ядра Linux. Она заменяет фрагментированную иерархию v1 единым деревом, которое последовательно управляет ЦП, памятью и вводом-выводом, а также лежит в основе изоляции контейнеров в Docker, Kubernetes и systemd. В этой статье рассказывается, как включить cgroups v2, установить ограничения через systemd и применить их в реальных сценариях многопользовательского хостинга.
Включение cgroups v2
Современные дистрибутивы поставляются с включенными по умолчанию cgroups v2: Ubuntu 21.10+, Debian 11+, Fedora 31+ и RHEL/Rocky 9+. Старые системы могут работать с гибридной иерархией или по-прежнему использовать v1 по умолчанию. Проверьте с помощью:
stat -fc %T /sys/fs/cgroup/
Вывод команды cgroup2fs подтверждает, что v2 активна. tmpfs обычно означает v1.
Чтобы переключить гибридную систему на чистую версию v2, отредактируйте /etc/default/grub и добавьте следующее в GRUB_CMDLINE_LINUX_DEFAULT:
systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all
Затем пересоздайте GRUB и перезагрузите систему:
sudo update-grub
sudo reboot
Для производственной среды запустите ядро 5.2 или более поздней версии, чтобы получить cgroup freezer для v2, и systemd 244+ для полной cpuset делегирование. В Rocky Linux 8 и RHEL 8 вам также может потребоваться явно включить учет ресурсов, добавив эти строки в /etc/systemd/system.conf:
DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes
Перезагрузите с помощью sudo systemctl daemon-reexec. После перезагрузки проверьте, какие контроллеры доступны:
cat /sys/fs/cgroup/cgroup.controllers
Вы должны увидеть cpu, memory, io, и pids перечисленные. Эти контроллеры по умолчанию не включены для дочерних cgroups. Чтобы активировать их, запишите в файл управления корневым поддеревом:
echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
Для подробного обзора того, чем v2 отличается от v1 внутренне, лучшим источником информации является доклад Майкла Керриска на NDC TechTown:
Как systemd организует cgroups
systemd создает cgroup для каждой запускаемой службы, называя ее в честь модуля. nginx.service получает /sys/fs/cgroup/system.slice/nginx.service/, и каждый запускаемый им процесс находится внутри этой cgroup. Три типа модулей напрямую соотносятся с иерархией:
| Тип модуля | Роль | Описание |
|---|---|---|
.slice | Внутренний узел | Группирует связанные службы и определяет общие ограничения |
.service | Конечный узел | Управляет процессами, запущенными systemd |
.scope | Листовой узел | Отслеживает процессы, запущенные извне (полезные нагрузки контейнеров, сеансы входа) |
По умолчанию поставляются четыре сегмента: -.slice (root), system.slice, user.sliceи machine.slice. Любое ограничение, примененное к слайсу, автоматически распространяется на все службы в нём.
Одно правило v2, о котором стоит помнить: процессы могут находиться только в листьевых узлах. Cgroup с дочерними cgroup не может напрямую размещать процессы, поэтому systemd никогда не помещает службы в ствол слайса.
Всегда устанавливайте ограничения через systemd, а не записывайте их /sys/fs/cgroup/ . Ручная запись не сохраняется после перезагрузки и вступает в конфликт с исключительным правом systemd на иерархию. Используйте systemctl set-property для разовых изменений и временных модулей (systemctl edit nginx.service) для постоянных.
Ограничения ЦП
cgroups v2 предоставляет два средства управления ЦП: жесткий лимит (cpu.max, представленное как CPUQuota в systemd) и пропорциональный вес (cpu.weight / CPUWeight).
CPUQuota — это абсолютный верхний предел. CPUQuota=50% позволяет использовать половину ядра; CPUQuota=200% позволяет использовать время, равное двум полным ядрам. Сервис будет ограничен, если попытается использовать больше, независимо от того, насколько простаивает остальная часть процессора.
CPUWeight имеет значение только при конкуренции за ресурсы. Диапазон значений — от 1 до 10 000, по умолчанию — 100. Три службы с весами 150, 100 и 50 получают примерно 50 %, 33 % и 17 % времени ЦП, когда все они требуют его одновременно. Когда ЦП в остальном находится в режиме простоя, веса ничего не ограничивают.
Для рабочих нагрузок, чувствительных к задержкам, привязывайте процессы к конкретным ядрам с помощью AllowedCPUs=. Это уменьшает количество переключений контекста и поддерживает кэш каждого ядра в активном состоянии:
[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3
Используйте жесткую квоту, когда вам нужны предсказуемые затраты (биллинг для нескольких арендаторов, изоляция от «шумных соседей»). Используйте веса, когда вам нужна максимальная загрузка оборудования и требуется лишь упорядочение по приоритетам во время пиковых нагрузок.
Ограничения памяти с cgroups v2
Память имеет два уровня: memory.high (мягкий, регулирующий) и memory.max (жесткий, OOM). Для получения общей информации о свопе, освобождении страниц и OOM-киллере ядра см. нашу сопутствующую статью об управлении памятью в Linux.
Установите memory.high примерно на 10–20% ниже memory.max. Ядро начинает освобождать страницы и ограничивать выделение памяти, как только memory.high этот порог будет превышен, что обычно позволяет рабочей нагрузке восстановиться до срабатывания OOM-киллера. Если использование достигает memory.max, ядро убивает процессы в cgroup.
Типичная конфигурация:
[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0
MemorySwapMax=0 отключает подкачку для этой cgroup. Это стоит сделать для рабочих нагрузок, чувствительных к задержкам (базы данных, потоковая передача в реальном времени), где ввод-вывод подкачки может резко увеличить задержку на конце.
Для пулов рабочих процессов, где оставление осиротевших братьев и сестер приведет к повреждению общего состояния, запишите 1 в файл memory.oom.group cgroup. Когда один процесс завершается из-за нехватки памяти (OOM), ядро завершает все процессы в cgroup одновременно.
Проверьте memory.events , чтобы увидеть, как часто сервис подвергался ограничению производительности или был убит из-за нехватки памяти (OOM):
cat /sys/fs/cgroup/system.slice/nginx.service/memory.events
Файл high и oom_kill показывают, правильно ли выбраны лимиты. Постоянные значения, отличные от нуля, означают, что рабочей нагрузке требуется больше свободного пространства.
Ограничения ввода-вывода
Контроллер ввода-вывода имеет ту же двухрежимную конструкцию: абсолютные ограничения посредством io.max и пропорциональное распределение через io.weight.
Ограничения устанавливаются для каждого блочного устройства, идентифицируемого номерами major:minor. Их можно найти с помощью lsblk -o NAME,MAJ:MIN. Типичная конфигурация systemd:
[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500
io.weight работает так cpu.weight: диапазон от 1 до 10 000, по умолчанию 100. Назначение значения 500 для сервиса, ориентированного на клиентов, и 50 для ночного резервного копирования предотвращает перегрузку диска во время пиковых нагрузок, но позволяет использовать полную пропускную способность, когда в ней не нуждается ничего другого.
Ограничения ввода-вывода применяются только в том случае, если вы указываете правильное устройство. Ядро отслеживает ввод-вывод по блочным устройствам, поэтому ограничение на /dev/sda не влияет на ввод-вывод, идущий на /dev/nvme0n1. На хостах с несколькими дисками устанавливайте ограничения для каждого устройства.
Изоляция многопользовательской среды с помощью сегментов
Для общих сред определите слайс на каждого арендатора. Создайте /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 ограничивает общее количество процессов и потоков, что предотвращает сбой хоста из-за fork-бомбы в одном арендаторе. Разместите сервисы арендатора в этом сегменте (через Slice=tenant-a.slice в их файлах модулей), и они автоматически унаследуют все настройки.
Этот паттерн также подходит для отделения «шумных» фоновых процессов от сервисов, взаимодействующих с пользователями. Поместите резервное копирование, ротацию логов и пакетные задания в background.slice с низким CPUWeight и io.weight значениями. Они получают полный доступ к ресурсам, когда система находится в режиме ожидания, и уступают место, когда поступает рабочий трафик.
Для сред выполнения контейнеров, таких как Docker и Podman, добавьте Delegate=yes в их файлы модулей systemd. Это позволяет им управлять собственными подгруппами cgroup без прав root, при этом ограничения, установленные на родительском сегменте, по-прежнему применяются ко всему, что находится ниже.
Мониторинг с помощью systemd-cgtop и PSI
Для просмотра в режиме реального времени информации о загрузке процессора, памяти и ввода-вывода по cgroup выполните:
systemd-cgtop
Для просмотра статической иерархии и информации о том, где находятся процессы, используйте systemd-cgls.
Самой полезной функцией v2 для мониторинга в производственной среде является Pressure Stall Information (PSI). PSI сообщает о проценте времени, в течение которого задачи в cgroup простаивали в ожидании ресурса, что отображается в трех файлах на каждую 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
CPU с загрузкой 100% и давлением 0% находится в нормальном состоянии. Каждая задача, требующая ресурсов CPU, получает их. То же CPU с загрузкой 80%, но давлением 30% означает, что задачи стоят в очереди на выполнение. Следите за PSI, а не за загрузкой: PSI фиксирует конфликты, которые показатели загрузки полностью упускают.
Настраивайте ограничения в режиме реального времени без перезапуска:
sudo systemctl set-property tenant-a.slice MemoryMax=6144M
Изменения вступают в силу немедленно и сохраняются после перезагрузки. В сочетании с оповещениями на основе PSI это позволяет реагировать на изменения нагрузки до того, как они приведут к прерыванию процессов из-за нехватки памяти (OOM) или к резкому увеличению задержки.
Если вы выполняете многопользовательские рабочие нагрузки высокой плотности и вам нужен хост с запасом мощности для четкого применения этих политик, наши выделенные серверы созданы именно для этого.
Почему важно иметь мощный и не тарифицируемый VPS
Неизмеряемый VPS предоставляет пропускную способность по фиксированной ставке при фиксированной скорости порта. Чем он отличается от дозированных тарифных планов, когда он окупается и что нужно проверить перед покупкой.
7 мин чтения - 9 мая 2025 г.
Управление памятью в Linux: Подкачка, убийца OOM и Cgroups
12 мин чтения - 31 мая 2026 г.

У вас есть вопросы или вам нужно индивидуальное решение?
Гибкие варианты
Глобальный охват
Мгновенное развертывание
Гибкие варианты
Глобальный охват
Мгновенное развертывание