Requests и limits в Kubernetes: как правильно задавать ресурсы
Продвинутое3 минРедакция Innovialabs
Коротко
Requests нужны планировщику, чтобы найти узел для пода, а limits — ядру, чтобы ограничить потребление. Ставьте requests по реальному использованию, memory limit — с запасом, а CPU limit — осознанно, потому что он вызывает throttling.
Коротко: requests для планирования, limits для ограничения
Requests — это то, сколько ресурсов под «бронирует» на узле. Планировщик (scheduler) размещает под только туда, где сумма requests уже запущенных подов плюс новый не превышает доступную ёмкость узла. Фактическое потребление при этом не учитывается.
Limits — это потолок, который контролирует ядро Linux через cgroups. Поведение при его достижении зависит от ресурса:
- CPU — сжимаемый ресурс. Если контейнер упирается в CPU limit, его просто притормаживают (throttling). Процесс живёт, но отвечает медленнее.
- Память — несжимаемый ресурс. Превысил memory limit — ядро убивает процесс, под получает статус OOMKilled и перезапускается.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"
QoS-классы и почему они важны
Kubernetes присваивает каждому поду класс качества обслуживания. От него зависит, кого выселят первым, когда на узле заканчивается память.
| Класс | Условие | Приоритет при нехватке памяти |
|---|---|---|
| Guaranteed | У всех контейнеров requests = limits по CPU и памяти | Выселяется последним |
| Burstable | Задан хотя бы один request или limit, но не Guaranteed | Средний |
| BestEffort | Ни requests, ни limits не заданы | Выселяется первым |
Для баз данных и критичных сервисов имеет смысл Guaranteed. Для большинства stateless-приложений подходит Burstable. BestEffort в продакшене почти всегда ошибка.
OOMKilled и CPU throttling: как распознать
OOMKilled видно сразу:
kubectl describe pod <pod> | grep -A3 "Last State"
Причины обычно три: limit ниже реального пика, утечка памяти или runtime, который не знает о лимите контейнера (например, старые настройки JVM или неограниченный heap в Node.js).
CPU throttling коварнее: под не падает, просто растёт латентность. Признак — метрика container_cpu_cfs_throttled_periods_total растёт, хотя средняя загрузка CPU выглядит умеренной. Так бывает, когда приложение потребляет CPU всплесками: за короткий период квота исчерпывается, и потоки ждут следующего окна.
Метод подбора по реальным данным
- Начните с разумной оценки. Запустите сервис с умеренными requests и memory limit с запасом.
- Соберите метрики за типичный цикл нагрузки — минимум несколько дней, включая пиковые часы. Нужны Prometheus и cAdvisor/kubelet-метрики или аналог.
- CPU request ставьте около высокого перцентиля (p90–p95) реального потребления, а не по пику.
- Memory request — по типичному рабочему объёму, memory limit — выше наблюдаемого максимума с запасом на всплески.
- CPU limit решайте отдельно. Многие команды не задают его для latency-чувствительных сервисов, чтобы избежать throttling, и полагаются на requests для справедливого распределения. Если limit нужен (multi-tenant кластер, защита от «шумных соседей»), ставьте его заметно выше request.
- Проверьте с нагрузкой. Прогоните нагрузочный тест и посмотрите на throttling, OOM и латентность.
- Пересматривайте регулярно. После крупных релизов профиль потребления меняется.
Помогает Vertical Pod Autoscaler в режиме рекомендаций (updateMode: "Off"): он не меняет поды, а только подсказывает значения.
Частые ошибки
- Requests сильно завышены. Узлы «заняты» на бумаге, но реально простаивают — вы платите за пустую ёмкость.
- Requests занижены. Планировщик упаковывает слишком много подов на узел, они конкурируют за ресурсы, растёт риск выселения.
- Memory limit = request «впритык». Любой всплеск — OOMKilled.
- Одинаковые ресурсы для всех сервисов. Скопированный шаблон редко подходит и API, и воркеру очередей.
- HPA по CPU без корректных requests. Horizontal Pod Autoscaler считает утилизацию относительно request, поэтому неверный request ломает автоскейлинг.