Kubernetes resource requests и limits: почему это важно
Requests и limits в Kubernetes: чём разница, что будет если не указать, OOMKilled, CPU throttling, QoS классы. Как прави
Привет, коллеги. Если вы разворачиваете что-то серьезнее демо-пода в продакшене, тема лимитов и запросов ресурсов — одна из первых, на которой вы обожжетесь. Разберем, как настроить это правильно, чтобы ваши поды не падали молча или не душили соседей по ноде.
База: Requests vs Limits
Requests (запросы) — это гарантированное количество ресурсов, которое планировщик Kubernetes резервирует для пода. Limits (лимиты) — жесткий потолок, выше которого под не может вырасти.
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
В этом примере под получает гарантию на 0.25 CPU и 256 МБ RAM. Он может использовать до 0.5 CPU, но память не может превысить 512 МБ.
Что будет, если не указать?
Если не указать requests, по умолчанию они считаются равными limits. Если не указаны и limits, то для пода нет ограничений. Это плохо по двум причинам:
- Память (OOMKilled): Без
limitsна memory под может съесть всю доступную память на ноде. Когда нода исчерпает память, kubelet начнет убивать процессы, чтобы спасти систему. Ваш под получит статусOOMKilled. Безrequestsпланировщик может разместить ваш memory-прожорливый под на ноде, где уже не хватит памяти для его работы. - CPU (Throttling): Без
limitsна CPU под может пытаться использовать все ядра. Безrequestsего могут поставить на загруженную ноду, где он не получит и минимума. Но ключевая проблема — throttling (подавление). Если указанlimits, контейнер не может его превысить. При попытке это сделать, ядро Linux (cgroups) ограничит контейнер, заставив его ждать. В мониторинге это выглядит как высокийthrottlingи просадки в производительности при скачках нагрузки, хотя общая загрузка CPU ноды может быть низкой.
QoS-классы: последствия ваших настроек
В зависимости от того, как вы задали requests и limits, Kubernetes присваивает поду один из классов качества обслуживания (QoS):
- Guaranteed: Самый высокий приоритет. Заданы
limitsиrequestsдля всех контейнеров, и они равны для каждого ресурса. Или заданы толькоlimits(тогда requests устанавливаются равными им). Эти поды убиваются в последнюю очередь при нехватке ресурсов. - Burstable: Заданы
requestsиlimits, но они не равны. Или заданыrequestsбезlimits. У этих подов есть гарантированный минимум, но они могут бустить. Убиваются вторыми. - BestEffort: Ничего не задано. Нет гарантий, первыми на вылет при нехватке памяти.
Стратегия проста: для критичных воркеров стремитесь к Guaranteed. Это дает предсказуемое поведение.
Как выставлять для реальных воркеров: практика
-
Начинайте с мониторинга. Запустите без limits (но с запасом по memory requests!) и соберите метрики потребления (CPU, memory) за несколько дней. Используйте инструменты вроде
kubectl top pod, Prometheus с Grafana. -
Для memory:
limitsдолжен быть выше пикового потребления с запасом 15-25%.requestsможно ставить ближе к среднему потреблению, но не ниже. Никогда не ставьте memory limit ниже memory request. Это путь к немедленному OOMKilled. -
Для CPU: Здесь гибче. Если приложение чувствительно к задержкам (база данных, RPC-сервис), ставьте
limitsс хорошим запасом от пиков, чтобы избежать throttling. Для фоновых задач или веб-сервисов с горизонтальным масштабированием можно сделатьlimitsв 1.5-2 раза вышеrequests. Это позволит использовать свободные ресурсы ноды, но цена — возможный throttling. -
Шаблоны для разных нагрузок:
- Python/Go веб-сервис: CPU requests/limits близки, memory limit с запасом.
resources: requests: memory: "300Mi" cpu: "200m" limits: memory: "400Mi" cpu: "300m"- Java-сервис с JVM: Memory requests и limits обязательно равны и соответствуют Xmx/Xms. Это предотвратит лишние аллокации и OOM.
resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "1Gi" cpu: "800m"
Типичные грабли
- Копипаст конфигов. Настройки, идеальные для Python-сервиса, убьют Java-приложение.
- Игнорирование CPU throttling. Приложение “тормозит”, хотя метрики CPU показывают неполную загрузку — первым делом смотрите
container_cpu_cfs_throttled_periods_total. - Заниженные memory limits для JVM. JVM, не видя лимита, может выставить слишком большой heap и быть убитой OOMKiller’ом, когда попытается его использовать.
- Не учитывать memory не-heap. Память контейнера — это не только heap JVM или RSS процесса. Добавляйте запас на shared libraries, мета-данные, кэши.
Грамотные requests и limits — это не оптимизация, а необходимое условие стабильности кластера. Потратьте время на настройку один раз, чтобы не тушить пожары каждую неделю.