Калькулятор ресурсів Docker та Kubernetes: requests/limits CPU, пам’ять, OOMKilled та щільність нод

Ефективне керування інфраструктурою контейнеризації вимагає точного розуміння того, як ядра Linux ізолюють процеси та виділяють обчислювальну потужність. Неправильно налаштовані параметри requests та limits у Docker і Kubernetes неминуче призводять до деградації мікросервісів, аварійних зупинок процесів або марного витрачання хмарного бюджету. За даними експертних досліджень Cloud Native Computing Foundation (CNCF), у неоптимізованих кластерах показник надлишкового резервування (overprovisioning) досягає 30-50% придбаних ресурсів інфраструктури.

Цей матеріал створено як технічний посібник та супровідний опис до інтерактивного інструменту - калькулятора ресурсів Docker та Kubernetes, який ви можете використовувати безпосередньо у фреймі вище для автоматизації розрахунків CPU (millicores) та оперативної пам’яті (RAM).

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

Чому правильний розподіл ресурсів у docker та kubernetes є критичним для продакшну

Архітектура мікросервісів передбачає розгортання десятків або сотень ізольованих компонентів на спільній апаратній базі. Кожен контейнер функціонує в межах обмежень ядра Linux, відомих як cgroups (control groups). Якщо інженер не задає явно апетит додатків до ресурсів, оркестратор ухвалює рішення на основі дефолтних політик кластера, що найчастіше призводить до двох крайнощів: голодування важливих сервісів або повного вичерпання ємності ноди.

Ключовим механізмом керування стабільністю в Kubernetes є класи якості обслуговування - Pod QoS Classes. Оркестратор автоматично призначає один із трьох класів (Guaranteed, Burstable, BestEffort) на основі взаємозв’язку між параметрами запитів (requests) та лімітів (limits) для CPU й пам’яті.

Клас QoSУмови конфігураціїПоведінка при нестачі ресурсів (Eviction / OOM)
GuaranteedДля кожного контейнера limits.cpu == requests.cpu та limits.memory == requests.memory (і обидва параметри задані).Найвищий пріоритет. Процеси витіщаються в останню чергу при критичному перевантаженні ноди.
BurstableХоча б один контейнер має відмінності між requests та limits, або задані лише requests/limits окремо.Середній пріоритет. Контейнери можуть споживати вільні ресурси ноди вище requests, але першими потрапляють під витіснення при вичерпанні пам’яті.
BestEffortДля жодного контейнера не задано ані requests, ані limits (ні для CPU, ні для RAM).Найнижчий пріоритет. Будь-який брак ресурсів на ноді призводить до негайного завершення цих подів.

Застосування класу BestEffort у продакшн-середовищі є критичною архітектурною помилкою. Такі додатки не мають жодних гарантій виконання і знищуються першими при локальних піках навантаження на ноду.

Аналіз головних загроз: що таке cpu throttling та oomkilled (exit code 137)

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

Помилка oomkilled та exit code 137

OOMKilled (Out Of Memory) виникає тоді, коли процес усередині контейнера намагається виділити обсяг оперативної пам’яті, що перевищує встановлений параметр limits.memory. У цей момент втручається підсистема ядра Linux oom_killer, яка примусово завершує процес за допомогою сигналу SIGKILL. У логах Kubernetes це фіксується як завершення з Exit Code 137 (де 128 + 9 [номер сигналу SIGKILL] = 137).

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

Механіка cpu throttling через cfs scheduler

Обмеження процесора в контейнерах базуються на алгоритмі планувальника Linux - Completely Fair Scheduler (CFS), а саме на параметрах cpu.cfs_period_us (стандартний період, зазвичай 100 000 мікросекунд або 100мс) та cpu.cfs_quota_us (дозволений час процесора протягом цього періоду).

Якщо мікросервіс із лімітом в 1 ядро (1000m) виконує інтенсивні обчислення у багатопотоковому середовищі й вичерпує свою квоту за перші 20 мілісекунд періоду, ядро зупиняє (throttle) виконання потоків на решту 80 мілісекунд. Це викликає різку затримку латентності (p99 latency spikes), хоча загальне середнє використання CPU за метриками може здаватися невисоким.

