Learning Platform
Глоссарий Troubleshooting
Урок 14.04 · 22 мин
Продвинутый
ResourceQuotaadmissionnamespacescopemulti-tenancy

ResourceQuota: namespace-level caps

ResourceQuota — namespaced объект (v1/ResourceQuota), который через admission controller ResourceQuota устанавливает жёсткие верхние границы на суммарные resources внутри namespace. В отличие от LimitRange (который про per-Pod policy), ResourceQuota — про per-namespace lifetime budget: «вся ваша команда не может занять больше 100 CPU и 200Gi памяти в этом namespace, ни на минуту, ни на секунду». Это основной инструмент multi-tenancy и cost-control в Kubernetes-кластерах с несколькими командами.


Формулы планирования ёмкости Kafka

Что можно квотировать

Возможные ключи в spec.hard:

Категории ресурсов в ResourceQuota
ComputeЛимиты на суммарные requests и limits compute resources всех Pods. Может квотироваться по CPU, memory, ephemeral-storage и extended resources типа nvidia.com/gpu.
StorageСуммарный storage requested через PVC, количество PVC, storage по storageClass-у. Удобно для разделения tiers (SSD vs HDD) внутри одного namespace.
Object countsПросто количество объектов: Pods, Services, ConfigMaps, Secrets, LoadBalancers, NodePorts. Защищает API от спама и контролирует overhead etcd.
Extended resourcesGPU, FPGA, custom devices — всё, что регистрируется на nodes как extended resource, можно квотировать. Только requests, не limits.

Основные ключи:

spec:
  hard:
    # compute
    requests.cpu: "10"           # сумма requests.cpu всех Pods
    requests.memory: 20Gi
    limits.cpu: "20"             # сумма limits.cpu
    limits.memory: 40Gi

    # ephemeral
    requests.ephemeral-storage: 50Gi
    limits.ephemeral-storage: 100Gi

    # storage
    requests.storage: 500Gi      # сумма storage в PVC
    persistentvolumeclaims: "10" # max количество PVC
    fast.storageclass.storage.k8s.io/requests.storage: 100Gi   # квота по конкретному StorageClass

    # object counts
    pods: "50"
    services: "20"
    services.loadbalancers: "2"  # ограничить дорогие LB
    services.nodeports: "5"
    configmaps: "100"
    secrets: "100"
    count/jobs.batch: "10"       # generic count/<resource>.<group>
    count/deployments.apps: "20"

    # extended
    requests.nvidia.com/gpu: "4"

Как работает enforcement

Admission controller ResourceQuota подключён в kube-apiserver по умолчанию. На каждое создание объекта он:

  1. Берёт все ResourceQuota в namespace, где создаётся объект.
  2. Для каждой считает текущее использование (status.used) из существующих объектов.
  3. Прибавляет к used потребности нового объекта (сколько он добавит к каждому квотируемому ключу).
  4. Если сумма по какому-то ключу превышает hard — admission rejected.
Error from server (Forbidden): error when creating "pod.yaml":
pods "name" is forbidden: exceeded quota: team-quota,
  requested: requests.cpu=2, used: requests.cpu=9, limited: requests.cpu=10

Сообщение точное: называет имя quota, какой ключ превышен, текущее и hard значения. На CKAD это часто встречается — учитесь читать.

Состояние квоты:

kubectl describe quota -n team-a
# Name:            team-quota
# Resource         Used   Hard
# requests.cpu     9      10
# requests.memory  18Gi   20Gi
# pods             47     50

Scopes: квоты не на всё подряд

spec.scopes или spec.scopeSelector позволяют применять quota только к Pods определённого вида:

spec:
  scopes:
  - BestEffort           # квота применяется только к BestEffort Pods
  hard:
    pods: "5"            # не больше 5 BestEffort Pods в namespace

Возможные scopes:

  • Terminating — Pods с activeDeadlineSeconds >= 0 (Jobs)
  • NotTerminating — Pods без activeDeadlineSeconds (long-running, Deployment-style)
  • BestEffort — Pods QoS BestEffort
  • NotBestEffort — Pods QoS Burstable или Guaranteed
  • PriorityClass — через scopeSelector, выбирает Pods конкретного priorityClassName

С scopeSelector можно делать сложные комбинации:

spec:
  scopeSelector:
    matchExpressions:
    - scopeName: PriorityClass
      operator: In
      values: ["high"]
  hard:
    requests.cpu: "100"
    requests.memory: 200Gi

Это значит: «для Pods с priorityClassName: high суммарные requests.cpu не превышают 100». Можно создавать разные квоты для разных priority tiers — типичный паттерн в multi-tenant cluster.


Killer момент: ResourceQuota требует requests/limits

Это важнейшая особенность ResourceQuota, на которой часто валятся на CKAD и в production.

Если в namespace есть ResourceQuota, который ограничивает requests.cpu/requests.memory/limits.cpu/limits.memory, то каждый создаваемый Pod ОБЯЗАН явно указать requests/limits для соответствующих ресурсов.

Pod без resources против ResourceQuota
Pod без resourceskubectl apply Pod, в spec нет resources блока вообще. Pod был бы BestEffort.
ResourceQuota admissionЕсли ResourceQuota ограничивает requests.cpu — admission ожидает, что Pod заявит requests.cpu. Container без requests НЕ считается как 0; admission отвергает с ошибкой.
rejectedfailed quota: must specify cpu for: <container>; memory for: <container>. Pod не создаётся, owner-controller повторяет попытку, поднимается алерт.

