Зачем нужны ResourceQuota и LimitRange: защита кластера от хаоса
Одно развертывание с ошибкой в конфигурации или агрессивное приложение может занять все доступные CPU и память узла, вызвав отказ других критичных сервисов. Эта проблема, известная как "noisy neighbor", угрожает стабильности всего кластера. ResourceQuota и LimitRange - стандартные инструменты Kubernetes для изоляции и гарантии доступности ресурсов.
ResourceQuota работает как жесткий бюджет для всего namespace, ограничивая суммарное потребление CPU, памяти и количество объектов. LimitRange задает правила для каждого pod или container, определяя допустимые запросы и лимиты ресурсов. Если провести аналогию с бизнес-процессами, ResourceQuota - это лимит трат по карте на отдел, а LimitRange - регламент оформления каждого заказа.
ResourceQuota: устанавливаем жесткий лимит ресурсов для namespace
ResourceQuota изолирует команды и проекты на уровне namespace, предотвращая исчерпание ресурсов одним приложением. Этот объект ограничивает два типа ресурсов: вычислительные (requests.cpu, limits.cpu, requests.memory, limits.memory) и количество объектов Kubernetes (pods, persistentvolumeclaims, services). Планировщик Kubernetes проверяет оставшуюся квоту перед созданием нового pod и отклоняет запрос, если его выполнение превысит установленные лимиты.
Готовый манифест ResourceQuota с пояснениями
Создайте файл resource-quota.yaml со следующим содержимым:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: production-team-a
spec:
hard:
pods: "10"
persistentvolumeclaims: "5"
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
Разберем секцию spec.hard построчно:
pods: "10"- максимальное количество pod в namespace production-team-a не может превышать 10.persistentvolumeclaims: "5"- ограничивает количество PersistentVolumeClaim (PVC) до 5.requests.cpu: "4"- суммарный запрос CPU от всех pod не должен превышать 4 ядра.requests.memory: "8Gi"- суммарный запрос памяти ограничен 8 ГиБ.limits.cpu: "8"- суммарный лимит CPU для всех контейнеров - 8 ядер.limits.memory: "16Gi"- суммарный лимит памяти - 16 ГиБ.
Примените манифест командой: kubectl apply -f resource-quota.yaml. Важно помнить, что квота применяется ко всем pod в namespace, включая системные, если они там находятся. Для системных namespace вроде kube-system квоты обычно не устанавливают.
Что происходит при нарушении квоты
Попытка создать pod, который превышает доступную квоту, будет немедленно отклонена API Kubernetes. Вы получите ошибку вида: Error from server (Forbidden): pods "web-app" is forbidden: exceeded quota: team-a-quota, requested: limits.cpu=2, limits.memory=2Gi, used: limits.cpu=7.5, limits.memory=15Gi, limited: limits.cpu=8, limits.memory=16Gi.
Чтобы проверить текущее использование квот, выполните команду kubectl describe resourcequota team-a-quota -n production-team-a. В выводе вы увидите разделы Used и Hard. Стратегия - устанавливать квоты с разумным запасом, но ниже общих лимитов кластера, чтобы сохранить ресурсы для других namespace. Для автоматизации этого процесса можно интегрировать создание квот в CI/CD пайплайн или инструменты IaC.
LimitRange: задаем разумные defaults и limits для pod и container
LimitRange решает проблему, когда разработчики не указывают requests и limits в манифестах. Это приводит к непредсказуемому планированию pod и потенциальному конфликту за ресурсы. Объект LimitRange, действующий внутри namespace, принудительно задает правила для каждого создаваемого container или pod.
Его основные функции: установка значений по умолчанию для limits (default) и запросов (defaultRequest), если они не указаны явно, а также определение минимальных (min) и максимальных (max) допустимых значений. LimitRange может быть типа Container, Pod или PersistentVolumeClaim.
Пример настройки LimitRange для container
Создайте файл limit-range.yaml:
apiVersion: v1
kind: LimitRange
metadata:
name: container-limits
namespace: production-team-a
spec:
limits:
- default:
cpu: "500m"
memory: "1Gi"
defaultRequest:
cpu: "200m"
memory: "512Mi"
type: Container
Секция limits содержит правила. Параметр type: Container указывает, что правило применяется к каждому контейнеру в pod. Поля default определяют лимиты CPU и памяти, которые будут автоматически проставлены контейнеру, если они не заданы в его манифесте. В нашем примере это 500 миллиядер CPU и 1 ГиБ памяти. Поля defaultRequest задают соответствующие запросы ресурсов - 200m CPU и 512 МиБ памяти.
Эффект простой: если разработчик создаст pod с контейнером, в манифесте которого нет секции resources, Kubernetes автоматически дополнит ее значениями из LimitRange. Это гарантирует, что все контейнеры будут иметь предсказуемые лимиты, что критично для корректной работы Deployment и планировщика.
Как LimitRange и ResourceQuota работают вместе
LimitRange и ResourceQuota - не альтернативы, а взаимодополняющие инструменты для комплексной защиты кластера. LimitRange осуществляет микроконтроль, задавая правила для каждого объекта. ResourceQuota выполняет макроконтроль, устанавливая потолок потребления для всего namespace.
Их совместная работа создает надежную схему: LimitRange гарантирует, что у каждого создаваемого pod есть адекватные limits и requests (даже проставленные по умолчанию). ResourceQuota, в свою очередь, следит, чтобы сумма всех limits и requests в namespace не превысила выделенный лимит. Идеальная практика - всегда использовать их в паре: LimitRange обеспечивает наличие limits, а ResourceQuota защищает от их суммарного превышения.
Практика: настраиваем управление ресурсами с нуля
Соберем знания в пошаговый сценарий для тестового окружения.
- Создайте namespace для демонстрации:
kubectl create ns resource-demo. - Примените манифест LimitRange, чтобы задать defaults для контейнеров (используйте YAML из примера выше, изменив namespace).
- Примените манифест ResourceQuota, чтобы установить жесткие квоты для этого namespace.
- Попробуйте развернуть простой pod без указания resources. Например, создайте файл
test-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: resource-demo
spec:
containers:
- name: nginx
image: nginx:alpine
После применения (kubectl apply -f test-pod.yaml) проверьте созданный pod: kubectl get pod test-pod -n resource-demo -o yaml. В секции resources контейнера вы увидите значения, автоматически подставленные из LimitRange (cpu: 500m, memory: 1Gi и соответствующие requests).
- Попробуйте превысить квоту. Создайте deployment, который запрашивает больше памяти, чем доступно по квоте, или попытайтесь создать 11-й pod при квоте в 10. Команда
kubectlвернет ошибку "exceeded quota".
Вывод: кластер защищен. Планировщик не допустит создания объектов, нарушающих установленные правила.
Частые ошибки и как их избежать
Опыт внедрения выявляет типичные проблемы:
- Слишком жесткие квоты блокируют обновления. Deployment не может выполнить rolling update, потому что для создания нового pod с новой версией нет квоты (старый pod еще не удален). Решение - всегда оставлять запас в 1-2 pod в квоте для операций обновления и перезапуска.
- Некорректная логика LimitRange. Установка значения
maxменьше, чемdefault, приведет к ошибкам при создании любого контейнера. Решение - проверять манифест на соответствие логике:min ≤ defaultRequest ≤ default ≤ max. - Игнорирование системных pod. Применение квот к namespace
kube-systemможет заблокировать запуск системных компонентов Kubernetes. Решение - не устанавливать ResourceQuota для системных namespace или явно исключать их из подсчета при планировании ресурсов кластера.
Всегда проверяйте текущую конфигурацию: kubectl describe limitrange -n <namespace> и kubectl describe resourcequota -n <namespace>. Для сложных кастомных ресурсов аналогичные принципы валидации и отладки описаны в гайде по полной диагностике CR.
Проверка актуальности: для каких версий Kubernetes это работает
Приведенные манифесты используют stable API (apiVersion: v1), который поддерживается много лет. Базовая логика работы ResourceQuota и LimitRange неизменна с версий Kubernetes примерно 1.10. Ключевые поля, такие как requests.cpu, limits.memory, pods, остаются стандартными и обратно совместимыми.
Для абсолютной уверенности в актуальности синтаксиса для вашей конкретной minor-версии (например, 1.28) сверьтесь с официальной документацией Kubernetes. Однако для production-задач, связанных с хранением данных, также стоит изучить современные практики управления PersistentVolume, количество которых также регулируется квотами.
Итог: стабильный кластер под контролем
ResourceQuota - главный инструмент для изоляции команд и проектов в Kubernetes. Он предотвращает исчерпание критичных ресурсов одним приложением, обеспечивая базовую стабильность и предсказуемость среды. LimitRange - обязательный инструмент для enforcement policy, гарантирующий, что у всех pod и контейнеров есть адекватные limits и requests, даже если разработчики их не указали.
Используя эти механизмы вместе, вы создаете защищенную и управляемую среду, где ресурсы распределены справедливо, а планировщик может эффективно размещать workload. Следующий практический шаг - внедрить эту схему в CI/CD пайплайн для автоматического создания namespace с предустановленными квотами и лимитами для каждого нового проекта или микросервиса, как часть общей стратегии оркестрации. Это снизит операционную нагрузку и исключит человеческий фактор при развертывании.