Симптом проблемиТехнічна причинаДіагностична метрикаМетод усунення
Раптове падіння подів з Exit Code 137Вичерпання ліміту оперативної пам’яті (OOM)container_memory_working_set_bytes досягає лімітуЗбільшити limits.memory або оптимізувати використання пам’яті додатком.
Зростання часу відповіді (latency) при низькому загальному CPUОбмеження квоти планувальника (CPU Throttling)container_cpu_cfs_throttled_seconds_totalЗбільшити limits.cpu або взагалі прибрати ліміт CPU для некритичних фонових сервісів.
Под не запускається (Pending)Брак вільних ресурсів на жодній ноді кластераПовідомлення планивальника: Insufficient cpu / memoryЗменшити requests подів або додати нові робочі ноди до кластера.

Як користуватися інтерактивним калькулятором ресурсів

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

  1. Визначення типу навантаження (Workload Profile): Оберіть специфіку мікросервісу - CPU-інтенсивний (обробка відео, шифрування), I/O-інтенсивний (API-шлюзи, проксі) чи пам’ятко-ємний (кешування в пам’яті, індексація пошуку).
  2. Введення базових метрик споживання: Вкажіть середнє та пікове (p95/p99) споживання ядер та оперативної пам’яті, отримане з систем моніторингу (Prometheus / Grafana).
  3. Конфігурація коефіцієнта безпеки (Safety Margin): Задайте відсоток запасу (наприклад, +20% для requests та +50% для limits), щоб покрити непередбачувані піки без загрози OOMKilled.
  4. Розрахунок щільності нод: Введіть характеристики хмарного інстансу (наприклад, AWS c6i.2xlarge: 8 vCPU, 16 GiB RAM) та залишкові системні резерви.
  5. Експорт конфігурації: Скопіюйте згенерований YAML-фрагмент для секції resources: вашого Kubernetes Deployment маніфесту.

Математичні моделі та формули розрахунку: від millicores до кількості нод

В основі роботи калькулятора лежать чіткі інженерні формули, які враховують обмеження платформи Kubernetes та апаратну архітектуру вузлів.

Розрахунок cpu requests та limits

Одиницею виміру CPU в Kubernetes є millicores (m), де 1000m дорівнює одному фізичному ядру процесора (або одному vCPU/Thread у віртуалізованому середовищі). Базова формула розрахунку запиту:

CPU_Request = Peak_CPU_Usage × Safety_Coefficient

Для лімітів CPU у багатопотокових додатках на базі Java, Node.js чи Go рекомендується встановлювати ліміт у 1.5-2 рази вищим за request або не встановлювати його зовсім (Burstable клас), щоб уникнути мікросекундного троттлінгу.

Розрахунок пам’яті та уникнення oom

Пам’ять вимірюється в байтах з використанням двійкових суфіксів (Mi - мебібайти, Gi - гексібайти). Формула ліміту пам’яті:

Memory_Limit = Working_Set_Max + Safe_Buffer (мінімум 20-30%)

Розрахунок щільності нод (node density) та системних резервів

Щоб дізнатися, скільки подів реально може поміститися на одній ноді без ризику падіння кластера, необхідно врахувати системні резерви операційної системи та демонів Kubernetes (kubelet, container runtime, kube-proxy, засоби моніторингу). Вони визначаються параметрами kube-reserved та system-reserved.

Доступна ємність ноди розраховується за формулою:

Usable_Node_Capacity = Total_Node_Capacity − (Kube_Reserved + System_Reserved + Eviction_Threshold)