Ошибка:

Error from server (Forbidden): pods "name" is forbidden:
  failed quota: team-quota: must specify limits.cpu for: app; requests.cpu for: app

Решение — связка LimitRange + ResourceQuota:

  1. LimitRange проставляет defaults на admission (defaultRequest, default).
  2. ResourceQuota видит Pod уже с заполненными requests/limits и проверяет квоту.

Без LimitRange ResourceQuota делает namespace недружелюбным: команды забывают и получают rejection. С LimitRange всё работает прозрачно — defaults подставляются, квота применяется к итоговым значениям.

# LimitRange (мутирует Pod, заполняет дефолты)
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: team-a
spec:
  limits:
  - type: Container
    defaultRequest: { cpu: 100m, memory: 128Mi }
    default:        { cpu: 500m, memory: 256Mi }

---
# ResourceQuota (проверяет суммы)
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi
    pods: "50"

Такой dual-setup — стандартная практика. На CKAD это типовая комбинация задач: «создайте LimitRange … и ResourceQuota … проверьте, что новый Pod без resources создаётся и считается в квоте».


Что произойдёт когда квота заполнена

Когда status.used достигает hard:

  • Новые объекты rejected на admission.

  • Существующие объекты продолжают работать без вмешательства.

  • Owner-controllers (Deployment, StatefulSet, Job) продолжают пытаться создать Pods — это видно в events:

    Warning  FailedCreate  3m  ReplicaSet
      Error creating: pods "app-xyz" is forbidden: exceeded quota
  • Replica controller не отменяет попытки; он повторяет с экспоненциальным backoff.

Это безопасно (cluster не падает), но видно только тому, кто читает events. В production нужны алерты типа kube_resourcequota{type="hard"} - kube_resourcequota{type="used"} == 0 в Prometheus.

WARNING

ResourceQuota — это hard cap. Когда квота забита, новые Pods не создаются, а existing keep running. Это значит, что rolling update Deployment в namespace на грани квоты может зависнуть: новый ReplicaSet не может создать replicas (квота заполнена старым), а удалить старые тоже нельзя (они защищены min available). Решение — освободить квоту или временно поднять hard.


Object count квоты

Один из самых полезных вариантов — ограничивать счётчики объектов:

spec:
  hard:
    pods: "50"
    secrets: "20"
    services.loadbalancers: "2"     # ограничить дорогие LBs
    services.nodeports: "5"
    persistentvolumeclaims: "10"
    count/jobs.batch: "100"          # max Jobs в namespace
    count/cronjobs.batch: "20"

services.loadbalancers — особенно полезно в облаке: каждый Service type=LoadBalancer создаёт реальный cloud LB (~$20/мес и выше). Без квоты команда случайно ставит десяток LBs.

secrets, configmaps — защита от спама. Известный паттерн: Helm chart создаёт sealed-secrets на каждый apply, через год — тысячи объектов и медленный list в kubectl.


Проверка знанийKnowledge check
В namespace есть ResourceQuota с requests.cpu=10. Сейчас used 8. Команда хочет создать Pod с requests.cpu=3. Что произойдёт?
ОтветAnswer
Admission отвергнет Pod. 8 (current used) + 3 (new request) = 11, что превышает hard 10. Сообщение: 'exceeded quota: ..., requested: requests.cpu=3, used: requests.cpu=8, limited: requests.cpu=10'. Pod не создаётся. Если это Pod от Deployment — ReplicaSet будет писать FailedCreate в events и retry с backoff.
Проверка знанийKnowledge check
В namespace создан ResourceQuota с limits.memory=10Gi. Команда применяет Pod без resources в spec. Что произойдёт и как исправить?
ОтветAnswer
Admission rejected: 'must specify limits.memory for: <container>'. ResourceQuota на limits.memory требует, чтобы каждый Pod явно указал limits.memory. Pod без resources не проходит. Исправление: либо явно указать limits в Pod, либо (типовое решение) создать LimitRange с default, который проставит limits на admission ДО ResourceQuota — тогда Pod проходит с дефолтным значением.
Проверка знанийKnowledge check
Какая разница между LimitRange и ResourceQuota в моделировании ограничений?
ОтветAnswer
LimitRange — per-object policy: задаёт пределы и defaults для отдельного Pod/Container/PVC. Действует через mutating+validating admission. ResourceQuota — namespace-level budget: задаёт суммарный hard cap на всё в namespace. Действует через validating admission. Они дополняют друг друга: LimitRange гарантирует, что каждый Pod 'разумного размера' и имеет defaults, ResourceQuota не даёт суммарно выйти за бюджет команды.
Проверка знанийKnowledge check
ResourceQuota со scope=BestEffort и hard=pods:5. В namespace 10 Burstable Pods. Можно ли создать ещё BestEffort Pod?
ОтветAnswer
Зависит от того, сколько уже есть BestEffort. Scope ограничивает применение quota только к BestEffort Pods. Если сейчас 0 BestEffort — можно создать до 5. Если уже 5 — admission rejected. Burstable Pods не считаются в этой quota. Это типичный multi-tier: можно отдельной quota разрешить, скажем, неограниченно Burstable (они с requests, под cluster planning), но жёстко ограничить BestEffort (они expendable, не должны массово создаваться).

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. В namespace ResourceQuota: requests.cpu=10, used 8. Pod с requests.cpu=3 — что произойдёт?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5