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 єдиним деревом, яке послідовно обробляє CPU, пам'ять та введення-виведення, а також забезпечує ізоляцію контейнерів у 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). Для отримання додаткової інформації про swap, відновлення сторінок та OOM-кілер ядра дивіться наш супутній пост про управління пам'яттю в Linux.

Встановіть memory.high приблизно на 10–20% нижче memory.max. Ядро починає відновлювати сторінки та обмежувати виділення пам'яті, щойно memory.high , що зазвичай дозволяє робочому навантаженню відновитися до того, як спрацює OOM-кілер. Якщо використання досягає memory.max, ядро вбиває процеси в cgroup.

Типова конфігурація:

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

MemorySwapMax=0 відключає swap для цієї cgroup. Варто зробити це для робочих навантажень, чутливих до затримки (бази даних, потокове передавання в реальному часі), де введення-виведення swap може різко збільшити кінцеву затримку.

Для пулів робочих процесів, де залишення сирітських братів і сестер може пошкодити спільний стан, вкажіть 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 caps обмежує загальну кількість процесів і потоків, що запобігає виведенню хоста з ладу через «fork bomb» в одному орендарі. Помістіть служби орендарів у цей сегмент (через Slice=tenant-a.slice у їхніх файлах модулів), і вони автоматично успадкують усе.

Цей шаблон також працює для відокремлення шумних фонових процесів від сервісів, що взаємодіють з користувачами. Помістіть резервне копіювання, ротацію логів та пакетні завдання у background.slice з низьким CPUWeight та io.weight . Вони отримують повні ресурси, коли система перебуває в режимі очікування, і відступають убік, коли надходить виробничий трафік.

Для середовищ виконання контейнерів, таких як Docker та Podman, додайте Delegate=yes до їхніх файлів модулів systemd. Це дозволяє їм керувати власними підгрупами без прав root, а обмеження, встановлені для батьківського сегмента, все одно застосовуються до всього, що знаходиться нижче.

Моніторинг за допомогою systemd-cgtop та PSI

Щоб отримати динамічний огляд у стилі top щодо CPU, пам'яті та вводу-виводу для кожної 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

Процесор із завантаженням 100% та тиском 0% працює нормально. Кожне завдання, яке потребує процесора, отримує його. Той самий процесор із завантаженням 80%, але тиском 30% означає, що завдання стоять у черзі на виконання. Слідкуйте за PSI, а не за завантаженням: це дозволяє виявити конфлікти, які показники завантаження повністю пропускають.

Налаштовуйте обмеження в режимі реального часу без перезапуску:

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

Зміни застосовуються негайно і зберігаються після перезавантаження. У поєднанні з оповіщеннями на основі PSI це дозволяє реагувати на зміни навантаження, перш ніж вони перетворяться на OOM-кіли або неконтрольовану затримку.

Якщо ви виконуєте багатокористувацькі робочі навантаження з високою щільністю і вам потрібен хост із запасом потужності для чіткого застосування цих політик, наші виділені сервери створені саме для цього.

Блог

На цьому тижні

Більше статей
Чому важливо мати потужний і нелімітований VPS

Чому важливо мати потужний і нелімітований VPS

Нелімітований VPS надає фіксовану пропускну здатність з фіксованою швидкістю порту. Чим він відрізняється від тарифних планів, коли він окупиться і що потрібно перевірити перед покупкою.

7 хв читання - 9 травня 2025 р.

Управління пам'яттю в Linux: Swap, OOM Killer та Cgroups

12 хв читання - 31 травня 2026 р.

Більше статей
background image

Маєте запитання або потребуєте індивідуального рішення?

icon

Гнучкі варіанти

icon

Глобальне охоплення

icon

Миттєве розгортання

icon

Гнучкі варіанти

icon

Глобальне охоплення

icon

Миттєве розгортання