Параметр резервуПризначення в архітектурі K8sТипові значення для ноди з 8 vCPU / 32GB RAM
kube-reservedРезерв для компонентів Kubernetes (kubelet, kube-proxy, container runtime).~100m–200m CPU, 512Mi–1Gi RAM
system-reservedРезерв для процесів ОС Linux (systemd, sshd, kernel tasks, logrotate).~100m–200m CPU, 512Mi–1Gi RAM
Eviction ThresholdПоріг витіснення подів при нестачі ресурсів (наприклад, memory.availableФіксований обсяг (наприклад, 200Mi - 500Mi RAM)

Практичні кейси та сценарії оптимізації мікросервісів

Розглянемо ілюстративний кейс середньої компанії з розробки програмного забезпечення, яка керувала кластером із 40 нод в AWS (тип t3.xlarge: 4 vCPU, 16 GiB RAM). До міграції на методологію точного розрахунку через калькулятор більшість мікросервісів працювали у режимі BestEffort або з завищеними limits «на око».

  1. Проблема: Часті аварійні перезапуски сервісу аутентифікації вранці (Exit Code 137) через те, що ліміт пам’яті був жорстко обмежений на рівні 512Mi, хоча кеш сесій у години пік вимагав до 750Mi.
  2. Рішення калькулятора: Аналіз метрик показав робочий набір пам’яті 600Mi з піками до 800Mi. Встановлено requests.memory: 768Mi та limits.memory: 1024Mi, перевівши під у клас Burstable. Задано точний ліміт CPU без жорсткого обмеження періоду для запобігання троттлінгу.
  3. Результат: Кількість інцидентів з падінням сервісу скоротилася до нуля. Завдяки оптимізації щільності подів на нодах, загальну кількість інстансів у кластері вдалося скоротити з 40 до 28, що знищило надлишкове резервування та зменшило місячний рахунок за хмарну інфраструктуру на 30%.

Поради експертів та найкращі практики (best practices) для devops

Впровадження стандартизованого розрахунку ресурсів вимагає дотримання перевірених інженерних практик:

  • Ніколи не залишайте деплойменти без requests: Відсутність requests переводить под у клас BestEffort, що робить його вразливим при будь-яких коливаннях навантаження на ноді.
  • Використовуйте Vertical Pod Autoscaler (VPA): Після первинного розрахунку за допомогою калькулятора підключіть VPA в режимі рекомендацій (Recommendation Mode), щоб автоматично коригувати requests/limits на основі історичних даних реального використання.
  • Розрізняйте стабільні бази даних та stateless сервіси: Для stateful додатків (PostgreSQL, Redis, Kafka) критично важливо встановлювати requests == limits (Guaranteed QoS), щоб гарантувати стабільну продуктивність диску та пам’яті без ризику витіснення.
  • Моніторьте метрики throttling: Налаштуйте алерти в Prometheus для відстеження росту споживання квот процесора через алерти на базі rate(container_cpu_cfs_throttled_seconds_total).

Часті запитання (faq)

Що робити, якщо додаток споживає пам’ять піками, які перевищують ліміт, але потім стабілізується?

Якщо піки короткочасні, а ліміт жорсткий, процес неминуче отримає OOMKilled (Exit Code 137). У такому разі необхідно або збільшити limits.memory до рівня пікового споживання, або оптимізувати внутрішній збір сміття (Garbage Collection) умовної мови програмування (наприклад, зменшити поріг спрацювання GC у Go чи Node.js).

Як впливає cgroups v2 на точність виконання cpu limits в сучасних дистрибутивах linux?

Версія cgroups v2 вирішує проблему «CPU burstiness» та надмірного троттлінгу, яка була притаманна cgroups v1. Новий механізм розподілу на основі дерев та ваг працює плавніше, дозволяючи потокам ефективніше використовувати вільні мікросекунди процесора без жорстких штучних пауз періоду CFS.

Чому метрики prometheus показують високе використання cpu, хоча ліміти ще не досягнуті?

Метрики зазвичай відображають реальне споживання процесорного часу (usage), тоді як ліміти контролюють квоту за розкладом планувальника. Високе споживання за відсутності throttling означає, що додаток ефективно використовує виділені йому ядра, але ще не вичерпав дозволений ліміт.

Як розрахувати резерв kube-reserved для кластерів із різною кількістю ядер (vcpu)?

Існують стандартні рекомендації Kubernetes: для першого ядра виділяється більший відсоток (наприклад, 60m CPU та 320Mi RAM), для наступних 4 ядер по 10m CPU / 64Mi RAM, і далі пропорційно масштабуванню ємності вузла.

Чи варто встановлювати ліміти cpu для критичних баз даних у kubernetes?

Для високопродуктивних баз даних експерти рекомендують або встановлювати щедрі ліміти CPU (або робити їх рівними requests), або взагалі не задавати limits.cpu, дозволяючи базі використовувати вільні ресурси ноди під час складних запитів без ризику затримки через throttling.

Яка різниця між пріоритетами подів (priorityclass) та qos класами при вимушеному витісненні (eviction)?

PriorityClass визначає черговість планування та витіснення подів на рівні планувальника при нестачі місця в кластері (хто кого замінить у черзі). QoS класи керують реакцією ядра Linux на рівні конкретної ноди при фізичній нестачі пам’яті чи процесора.

Як перевірити поточні ліміти та використання прямо з cli за допомогою kubectl?

Використовуйте команду kubectl top pods --all-namespaces для перегляду поточного споживання в режимі реального часу, а також kubectl describe pod [pod-name] для перевірки сконфігурованих запитів та лімітів у маніфесті.

Чи допоможе калькулятор при використанні spot-інстансів (aws ec2 spot)?

Так, калькулятор корисний для максимізації щільності подів на Spot-інстансах. Оскільки Spot-ноди можуть бути раптово відключені хмарним провайдером, щільне та збалансоване пакування подів дозволяє ефективно переносити навантаження на резервні ноди без марної трати коштів.

Чому виникає помилка exit code 137 навіть за відсутності видимих витоків пам’яті?

Окрім витоків, причиною може бути спільне використання пам’яті процесами контейнера (наприклад, кешування в базі даних або робота JVM-додатків, де купа пам’яті виділяється під кеш без урахування лімітів cgroups, що призводить до перевищення загального ліміту подів).

Як правильно налаштувати horizontal pod autoscaler (hpa) з огляду на задані requests?

HPA розраховує потребу в масштабуванні на основі відсотка використання саме requests, а не limits. Тому точний розрахунок початкових requests у нашому калькуляторі є критичною умовою для коректної та стабільної роботи автоскасування.

FAQ

Чи можна використовувати калькулятор для серверів з гетерогенними апаратними характеристиками, де ноди мають різну кількість CPU та RAM?+

Так, але розрахунок щільності доведеться виконувати окремо для кожного типу вузлів у кластері. Оскільки калькулятор оперує базовими параметрами інстансу, ви можете ввести характеристики конкретної ноди (наприклад, для GPU-вузлів або пам'ятко-ємних інстансів), щоб отримати точну ємність. Для гетерогенних середовищ найкращою практикою є використання міток (node selectors) та таднтів (taints and tolerations), щоб таргетно розгортати мікросервіси на відповідних за потужністю нодах без ризику некоректного розподілу ресурсів планувальником.

Що робити, якщо мікросервіс працює в режимі черги завдань (Worker) і споживання пам'яті різко зростає при отриманні великого пакета даних?+

Для воркерів із піковим навантаженням категорично не рекомендується використовувати жорсткі ліміти пам'яті, що дорівнюють середньому значенню. Необхідно закладати коефіцієнт безпеки щонайменше 50–100% або налаштовувати пакетну обробку даних меншими порціями (batching). Якщо пакет неможливо розділити, збільште `limits.memory` з урахуванням найтяжчого сценарію, інакше воркер гарантовано отримає `OOMKilled` під час обробки масивного завдання. Також варто перевірити наявність обмежень на максимальну кількість одночасних завдань, які воркер бере в роботу з черги.

Як впливає вибір базового образу контейнера (наприклад, Alpine Linux проти Ubuntu) на реальне споживання оперативної пам'яті та встановлення лімітів?+

Базовий образ суттєво впливає на накладні витрати пам'яті на старті контейнера. Мінімалістичні образи на базі Alpine або Distroless споживають мінімум системної пам'яті (часто менше 10–20 MiB), тоді як повноцінні дистрибутиви на кшталт Ubuntu чи Debian можуть виділяти більше ресурсів під системні бібліотеки та утиліти. Хоча це зазвичай не критично для великих додатків, для легких мікросервісів (наприклад, на Go чи Rust) різниця в базовому образі може становити значну частку від загального споживання, що слід враховувати при мінімізації `requests.memory`.

Чи потрібно враховувати ресурси sidecar-контейнерів (наприклад, Service Mesh, проксі або агенти моніторингу) при розрахунку лімітів для основного додатка?+

Так, обов'язково. Усі sidecar-контейнери, що працюють усередині того ж пода, споживають ресурси спільно з основним додатком і сумуються для визначення загального класу QoS пода. Якщо ви забудете додати CPU та RAM для службових контейнерів (наприклад, Envoy-проксі чи агентів логування), сумарне споживання пода перевищить очікування, що призведе до неочікуваного троттлінгу або витіснення. Завжди додавайте до розрахунку додатково 50–100m CPU та 64–128Mi RAM на кожен допоміжний sidecar-контейнер.

Чому після оновлення версії мови програмування (наприклад, перехід з Java 11 на Java 17 або Node.js 16 на Node.js 20) мікросервіс починає споживати менше пам'яті?+

Нові версії рантаймів та віртуальних машин часто мають оптимізовані алгоритми збирання сміття (Garbage Collection) та ефективніше керують внутрішніми структурами даних. Наприклад, сучасні версії JVM агресивніше повертають невикористану пам'ять операційній системі замість того, щоб утримувати її в купі (heap). Це дозволяє безпечно знизити раніше завищені ліміти пам'яті та підвищити щільність розміщення інших подів на ноді без ризику деградації продуктивності.

Як перевірити, чи дійсно ваш мікросервіс страждає від CPU Throttling, якщо в графіках Prometheus немає очевидних піків затримки?+

Необхідно проаналізувати спеціальну метрику планувальника `container_cpu_cfs_throttled_periods_total` у співвідношенні до загальної кількості періодів `container_cpu_cfs_periods_total`. Якщо відсоток троттлінг-періодів перевищує 10–15%, це свідчить про наявність проблеми, навіть якщо поточна латентність здається прийнятною. У такому разі варто перевірити багатопотоковість застосунку або скоригувати ліміти процесора, оскільки додаток штучно стримується ядром операційної системи.

Чи можна використовувати Vertical Pod Autoscaler спільно з Horizontal Pod Autoscaler для одного і того ж деплойменту?+

Користуватися обома автоскалерами одночасно для метрик CPU та пам'яті не рекомендується, оскільки вони конфліктуватимуть між собою. HPA масштабує кількість копій (подік), спираючись на використання `requests`, тоді як VPA змінює самі розміри `requests` і `limits` усередині пода. Якщо VPA збільшить ресурси існуючого пода, HPA може хибно інтерпретувати це як зниження навантаження на копію і почати зменшувати їхню кількість. Стандартною практикою є використання VPA в режимі рекомендацій для початкового налаштування калькулятора, а HPA — для динамічного продакшн-масштабування.

Які приховані ризики виникають, якщо встановити для всіх мікросервісів у кластері однакові універсальні ліміти «про всяк випадок»?+

Універсальні ліміти призводять або до серйозного надлишкового резервування (overprovisioning), через що хмарний бюджет витрачається на порожні ресурси, або до голодування складніших сервісів. Легкі сервіси марно блокують ємність ноди, яку могли б зайняти інші поди, а ресурсомісткі додатки все одно отримують OOMKilled або Throttling через невідповідність специфічному профілю навантаження. Розрахунок має базуватися на реальних метриках конкретного сервісу, а не на середньостатистичних шаблонах.

Як налаштування limits впливає на час швидкого холодного старту (Cold Start) безсерверних або контейнеризованих додатків, що масштабуються з нуля?+

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

Чому при налаштуванні Guaranteed QoS класу іноді виникає помилка валідації маніфесту в Kubernetes API?+

Помилка виникає тоді, коли параметри задані не повністю для всіх контейнерів у поді. Клас Guaranteed вимагає, щоб абсолютні значення `requests` та `limits` для CPU і пам'яті були явно прописані для *кожного* контейнера без винятку, і при цьому `requests` дорівнював `limits`. Якщо хоча б в одному контейнері забути вказати ліміт пам'яті або залишити порожнім поле CPU, Kubernetes автоматично присвоїть поду клас Burstable або BestEffort, видавши попередження або відхиливши маніфест залежно від політик валідації кластера.