LimitRange, ResourceQuota и VPA: управление ресурсами на уровне namespace
Published: 2026-04-21
Kubernetes по умолчанию не ограничивает потребление ресурсов. Неправильно настроенное приложение может съесть всю память ноды и вытеснить всё остальное. Три инструмента предотвращают это: LimitRange (значения по умолчанию для контейнеров), ResourceQuota (лимиты на namespace) и VPA (рекомендации по right-sizing).
LimitRange
Подставляет requests/limits по умолчанию в контейнеры без явных значений и ограничивает максимум для одного контейнера:
yamlapiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: dev
spec:
limits:
- type: Container
default: # применяется, если limits: не задан
cpu: "500m"
memory: "512Mi"
defaultRequest: # применяется, если requests: не задан
cpu: "100m"
memory: "128Mi"
max: # контейнер не может превысить
cpu: "4"
memory: "4Gi"
min: # контейнер должен запросить минимум
cpu: "50m"
memory: "64Mi"
- type: Pod
max:
cpu: "8"
memory: "8Gi"
- type: PersistentVolumeClaim
max:
storage: "50Gi"
Без этого под без блока resources: планируется с нулевыми requests — планировщик его не учитывает, на нодах возникает overcommit, приложения убивает OOM killer.
Проверка LimitRange:
bashkubectl describe limitrange default-limits -n dev
ResourceQuota
Ограничивает суммарное потребление ресурсов в namespace:
yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dev
spec:
hard:
requests.cpu: "20"
requests.memory: "20Gi"
limits.cpu: "40"
limits.memory: "40Gi"
pods: "100"
services: "30"
services.loadbalancers: "0"
persistentvolumeclaims: "20"
requests.storage: "200Gi"
count/secrets: "100"
count/configmaps: "100"
services.loadbalancers: "0" не даёт разработчикам случайно создать LoadBalancer Service (что в облаке влечёт создание реального балансировщика и дополнительные расходы).
При наличии ResourceQuota в namespace каждый под обязан иметь resource requests. LimitRange автоматически это обеспечивает.
Квоты по окружениям
yaml# namespace dev — щедро, быстрые итерации
limits.cpu: "40"
limits.memory: "40Gi"
# namespace test — приближено к production, но поменьше
limits.cpu: "20"
limits.memory: "20Gi"
# namespace sre — минимум, только для инструментов
limits.cpu: "8"
limits.memory: "8Gi"
Хранятся в spoke/namespaces/ как отдельные YAML-файлы. Kustomize-патчи переопределяют значения для каждого окружения.
VPA для рекомендаций по right-sizing
VPA в режиме Off вычисляет рекомендации без изменения подов:
yamlapiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: elasticsearch
namespace: observability
spec:
targetRef:
apiVersion: apps/v1
kind: StatefulSet
name: elasticsearch-master
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: elasticsearch
minAllowed:
cpu: 100m
memory: 1Gi
maxAllowed:
cpu: 8
memory: 16Gi
Через несколько дней трафика проверить рекомендации:
bashkubectl describe vpa elasticsearch -n observability
Пример вывода:
Container Recommendations:
Container Name: elasticsearch
Lower Bound:
cpu: 216m
memory: 2Gi
Target:
cpu: 500m
memory: 3Gi
Upper Bound:
cpu: 2
memory: 5Gi
Использовать Target для обновления HelmRelease. Не использовать updateMode: Auto для stateful-приложений — чтобы применить изменения, VPA вытесняет поды, а это простой.
Проверка использования квоты
bashkubectl describe resourcequota -n dev
Name: dev-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
limits.cpu 12500m 40
limits.memory 14Gi 40Gi
pods 47 100
requests.cpu 3200m 20
requests.memory 5Gi 20Gi
services.loadbalancers 0 0
Если deployment падает с exceeded quota, этот вывод сразу показывает, какой ресурс исчерпан.
Диагностика ошибок квоты
Под не создаётся: pods "name" is forbidden: exceeded quota
bashkubectl describe resourcequota -n <namespace>
# Найти, какой ресурс достиг лимита
# Увеличить квоту или удалить неиспользуемые рабочие нагрузки
must specify limits.memory — ResourceQuota есть, LimitRange отсутствует. Добавить LimitRange со значениями по умолчанию.
Helm upgrade падает с ошибкой квоты — при rolling update новые поды создаются до удаления старых. Временно увеличить квоту по числу подов или ускорить завершение через terminationGracePeriodSeconds.
Priority classes
Для критических системных рабочих нагрузок, которые должны переживать eviction:
yamlapiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical-addons
value: 100000000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Для критических аддонов кластера"
Применить к подам:
yamlspec:
priorityClassName: critical-addons
Поды с более высоким приоритетом вытесняются последними при нехватке памяти на ноде.