← 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 (миллиядрах):

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

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

Memory

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

Память — несжимаемый (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 в порядке: BestEffort → Burstable → Guaranteed.

# Посмотреть 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

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

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

  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.

Альтернативы

Vertical Pod Autoscaler (VPA) — подбирает requests/limits по историческому потреблению. Режимы Off (рекомендации) и Auto (рестарт Pod). Устанавливается отдельно.

Horizontal Pod Autoscaler (HPA) — масштабирует реплики по CPU/memory/custom метрикам.

KEDA — event-driven autoscaler по внешним метрикам (Kafka lag, RabbitMQ, SQS).


12: Job и CronJob

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

Resource Management: requests, limits и QoS | Aleksandr Suprun