ограничение ресурсов cgroups v2 с помощью systemd

11 мин чтения - 3 июня 2026 г.

hero section cover
Содержание
  • Ограничения ресурсов cgroups v2 с systemd
  • Включение cgroups v2
  • Как systemd организует cgroups
  • Ограничения ЦП
  • Ограничения памяти с cgroups v2
  • Ограничения ввода-вывода
  • Изоляция многопользовательской среды с помощью сегментов
  • Мониторинг с помощью systemd-cgtop и PSI
Поделиться

Установка ограничений на процессор, память и ввод-вывод с помощью 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

Неизмеряемый VPS предоставляет пропускную способность по фиксированной ставке при фиксированной скорости порта. Чем он отличается от дозированных тарифных планов, когда он окупается и что нужно проверить перед покупкой.

7 мин чтения - 9 мая 2025 г.

Управление памятью в Linux: Подкачка, убийца OOM и Cgroups

12 мин чтения - 31 мая 2026 г.

Другие статьи
background image

У вас есть вопросы или вам нужно индивидуальное решение?

icon

Гибкие варианты

icon

Глобальный охват

icon

Мгновенное развертывание

icon

Гибкие варианты

icon

Глобальный охват

icon

Мгновенное развертывание