Прямой ответ
ResourceQuota ограничивает суммарное потребление ресурсов в namespace. Без неё одна команда может занять всю ёмкость кластера, и остальные namespace останутся без возможности запускать поды.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: billing
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "50"
persistentvolumeclaims: "20"
requests.storage: 200Gi
services: "10"
configmaps: "100"
secrets: "100"
count/deployments.apps: "30"
count/jobs.batch: "100"
Важное следствие: если в namespace действует квота на CPU или память, каждый под обязан указывать requests и limits для соответствующего ресурса. Иначе создание отклоняется с ошибкой must specify .... Это одна из самых частых причин внезапного отказа деплоя после введения квот.
Текущее использование видно так:
kubectl describe quota team-quota -n billing
kubectl get resourcequota -n billing -o yaml
LimitRange задаёт значения по умолчанию и границы для отдельного контейнера:
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: billing
spec:
limits:
- type: Container
default: # лимит по умолчанию, если не задан
cpu: "1"
memory: 512Mi
defaultRequest: # requests по умолчанию
cpu: 100m
memory: 128Mi
min:
cpu: 50m
memory: 64Mi
max:
cpu: "4"
memory: 8Gi
LimitRange решает ровно ту проблему, которую создаёт квота: манифесты без явных ресурсов получают осмысленные значения вместо отказа. Но значения по умолчанию не заменяют расчёта — под с дефолтом может получить слишком мало памяти и падать, см. статью про requests и limits.
Практика: как проектировать квоты
1. Квотируйте и requests, и limits. Ограничение только limits не мешает занять всю ёмкость планировщика: поды с маленькими requests размещаются на узлах, пока они не закончатся.
2. Ограничивайте число объектов. Квота на pods, configmaps и secrets защищает от бесконечного создания Job и от утечек конфигурации. Для контроллеров используйте форму count/<ресурс>.<группа>, например count/deployments.apps.
3. Учитывайте хранилище. persistentvolumeclaims и requests.storage предотвращают ситуацию, когда namespace заказывает тома больше, чем есть в кластере.
4. Используйте scope selectors для разделения. Квота может применяться только к определённым подмножествам:
spec:
scopes: ["NotTerminating"]
hard:
pods: "40"
Селекторы Terminating/NotTerminating, BestEffort/NotBestEffort и PriorityClass позволяют разделить квоты для рабочих нагрузок и для разовых задач, чтобы миграция или бэкап не расходовали квоту продакшн-подов.
5. Квота не защищает узел. Она ограничивает namespace, но не гарантирует качество обслуживания: поды одного namespace продолжают конкурировать между собой. Изоляцию трафика решает NetworkPolicy — см. статью про изоляцию подов.
6. Квота сочетается с RBAC. Право создавать поды в namespace без права превышать квоту — обычная схема: разработчики могут деплоить, но не могут расширить лимиты. Настройка прав описана в статье про RBAC.
Рекомендации:
- Вводите LimitRange до ResourceQuota. Тогда существующие манифесты без ресурсов продолжат разворачиваться, а команда получит время указать явные значения.
- Считайте квоту от фактического потребления. Значение, взятое «с запасом в три раза», делает квоту бессмысленной; значение по текущему среднему блокирует рост.
- Оставьте запас на всплески. Квота по requests должна учитывать масштабирование: если HPA поднимает число подов вдвое, квота обязана это вместить.
- Мониторьте заполнение. Метрика использования квоты близка к 100 % задолго до инцидента; алерт на 85 % даёт время на реакцию.
- Не смешивайте команды в одном namespace. Квота применяется к namespace целиком, поэтому общий namespace означает общую квоту и взаимные блокировки.
Практический сценарий: деплой встал после введения квот
Контекст: в namespace добавили ResourceQuota с ограничением requests.cpu: 20. В тот же день все деплои начали падать с ошибкой создания пода.
- Диагностика.
kubectl describe podпоказал сообщениеforbidden: failed quota: must specify requests.cpu, requests.memory. То есть отказ связан не с нехваткой ёмкости, а с отсутствием полей в манифесте. - Проверка масштаба. Из 18 деплойментов явные ресурсы имели 5. Остальные развернулись раньше введения квоты и работали, но не могли пересоздаться.
- Причина. Квоту ввели без LimitRange и без предварительной проверки манифестов.
- Действия. (а) Добавили LimitRange со значениями по умолчанию, чтобы деплой восстановился немедленно; (б) для каждого сервиса проставили реальные requests по метрикам потребления, а не по умолчанию; (в) добавили в CI проверку наличия
resourcesв манифестах; (г) для разовых задач создали отдельную квоту со scopeNotTerminating, чтобы миграции не расходовали квоту рабочих подов; (д) настроили алерт на заполнение квоты выше 85 %. - Результат. Деплой восстановился; через неделю значения по умолчанию остались только у трёх некритичных сервисов, а квота начала выполнять свою функцию — при попытке занять больше выделенного запрос отклонялся с понятной причиной.
Вывод: ResourceQuota меняет требование к манифестам — ресурсы становятся обязательными. Вводить квоту нужно вместе с LimitRange и предварительной проверкой существующих манифестов, иначе деплой останавливается в момент введения, а не в момент исчерпания.
FAQ
Что происходит при попытке превысить квоту? Запрос отклоняется admission-контроллером, объект не создаётся. Уже работающие поды не затрагиваются: квота проверяется в момент создания или обновления.
Чем ResourceQuota отличается от LimitRange? ResourceQuota ограничивает сумму по namespace, LimitRange — значения отдельного контейнера и задаёт значения по умолчанию. Они решают разные задачи и обычно применяются вместе.
Влияет ли квота на уже запущенные поды? Нет. Квота учитывается при создании и обновлении объектов, поэтому изменение квоты в меньшую сторону не завершает работающие поды, но блокирует новые.
Можно ли задать разные квоты для разных команд в одном namespace? Нет, квота привязана к namespace. Разделение команд требует отдельных namespace, что совпадает и с требованиями RBAC, и с сетевой изоляцией.
Источники: Kubernetes docs (Resource Quotas, Limit Ranges) — ссылки в шапке.