Ліміти CPU, пам'яті та I/O: налаштування cgroups v2 у Linux

Пікове навантаження на одному вузлі обвалює сусідні служби, і простій б'є по виторгу. Ми налаштовуємо cgroups v2 під ключ: ізолюємо служби, задаємо ліміти та резерви, щоб один процес не забирав спільні ресурси. Витрати на інфраструктуру перестають зростати навмання, а розбір інцидентів прискорюється в рази.
Часті запитання

cgroups v2 та обмеження ресурсів: де ламається прод

У контейнерному середовищі без обмежень ресурсів вузол живе до першого сплеску. Служба, у якої немає стелі за пам'яттю, під час витоку зростає до межі ядра, витісняє сусідів у своп, і диспетчер починає витрачати час на звільнення сторінок замість корисної роботи.

Деградація під навантаженням поширюється на весь вузол — включно з процесами, які до винуватця не мають відношення. Це класичний шумний сусід: формально винен один сервіс, фактично перестає відповідати весь вузол.

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

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

cgroups v2 закриває це єдиним деревом ієрархії.

cpu.max задає верхню межу часу процесора, cpu.weight — частку за конкуренції, memory.max — жорстку межу із завершенням процесу, memory.high — м'який поріг із гальмуванням замість аварії, io.max — смугу на дисковому пристрої, memory.swap.max — заборону свопу для служб, яким він протипоказаний.

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

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

Вимірюваний ефект: затримки, простій, ізоляція служб

Поки у служб спільний пул ресурсів, один процес із витоком пам'яті або сплеском вводу-виводу забирає вузол повністю: сусіди втрачають відклик, ядро запускає аварійне звільнення пам'яті, деградує вже все.

З cgroups v2 кожен модуль живе у своїй гілці ієрархії і не може вийти за відведену частку — каскадна відмова як клас зникає.

Час відклику під навантаженням перестає бути лотереєю. Вага планувальника (cpu.weight) і стеля на процесорний час дають службі гарантовану частку навіть тоді, коли сусід завантажений, — їй не потрібно чекати звільнення ядра.

Це видно по хвосту розподілу: найповільніші запити перестають відставати від медіани в рази, а не в десятки разів.

Пам'ять і дискові операції обмежуємо м'яко. Поріг memory.high вмикає стискання сторінок замість жорсткого вбивства процесу, а io.max не дає резервному копіюванню або переіндексації задушити транзакційні запити.

Споживання стає передбачуваним — за ним можна рахувати щільність розміщення служб на вузлі без запасу «на око».

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

Режими обмеження: жорсткі ліміти, резерви, ваги

Жорстка стеля і вага вирішують різні завдання, і плутати їх — джерело аварій.

Жорсткий ліміт (cpu.max, memory.max) задає межу, за яку служба не вийде навіть за вільних ресурсів хоста: перевищення за пам'яттю призводить до спрацювання механізму витіснення, а не до деградації сусідів.

Гарантований резерв (memory.min, memory.low) працює навпаки — він захищає процеси від витіснення, коли на вузлі починається тиск пам'яті.

Пріоритет за вагами (cpu.weight, io.weight) нічого не обмежує: він вмикається лише за конкуренції і лише перерозподіляє звільнені ресурси. Тому ваги — правильний вибір для пакетних завдань, які мають поступатися онлайн-навантаженню, але можуть зайняти простій.

Окрема група — обмеження кількості процесів (pids.max) і стеля вводу-виводу (io.max): їх часто пропускають, а саме вони утримують вузол, коли служба вичерпала ліміт файлових дескрипторів або влаштувала лавину процесів.

Варіант Коли підходить Обмеження
Жорсткий ліміт за процесором і пам'яттю Сервіси з передбачуваним профілем споживання За неправильного розрахунку — періодичні перезапуски; піковий трафік не вкладеться
Гарантований резерв Критичні служби поруч із шумними сусідами Резервує пам'ять навіть простоюючим процесам, знижуючи загальну утилізацію вузла
Пріоритет за вагами Пакетна обробка, фонові завдання Не дає гарантій відклику: за повного завантаження вузла служба чекає
Обмеження кількості процесів Публічні та користувацькі сервіси Потребує підбору за реальною кількістю воркерів, інакше відмова замість деградації
Стеля вводу-виводу Бази даних, реплікація, вивантаження Обмеження на рівні пристрою, потрібен розрахунок за смугою диска

