Що дають ліміти cgroups і systemd
Ліміти cgroups дозволяють обмежити службі процесор, пам’ять, введення-виведення і кількість процесів, не змінюючи її код: потрібні директиви пишуться в юніт systemd, а ядро стежить за їх виконанням. Так одна служба, що зірвалася, не забере ресурси в решти. Створення самої служби описане в статті як створити службу systemd.
| Директива | Що обмежує | Значення |
|---|---|---|
| CPUQuota= | стелю часу процесора | відсотки: 20% — не більше 20% часу одного ядра, понад 100% — час кількох ядер |
| CPUWeight= | частку процесора за конкуренції | від 1 до 10000, за замовчуванням 100 |
| MemoryHigh= | м’який поріг пам’яті | байти із суфіксами K, M, G, T, відсоток або infinity |
| MemoryMax= | жорсткий ліміт пам’яті | ті самі одиниці |
| MemorySwapMax= | ліміт використання swap | байти, відсоток від розміру swap або infinity |
| IOWeight= | частку блочного введення-виведення | від 1 до 10000, за замовчуванням 100 |
| TasksMax= | кількість завдань у юніті | число, відсоток або infinity |
Значення — за man systemd.resource-control. Версію systemd перевірте командою systemctl --version: сторінка man описує останню версію.
Як перевірити, що у вас cgroup v2
Перевірка одна: якщо існує файл /sys/fs/cgroup/cgroup.controllers, система використовує cgroup v2. За документацією Kubernetes і проєкту rootlesscontaine.rs v2 увімкнена за замовчуванням у Debian 11 і новіших, Ubuntu 21.10 і новіших (рекомендується 22.04 LTS і новіші), RHEL 9 і новіших, Fedora 31 і новіших.
Процесор: CPUQuota і CPUWeight
CPUQuota= ставить жорстку стелю: 20% означає, що процеси служби ніколи не отримають понад 20% часу одного ядра, а значення понад 100% дає час кількох ядер, наприклад 200% — двох. CPUWeight= стелі не ставить і працює лише за конкуренції: вільний процесор служба отримає цілком.
CPUWeight= приймає значення від 1 до 10000, за замовчуванням 100, і ділить процесор між службами одного слайса пропорційно вагам.
Пам’ять: MemoryHigh, MemoryMax і swap
MemoryMax= — жорсткий ліміт: якщо вкластися не вдається, OOM-killer викликається всередині юніта. MemoryHigh= — м’який поріг: процеси сильно уповільнюються і в них відбирають пам’ять, але OOM-killer не викликається. Документація systemd радить основним механізмом робити MemoryHigh=, а MemoryMax= тримати як останню лінію оборони.
На практиці це означає: надто низький MemoryHigh= проявиться як повільна служба, надто низький MemoryMax= — як убита. Як OOM-killer обирає жертву, розібрано в статті OOM-killer у Linux.
MemorySwapMax= задає абсолютний ліміт використання swap службою: значення 0 забороняє їй swap зовсім. Про swap — у статті налаштування swap у Linux.
Введення-виведення і кількість процесів
IOWeight= ділить пропускну здатність блочного введення-виведення між службами одного слайса пропорційно вазі, від 1 до 10000, за замовчуванням 100. TasksMax= обмежує кількість завдань у юніті; значення за замовчуванням береться з DefaultTasksMax= у systemd-system.conf. Ефект IOWeight= залежить від пристрою та налаштувань ядра, перевіряйте його виміром.
Як застосувати ліміти: drop-in за кроками
Найкращий спосіб — drop-in: невеликий файл у каталозі /etc/systemd/system/<служба>.service.d/, який доповнює юніт і не чіпає оригінал. Команда systemctl edit створює його сама, а systemctl edit --full відкриває повну копію юніта. Значення нижче приблизні: підберіть їх під свою службу і перевірте результат на тестовому екземплярі.
- Створіть файл limits.conf у каталозі drop-in або виконайте
sudo systemctl edit myapp.service. - Впишіть директиви в секцію [Service], як у прикладі нижче.
- Виконайте
sudo systemctl daemon-reload. - Перезапустіть службу:
sudo systemctl restart myapp.service.
# /etc/systemd/system/myapp.service.d/limits.conf
[Service]
CPUQuota=50%
CPUWeight=50
MemoryAccounting=1
MemoryHigh=512M
MemoryMax=768M
MemorySwapMax=0
IOAccounting=1
IOWeight=50
TasksMax=200MemoryAccounting=1 і IOAccounting=1 вмикають облік для systemd-cgtop. MemoryHigh= стоїть нижче за MemoryMax=, щоб спершу спрацювало уповільнення, і лише потім убивство.
Як подивитися результат
Чинні значення показує systemctl show, а навантаження по групах — systemd-cgtop: він впорядковує cgroup за навантаженням на процесор, пам’ять або диск. Дивитися результат варто одразу після перезапуску служби і ще раз під навантаженням. Дані неповні, поки для потрібних служб не ввімкнені MemoryAccounting=1 та IOAccounting=1.
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MemoryHigh -p MemoryMax -p MemorySwapMax -p TasksMax
systemctl show myapp.service | grep -i cpuquota
systemd-cgtopІм’я властивості квоти в systemctl show може відрізнятися від імені директиви, тому шукаємо за cpuquota. Якщо служба гальмує і після лімітів, переходьте до профілювання: профілювання продуктивності Linux.
Ліміти для звичайного користувача
Для сеансу звичайного користувача, не root, за замовчуванням делегуються лише контролери memory і pids; cpu та io потрібно делегувати окремо. Це з документації проєкту rootlesscontaine.rs, а не з man systemd, і значення за замовчуванням залежить від дистрибутива: дивіться /lib/systemd/system/user@.service.
# /etc/systemd/system/user@.service.d/delegate.conf
[Service]
Delegate=cpu cpuset io memory pidsПісля створення файла виконайте systemctl daemon-reload і перезайдіть у сеанс або перезавантажте сервер. Контролер cpuset вимагає systemd 244 або новішої версії. Для системних служб, які запускає root, делегування не потрібне: вистачає директив у юніті.
Часті запитання
CPUQuota=100% — це весь сервер?
Ні. 100% — це час одного ядра. Служба з CPUQuota=100% не займе понад одне ядро, а щоб дати їй два ядра, ставлять 200%.
З чого почати, якщо я не знаю, скільки пам’яті потрібно службі?
З вимірювання: увімкніть облік і поспостерігайте за службою в systemd-cgtop під звичайним навантаженням. Потім поставте MemoryHigh= із запасом над типовим значенням, а MemoryMax= — вище за нього.
Що робити клієнтам ZevsHost?
На спільному хостингу лімітами ресурсів керує панель, налаштовувати нічого не потрібно. На VPS і виділеному сервері службами керуєте ви: файл drop-in з лімітами пише сам власник сервера.