← Back to notes

Resource Management: requests,
limits и QoS


Без requests и limits scheduler не знает, сколько ресурсов нужно Pod, и при нехватке памяти kubelet вытесняет Pod произвольно. Resource management даёт scheduler данные для размещения и kubelet — приоритеты для eviction.

source: kubernetes.io/docs/concepts/configuration/manage-resources-containers/

Requests и Limits

Каждому контейнеру задаётся два параметра для CPU и памяти:

  • requests — гарантированный минимум. Scheduler использует requests для выбора узла: если на узле нет запрошенных ресурсов — Pod туда не попадёт.
  • limits — максимально допустимое потребление, принудительно ограничивается kubelet.
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: myapp:1.0
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: 500m
          memory: 512Mi

CPU

Измеряется в millicores (миллиядрах):

ЗначениеОписание
1 или 1000m1 полное ядро CPU
500m0.5 ядра
250m0.25 ядра
100m0.1 ядра

CPU — сжимаемый (compressible) ресурс. При превышении limit контейнер троттлится — замедляется, но не убивается.

Часть команд не ставит CPU limits в production, чтобы избежать throttling при наличии свободных ресурсов. Memory limits — обязательны.

Memory

Измеряется в байтах с суффиксами:

СуффиксЗначение
KiКибибайт (1024 байт)
MiМебибайт (1024 Ki)
GiГибибайт (1024 Mi)

Память — несжимаемый (incompressible) ресурс. При превышении limit контейнер получает OOMKilled и перезапускается. kubectl describe pod покажет Reason: OOMKilled.

QoS классы

Kubernetes присваивает каждому Pod класс QoS. Он определяет приоритет при eviction на узле.

source: kubernetes.io/docs/concepts/workloads/pods/pod-qos/

Guaranteed

Все контейнеры в Pod имеют requests == limits для CPU и memory.

resources:
  requests:
    cpu: 500m
    memory: 256Mi
  limits:
    cpu: 500m
    memory: 256Mi

Наивысший приоритет, последний кандидат на eviction. Для критических сервисов.

Burstable

Хотя бы один контейнер имеет requests, и requests != limits (или limit не задан).

resources:
  requests:
    cpu: 250m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

Средний приоритет. Вытесняется после BestEffort.

BestEffort

Ни один контейнер не имеет requests и limits.

# resources не указаны вообще

Низший приоритет. Первый кандидат на eviction при нехватке ресурсов.

Порядок eviction

При нехватке памяти на узле kubelet выбирает Pod для eviction не по QoS-классу напрямую, а по: (1) превышает ли usage requests, (2) Pod Priority, (3) usage относительно requests. На практике порядок при memory pressure примерно соответствует BestEffort → Burstable → Guaranteed; QoS также влияет на oom_score_adj для OOM killer.

# Посмотреть QoS класс Pod
kubectl get pod <name> -o jsonpath='{.status.qosClass}'

LimitRange

LimitRange задаёт defaults и допустимые диапазоны для контейнеров в Namespace.

source: kubernetes.io/docs/concepts/policy/limit-range/

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: dev
spec:
  limits:
    - type: Container
      default:           # limits по умолчанию (если не указаны)
        cpu: 500m
        memory: 256Mi
      defaultRequest:    # requests по умолчанию (если не указаны)
        cpu: 100m
        memory: 128Mi
      min:               # минимально допустимые requests
        cpu: 50m
        memory: 64Mi
      max:               # максимально допустимые limits
        cpu: "2"
        memory: 2Gi
    - type: Pod
      max:
        cpu: "4"
        memory: 4Gi

Если контейнер запросит ресурсы за пределами min/max — API Server отклонит манифест.

kubectl get limitrange -n dev
kubectl describe limitrange default-limits -n dev

ResourceQuota

ResourceQuota ограничивает суммарное потребление ресурсов в Namespace.

source: kubernetes.io/docs/concepts/policy/resource-quotas/

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-quota
  namespace: dev
spec:
  hard:
    # Вычислительные ресурсы
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    # Количество объектов
    pods: "20"
    services: "10"
    services.loadbalancers: "2"
    persistentvolumeclaims: "10"
    configmaps: "20"
    secrets: "20"

При активной ResourceQuota с requests.cpu или requests.memory каждый Pod обязан указать соответствующие requests — иначе API Server отклонит Pod. Поэтому ResourceQuota используют вместе с LimitRange: LimitRange задаёт defaults, ResourceQuota ограничивает суммарно.

# Просмотр квот и текущего использования
kubectl get resourcequota -n dev
kubectl describe resourcequota compute-quota -n dev

Storage limits

Хранилище ограничивается отдельно от CPU/memory — на двух уровнях: ephemeral-storage контейнеров (локальный диск ноды) и PVC (persistent-хранилище).

ephemeral-storage контейнера

Локальное эфемерное хранилище (writable layer контейнера, emptyDir, логи) задаётся как обычные requests/limits:

resources:
  requests:
    ephemeral-storage: 1Gi
  limits:
    ephemeral-storage: 2Gi

При превышении ephemeral-storage limit Pod подлежит eviction (в отличие от memory — не OOMKill, а вытеснение).