На практиці режими комбінують: критичній службі ставлять резерв і стелю одночасно, а фоновій — лише ваги, щоб вона не конкурувала за ресурс у години навантаження.

Як ми впроваджуємо ліміти: від аудиту до передачі

Ліміти, виставлені за документацією, а не за фактом, або душать сервіс, або не спрацьовують: робочий набір пам'яті виявляється втричі більшим за заявлений, а старт іде в тротлінг. Тому ми не змінюємо налаштування, доки не зняли реальні профілі споживання.

  1. Аудит споживання. Дивимося, де процеси живуть у дереві cgroups v2, що потрапляє в кореневий зріз і хто із сервісів на одній машині фактично з'їдає ресурси.
  2. Профілі навантаження. Знімаємо добовий і тижневий профіль: робочий набір пам'яті, пікові вікна, кількість потоків і дискових операцій, метрики тиску під час деградації сусідів.
  3. Проєктування політики. Розводимо м'який і жорсткий поріг за пам'яттю, задаємо вагу та стелю процесорного часу, ліміти вводу-виводу із запасом на сплеск, вибудовуємо ієрархію зрізів.
  4. Налаштування. Переносимо значення в конфігурацію зрізів, окремо обробляємо системні та прикладні сервіси, перевіряємо, що запуск і фонові завдання не впираються в стелю.
  5. Перевірка під навантаженням. Повторюємо піковий сценарій: тротлінг не ріже відклик, захист пам'яті спрацьовує передбачувано, сусідні сервіси не деградують.
  6. План відкату. Фіксуємо, які параметри і в якому порядку повертаються, де точка неповернення і як швидко зняти ліміт на конкретному сервісі.
  7. Передача в експлуатацію. Віддаємо регламент: значення, метрики та алерти на наближення до стелі, порядок зміни лімітів і відповідальних за перегляд профілів.

Що залишається на руки: конфігурації, схеми, регламенти

Підсумок робіт — не запущений сервер, а комплект, за яким ваша команда супроводу веде систему без нас і без усних пояснень.

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

  • Файли конфігурації з лімітами на кожен сервіс: cpu.max, memory.max, memory.high, io.max — зі значенням і коментарем, з яких замірів його отримано.
  • Схема розподілу ресурсів: ієрархія груп, що вкладено в що, де зарезервовано під системні процеси, де залишено запас на пікове навантаження.
  • Набори значень для контурів: окремі профілі для промислового, передпродуктивного середовища та локальної розробки, щоб тест не повторював бойові обмеження.
  • Регламент обслуговування: періодичність перевірок, пороги спрацювання memory.high, реакція на стійке зростання споживання, порядок зняття статистики за групами.
  • Інструкція для чергових: покрокові сценарії на типові події — група впирається в ліміт, процес знято через нестачу пам'яті, дискова підсистема гальмує через конкуренцію.
  • Порядок зміни лімітів: хто погоджує правку, як замірюється реальна потреба, яким чином значення доїжджає до вузлів і за який час відкочується.
  • Карта моніторингу: які показники груп виведено на панелі та на які оповіщення зав'язані ранні ознаки наближення до межі.

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

Кейс: вузол перестав падати під піковим навантаженням

До: відмова одного сервісу зупиняла вузол

Один сервіс із повільним витоком пам'яті поступово з'їдав її в сусідів по вузлу. Коли вільна пам'ять закінчувалася, ядро запускало аварійне завершення процесів, і падав не проблемний процес, а весь вузол разом із робочими сервісами.

Піки навантаження приходили кілька разів на тиждень, і щоразу відновлення займало години: ручний перезапуск, перевірка цілісності даних, повторний прогрів.

Що змінили

Перевели вузол на cgroups v2 з єдиною ієрархією замість розрізнених контролерів. Задали жорсткі межі за пам'яттю та кількістю процесів, відносні ваги за процесорним часом і квоти на дисковий ввід-вивід для кожного сервісу.

Ключове — групове завершення: у разі перевитрати пам'яті гаситься один проблемний процес, а не весь вузол. Поведінка тримається й коли сусіди одночасно виходять на пік.

Результат

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

Метрики знімали в Prometheus — за піків споживання впирається в задану межу, а не зростає до вичерпання. Клас відмов «витік одного сервісу валить сусідів» закрито, а межі живуть у конфігурації та переглядаються без перезбирання образів.

