Ресурсы и лимиты

TL;DR: requests — гарантированный минимум (влияет на scheduling). limits — жёсткий потолок (превышение CPU = throttling, RAM = OOMKill). Всегда задавайте оба. LimitRange/ResourceQuota — защита на уровне namespace.

Requests и Limits

spec:
  containers:
    - name: app
      image: myapp:v1
      resources:
        requests:                  # Scheduler гарантирует эти ресурсы
          cpu: "250m"              # 0.25 ядра (1000m = 1 core)
          memory: "128Mi"          # 128 мебибайт
        limits:                    # Жёсткий потолок
          cpu: "500m"
          memory: "256Mi"

Единицы измерения

РесурсЕдиницыПримеры
CPUmillicores100m = 0.1 ядра, 1 = 1 ядро, 1500m = 1.5 ядра
Memorybytes128Mi = 128 MiB, 1Gi = 1 GiB, 256M = 256 MB (десятичный)

Что происходит при превышении

РесурсДействиеРезультат
CPU > limitThrottlingПод замедляется, но не убивается
Memory > limitOOMKillКонтейнер убивается и перезапускается
Memory > node capacityEvictionKubelet вытесняет поды с ноды

QoS классы

Kubernetes автоматически назначает QoS-класс на основе requests/limits.

QoS ClassУсловиеПриоритет при eviction
Guaranteedrequests == limits (для всех контейнеров)Последний (самый защищённый)
Burstablerequests < limits (хотя бы у одного)Средний
BestEffortНет requests и limitsПервый (убивается первым)
# Guaranteed (рекомендуется для production)
resources:
  requests:
    cpu: "500m"
    memory: "256Mi"
  limits:
    cpu: "500m"           # == requests
    memory: "256Mi"       # == requests

Рекомендации по значениям

# Начальные значения (подстроить после мониторинга)
# Веб-сервер (Node.js/Python)
resources:
  requests: { cpu: "100m", memory: "128Mi" }
  limits:   { cpu: "500m", memory: "512Mi" }
 
# Java-приложение
resources:
  requests: { cpu: "500m", memory: "512Mi" }
  limits:   { cpu: "1000m", memory: "1Gi" }
 
# PostgreSQL
resources:
  requests: { cpu: "250m", memory: "256Mi" }
  limits:   { cpu: "1000m", memory: "1Gi" }
 
# Nginx sidecar
resources:
  requests: { cpu: "50m", memory: "32Mi" }
  limits:   { cpu: "100m", memory: "64Mi" }

Мониторинг реального потребления

# Требуется metrics-server
kubectl top pods
kubectl top pods --sort-by=memory
kubectl top nodes
 
# Minikube
minikube addons enable metrics-server

LimitRange (дефолты для namespace)

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: default
spec:
  limits:
    - type: Container
      default:                     # limits по умолчанию
        cpu: "500m"
        memory: "256Mi"
      defaultRequest:              # requests по умолчанию
        cpu: "100m"
        memory: "128Mi"
      max:                         # максимум для контейнера
        cpu: "2"
        memory: "2Gi"
      min:                         # минимум
        cpu: "50m"
        memory: "32Mi"

ResourceQuota (лимит на namespace)

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "10"             # суммарно 10 ядер requests
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    pods: "50"                     # максимум 50 подов
    services: "10"
    persistentvolumeclaims: "20"
    configmaps: "50"
    secrets: "50"
kubectl get resourcequota -n team-a
kubectl describe resourcequota team-quota -n team-a

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

ОшибкаСимптомРешение
Нет requests/limitsPod = BestEffort, убивается первым при нехватке ресурсовВсегда указывать requests и limits
requests.memory слишком малPod не влезает ни на одну ноду → Pendingkubectl describe pod → Events: Insufficient memory
limits.memory слишком малOOMKilled → CrashLoopBackOffkubectl top pod, увеличить limits
CPU limits для latency-sensitiveThrottling вызывает всплески задержкиДля latency: убрать CPU limits, оставить только requests
Забыли LimitRangeПоды без limits потребляют все ресурсы нодыСоздать LimitRange с defaults для каждого namespace