source: kubernetes.io/docs/concepts/configuration/manage-resources-containers/

ResourceQuota для storage

ResourceQuota ограничивает суммарное хранилище и число PVC в namespace:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-quota
  namespace: dev
spec:
  hard:
    requests.storage: 100Gi                 # сумма storage requests всех PVC
    persistentvolumeclaims: "20"            # число PVC в namespace
    requests.ephemeral-storage: 20Gi        # сумма ephemeral-storage requests всех Pod
    limits.ephemeral-storage: 40Gi          # сумма ephemeral-storage limits
    # Ограничения per-StorageClass:
    gold.storageclass.storage.k8s.io/requests.storage: 50Gi
    gold.storageclass.storage.k8s.io/persistentvolumeclaims: "5"
    # Общий синтаксис подсчёта объектов:
    count/deployments.apps: "10"

Формат per-StorageClass — <storage-class-name>.storageclass.storage.k8s.io/requests.storage и .../persistentvolumeclaims.

source: kubernetes.io/docs/concepts/policy/resource-quotas/

LimitRange для PVC и ephemeral-storage

LimitRange задаёт min/max на один PVC (type: PersistentVolumeClaim) и defaults/max для ephemeral-storage контейнеров (type: Container):

apiVersion: v1
kind: LimitRange
metadata:
  name: storage-limits
  namespace: dev
spec:
  limits:
    - type: PersistentVolumeClaim
      min:
        storage: 1Gi
      max:
        storage: 2Gi
    - type: Container
      default:
        ephemeral-storage: 1Gi
      defaultRequest:
        ephemeral-storage: 500Mi
      max:
        ephemeral-storage: 4Gi

PVC с запросом storage вне диапазона min/max отклоняется admission-контроллером LimitRange.

source: kubernetes.io/docs/tasks/administer-cluster/limit-storage-consumption/ source: kubernetes.io/docs/reference/kubernetes-api/policy-resources/limit-range-v1/

LimitRange и ResourceQuota для storage работают в паре: при max 2Gi на PVC и квоте requests.storage: 5Gi нельзя создать три PVC по 2Gi — суммарные 6Gi превысят квоту в 5Gi.

source: kubernetes.io/docs/tasks/administer-cluster/limit-storage-consumption/

Рекомендации по выбору значений

Ресурсrequestslimits
CPUСредняя нагрузка приложенияПиковая нагрузка (или не задавать — throttling безопасен)
MemoryСтабильное потреблениеМаксимально допустимое (превышение = OOMKilled)

Практический подход:

  1. Запустить приложение без limits
  2. Собрать метрики потребления (Prometheus + kubectl top pods --containers)
  3. Установить requests ≈ p50 потребления, limits ≈ p99 + запас 20-30%
# Текущее потребление ресурсов
kubectl top nodes
kubectl top pods
kubectl top pods --containers    # по контейнерам
kubectl top pods -n production

Полный пример: Namespace с квотами и defaults

# 1. Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: production
---
# 2. LimitRange  defaults для контейнеров без явных requests/limits
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:
        cpu: 500m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      max:
        cpu: "4"
        memory: 4Gi
---
# 3. ResourceQuota  суммарные ограничения для namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "100"

Когда использовать

requests всегда — без requests scheduler разместит Pod на узле без достаточных ресурсов.

memory limits всегда — утечка памяти в одном контейнере может убить весь узел.

CPU limits — опционально. Без них Pod может burst в свободные ресурсы; с ними throttling начинается даже при наличии свободных ядер.

LimitRange — в каждом namespace с пользователями или CI/CD. Защищает от Pod без requests/limits.

ResourceQuota — для мультикомандных кластеров с лимитами на команду/окружение.

Типичные ошибки

Нет requests — Pod в BestEffort. Pod первым вытесняется при нехватке ресурсов. В production у всех Pod должны быть requests.

OOMKilled в цикле. Приложение превышает memory limit, перезапускается, снова OOMKilled. Увеличить limit или исправить утечку. Диагностика: kubectl describe podLast State и Reason: OOMKilled.

CPU throttling незаметно ломает latency. kubectl top показывает текущее потребление, не пиковое. Throttling виден через Prometheus container_cpu_cfs_throttled_seconds_total.

ResourceQuota без LimitRange. При активной квоте Pod без явных requests не пройдут admission. Используй вместе.

Жёсткие limits для JVM. JVM потребляет много памяти при старте (heap + metaspace + stack). Ставь -Xmx ниже memory limit.

Автоскейлинг и in-place resize

In-place pod resize (InPlacePodVerticalScaling) — GA с Kubernetes 1.35: CPU/memory контейнера меняются без рестарта Pod через subresource resize (политики NotRequired/RestartContainer на ресурс).

source: kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/

Автоматический подбор ресурсов и масштабирование строятся поверх этого: HPA (реплики), VPA (requests/limits, режим InPlaceOrRecreate использует in-place resize), KEDA (event-driven), Cluster Autoscaler / Karpenter (ноды). Подробно — 28: Autoscaling.


12: Job и CronJob

14: kubectl: справочник

Resource Management: requests, limits и QoS | Aleksandr Suprun