Ресурсы: requests и limits, HPA, автомасштабирование

↑ Этап 5 · Kubernetes

Желательно~6 мин чтенияУровень: middle

Зачем это на собесе

requests и limits определяют планирование, стабильность и стоимость; HPA — основа автомасштабирования. Классические вопросы про OOMKilled и троттлинг.

requests и limits

resources:
  requests: { cpu: 250m, memory: 256Mi }     # гарантия и основа планирования
  limits:   { cpu: "1",  memory: 512Mi }     # потолок
  • requests — сколько ресурсов планировщик резервирует для пода на ноде (сумма requests на ноде ≤ allocatable); определяет долю CPU при конкуренции;
  • limits — максимум, который контейнер может использовать.

Единицы: CPU в ядрах (1 = 1 vCPU, 500m = 0,5 ядра); память в байтах (Mi, Gi — двоичные; M, G — десятичные).

Поведение при превышении

РесурсПри превышении limits
CPU (сжимаемый)троттлинг (CFS quota): процесс замедляется, но не убивается; латентность растёт
Memory (несжимаемый)OOM kill контейнера (OOMKilled, exit 137), перезапуск

Частая практика: requests.cpu задавать, limits.cpu не задавать или задавать с запасом (троттлинг вредит латентности даже при свободных ядрах ноды); limits.memory = requests.memory (предсказуемость, гарантированная память) или небольшой запас. Обсуждаемая тема; выбор зависит от политики кластера.

QoS-классы

КлассУсловиеВытеснение при нехватке памяти на ноде
Guaranteedдля всех контейнеров requests == limits (CPU и memory)последними
Burstableзаданы requests (меньше limits)средние
BestEffortничего не заданопервыми

При давлении на ноду kubelet вытесняет (evicts) поды по QoS и использованию сверх requests. Всегда задавайте requests.

Подбор значений

  • измерять реальное потребление (kubectl top, Prometheus: container_memory_working_set_bytes, rate(container_cpu_usage_seconds_total)), p95/p99 под пиком;
  • VPA (Vertical Pod Autoscaler) в режиме рекомендаций (Off/Initial), Goldilocks;
  • .NET: учитывать GC и лимиты памяти контейнера (DOTNET_GCHeapHardLimit, серверный GC, DOTNET_gcServer), число потоков = ядра по limits; JVM: -XX:MaxRAMPercentage; Node: --max-old-space-size;
  • запас на пики, прогрев; слишком высокие requests — низкая утилизация и стоимость, слишком низкие — нестабильность и «шумные соседи».

LimitRange и ResourceQuota

  • LimitRange: значения по умолчанию и min/max для контейнеров в namespace;
  • ResourceQuota: общий лимит namespace (CPU, память, число объектов, хранилище).

Масштабирование

ИнструментЧто масштабируетПо чему
HPA (Horizontal Pod Autoscaler)число репликCPU/память (metrics-server), кастомные и внешние метрики
VPArequests/limits подаисторическое потребление
KEDAчисло реплик (в т.ч. до 0)события: длина очереди (Kafka, RabbitMQ, SQS), Prometheus, cron, HTTP
Cluster Autoscaler / Karpenterчисло нодPending поды из-за нехватки ресурсов; удаление недозагруженных нод

HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: api }
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }     # от requests!
    - type: Pods
      pods: { metric: { name: http_requests_per_second }, target: { type: AverageValue, averageValue: "100" } }
  behavior:
    scaleUp:   { stabilizationWindowSeconds: 0,   policies: [{ type: Percent, value: 100, periodSeconds: 60 }] }
    scaleDown: { stabilizationWindowSeconds: 300, policies: [{ type: Pods,    value: 2,   periodSeconds: 60 }] }

Формула: desired = ceil(current × currentMetric / targetMetric).

Важное:

  • утилизация считается относительно requests → без requests HPA не работает;
  • нужен metrics-server (для CPU/памяти); для кастомных метрик — Prometheus Adapter, KEDA;
  • не использовать HPA и VPA по одному и тому же ресурсу одновременно;
  • задержка реакции (scrape интервал, стабилизация): для резких пиков заранее больше minReplicas, scheduled scaling (KEDA cron);
  • медленный старт приложения и readiness ограничивают скорость наращивания; прогрев; startupProbe;
  • stateful нагрузки масштабируются сложнее;
  • scale down осторожно (окно стабилизации) + graceful shutdown + PodDisruptionBudget;
  • учитывать пределы зависимостей (пул соединений БД ↑ с числом подов): PgBouncer, лимиты.

Cluster Autoscaler / Karpenter

Добавляют ноды, когда есть поды в Pending с FailedScheduling из-за ресурсов; удаляют недозагруженные. Условия: корректные requests, PDB, топологические ограничения, лимиты группы нод. Karpenter подбирает тип инстанса под поды (AWS), консолидация (bin packing), spot.

Практики планирования

  • requests для всего; limits памяти; мониторинг throttling (container_cpu_cfs_throttled_periods_total);
  • priorityClass: критичные поды вытесняют менее важные;
  • overprovisioning (pause-поды низкого приоритета) для быстрого масштабирования нод;
  • topologySpreadConstraints и anti-affinity для распределения по зонам;
  • PodDisruptionBudget для минимальной доступности;
  • nodeSelector / taints для выделенных пулов (GPU, spot);
  • учёт системного резерва нод (kube-reserved, system-reserved, allocatable);
  • стоимость: rightsizing, spot/preemptible, автоскейл до минимума в dev, выключение по расписанию (KEDA cron, kube-downscaler).

Диагностика

kubectl top pods -n clinic --containers; kubectl top nodes
kubectl describe node worker-1                 # Allocated resources, Conditions (MemoryPressure)
kubectl describe pod x                          # Last State: OOMKilled, Reason: Evicted
kubectl get hpa -w; kubectl describe hpa api    # <unknown>/70% = нет метрик/requests
kubectl get events --field-selector reason=FailedScheduling
ПроблемаПричина
Pod Pending (Insufficient cpu/memory)requests больше свободного; нужен новый узел/уменьшить requests
OOMKilledлимит памяти мал или утечка; JVM/.NET кучи без учёта лимита
Высокая латентность при низком CPU нодыCPU throttling из-за limits
HPA <unknown>нет metrics-server/requests
Поды вытеснены (Evicted)давление ноды; BestEffort/Burstable
Флаппинг масштабированияузкое окно стабилизации, шумная метрика

Вопросы с ответами