Що робити, якщо ліміти вже налаштовані в попередній версії cgroups?

Перенести політики «як є» не вийде: у попередній гілці кожен контролер живе у своїй ієрархії, а в новій дерево одне і параметри в ньому об'єднані.

cpu.shares поступається місцем cpu.weight з іншим діапазоном значень, квота й період зливаються в cpu.max, дискові обмеження — в io.max. Отже, перенесення — це перерахунок значень і явне ввімкнення потрібних контролерів у cgroup.subtree_control, а не заміна імен за шаблоном.

Зворотну сумісність дає змішаний режим: частина контролерів залишається в старій ієрархії, частина переходить у нову.

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

Міграція без простою будується на поступовому ввімкненні: спочатку один вузол, потім по одному решта. Поки вузол виведено з балансування, навантаження переїжджає на сусідів, а після перезапуску служби під керуванням systemd піднімаються вже в новому дереві. На кожному кроці під зміною один вузол, і відкат коштує одного перезапуску.

Перевірка після переходу — не лише «служба запустилася». Звіряють, що контролери активні та ліміти застосувалися до процесів, знімають метрики тиску memory.pressure, cpu.pressure, io.pressure — у попередній гілці їх просто немає.

Потім навантажувальний профіль, близький до робочого, підтверджує, що обмеження не ріжуть корисну роботу.

Часті технічні сумніви та заперечення

Перед стартом робіт із лімітів cgroups v2 питання в інженерів і керівників збігаються: чи не задушимо ми навантаження, наскільки оборотні налаштування і хто відповідає за поведінку системи після передачі. Нижче — прямі відповіді без загальних слів.

Чи не задушимо ми службу жорстким лімітом?

Ліміт процесорного часу не вимикає процес, а обмежує його частку: сплеск обробляється повільніше, але не падає. Небезпечна саме жорстка межа пам'яті.

Тому спочатку знімаємо профіль споживання під реальним навантаженням, ставимо ліміт із запасом і вмикаємо м'який поріг раніше за жорсткий — служба віддає кеш замість аварії.

Налаштування оборотні, якщо щось піде не так?

Параметри — це файли ієрархії, ми фіксуємо їхній стан до правок. Відкат — повернення кількох значень, перезавантаження не потрібне. Зміни зберігаємо декларативно в системі керування конфігурацією, тому будь-який крок відтворюваний і повторюється на іншій машині без ручної роботи.

Як зрозуміти, що вузьке місце не в лімітах?

До правок знімаємо метрики: час примусового обмеження, події пам'яті, статистику вводу-виводу, показники тиску на ресурси. Якщо обмеження не зростає, а затримки є — причина в диску, мережі або самому коді, і ліміти її не створили.

Чи сумісно це з контейнерами?

Контейнерні середовища та оркестратори працюють із cgroups v2 через системний драйвер, тому наші правила й ліміти з маніфестів діють узгоджено. Конфліктів немає: застосовується суворіше обмеження на рівні служби.

Що відбувається після здачі робіт?

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

Обговоримо задачу: з чого почнемо і що потрібно від вас

Щоб повернутися з оцінкою та планом, а не з уточнювальними питаннями, нам достатньо кількох вступних. Чим точніше опишете поточний стан, тим менше часу піде на розвідку і тим швидше почнуться роботи.

  • Що розміщено на вузлі: скільки служб, які критичні за затримкою, а які терплять чергу.
  • Як зараз задані обмеження: жорсткі ліміти, вагові коефіцієнти або процеси живуть без меж.
  • Симптоми: сплески затримок, аварійне завершення процесів, зростання черги дискових операцій.
  • Платформа: гілка ядра, дистрибутив, чи є єдине керування групами через systemd або оркестратор.
  • Обмеження: вікна обслуговування, вимоги до безперервності, хто видає доступи до вузлів.
  • Хто з вашого боку ухвалює рішення та перевіряє результат після змін.

Далі порядок простий: обговорюємо задачу, фіксуємо цільові показники за процесором, пам'яттю та дисковими операціями і погоджуємо план робіт за етапами з точками перевірки.

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

Формат співпраці обираєте ви: разові роботи за вузькою ділянкою або постійний супровід з оновленням профілів під зростання навантаження.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1393
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1368
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    815
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1139