{ "index": 128, "slug": "editorial-2024-06-mechanism-container-orchestration", "title": "Контейнерная нагрузка без догадок: как связать requests, readiness и HPA", "excerpt": "После обновления образа Pod может стать Unready, а HPA — не изменить число реплик. Разбираем, какой механизм отвечает за placement, traffic и масштабирование, как проверить гипотезу и когда изменение манифеста действительно готово.", "contentHtml": "
После обновления образа часть Pod может долго оставаться на старой версии, а новая версия — перейти в Unready. Команда увеличивает maxReplicas, поднимает CPU limit и запускает rollout повторно. Симптомы меняются, но причина не становится яснее. Цена такой подмены — лишние реплики, перегруженные Node и откат, который трудно объяснить.
У Kubernetes здесь не одна «ручка нагрузки», а несколько независимых контуров. Scheduler размещает Pod по requests. Runtime применяет limits. Readiness определяет, можно ли отправлять Pod трафик Service. HPA рассчитывает желаемое число реплик по выбранной метрике. Эти контуры встречаются в одном манифесте, но не доказывают друг друга.
\n| Симптом | Вероятный контур | Проверка | Следующее действие |
|---|---|---|---|
HPA показывает <unknown> или не меняет реплики | Метрика недоступна либо для Pod нет нужного resource request | Проверить HPA conditions, metrics.k8s.io и request каждого container | Восстановить контракт метрики; не поднимать maxReplicas вслепую |
Pod запущен, но Unready | Readiness не проходит или приложение ещё прогревается | Сопоставить probe, Pod conditions, события и endpoint | Исправить условие готовности или добавить startup-защиту |
Pod остаётся Pending | Сумма requests не помещается на доступные Node | Сравнить requests с allocatable, quota и admission-правилами | Пересмотреть профиль ресурсов или размещение |
| CPU limit увеличили, но задержка не исчезла | Доминирует память, очередь или внешняя зависимость | Разделить CPU, memory, latency, queue и dependency signals | Изменить один подтверждённый фактор и повторить замер |
Request отвечает за планирование. Scheduler учитывает requests контейнеров при выборе Node. Для одного ресурса Pod получает сумму requests своих контейнеров. Поэтому изменение request может перевести Pod из «размещается» в «не помещается», даже если текущее потребление на Node пока невелико. Request — заявленная потребность для планирования, а не обещание постоянной скорости.
\nLimit задаёт потолок контейнера. CPU limit может ограничивать долю CPU-времени, а превышение memory limit может привести к срабатыванию механизма out-of-memory. Limit не сообщает, сколько запросов выдержит приложение, и не является автоматически целью HPA. Между «контейнер не превысил лимит» и «у сервиса есть запас по latency» нет логического равенства.
\nReadiness управляет маршрутизацией. При неуспешной readiness probe Kubernetes помечает контейнер неготовым, а адрес Pod перестаёт быть готовым endpoint для соответствующих Service. Это сигнал «можно ли принимать этот трафик сейчас», а не проверка всех зависимостей системы. Если endpoint опрашивает необязательную базу или внешний API, краткий сбой этой зависимости может убрать из трафика исправное приложение.
\nHPA меняет желаемое число реплик. Для resource metric с averageUtilization процент считается относительно соответствующего request. Target 70 процентов — это 70 процентов request, не Node и не limit. Для CPU request 500m учебная точка 70 процентов равна 350m на Pod. Это объяснение знаменателя, а не рекомендация профиля.
Deployment отвечает ещё за один отдельный вопрос: как заменить Pod старой версии на Pod новой. При RollingUpdate параметры maxUnavailable и maxSurge ограничивают число временно недоступных и дополнительных Pod. Readiness влияет на то, когда новая реплика считается пригодной для трафика, но сама по себе не гарантирует, что rollout завершится: новый образ может не стартовать, не пройти probe или не поместиться по requests.
Поэтому «новые Pod появились» и «новая версия обслуживает нагрузку» — разные проверки. Для отката нужно заранее знать имя Deployment, границу времени и состояние, которое считается безопасным. Команда kubectl rollout status подтверждает наблюдаемое состояние rollout, но не заменяет проверку ошибок приложения и latency.
Ниже один минимальный пример для HTTP-сервиса. В нём добавлены обязательные для Deployment selector и labels, startup probe для долгого старта и readiness probe для допуска к трафику. Значения ресурсов, пути и образ проектные: их нельзя переносить в production без измерения.
\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: orders\nspec:\n replicas: 2\n strategy:\n type: RollingUpdate\n rollingUpdate:\n maxUnavailable: 0\n maxSurge: 1\n selector:\n matchLabels:\n app: orders\n template:\n metadata:\n labels:\n app: orders\n spec:\n containers:\n - name: app\n image: registry.example/orders:2024-06-01\n ports:\n - name: http\n containerPort: 8080\n resources:\n requests:\n cpu: 500m\n memory: 384Mi\n limits:\n cpu: 1000m\n memory: 768Mi\n startupProbe:\n httpGet:\n path: /healthz\n port: http\n periodSeconds: 10\n failureThreshold: 30\n readinessProbe:\n httpGet:\n path: /ready\n port: http\n periodSeconds: 5\n failureThreshold: 3\n---\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n name: orders\nspec:\n scaleTargetRef:\n apiVersion: apps/v1\n kind: Deployment\n name: orders\n minReplicas: 2\n maxReplicas: 8\n metrics:\n - type: Resource\n resource:\n name: cpu\n target:\n type: Utilization\n averageUtilization: 70\nПри таком request target 70 процентов означает условные 350m среднего CPU на Pod. Но манифест не доказывает, что 500m достаточно, endpoint действительно отделяет startup от serving, а metrics API доступен. Если у контейнера, который участвует в CPU utilization, нет CPU request, HPA не сможет определить эту utilization для метрики. Это повод проверить конфигурацию и condition HPA, а не подставить процент вручную.
\nЭти команды предполагают доступ к namespace и установленный kubectl. Первая команда проверяет манифест сервером без изменения объекта; остальные читают состояние или запускают обычный rollout после явного применения. В тестовом кластере замените namespace и имя образа на свои.
kubectl apply --dry-run=server -f deployment.yaml\nkubectl apply -f deployment.yaml\nkubectl rollout status deployment/orders --timeout=10m\nkubectl get deployment/orders\nkubectl get pods -l app=orders -o wide\nkubectl describe deployment/orders\nkubectl describe pod -l app=orders\nkubectl get hpa orders\nkubectl top pods -l app=orders\nОжидаемый результат нужно формулировать заранее: например, rollout завершается за 10 минут, две минимальные реплики находятся в состоянии Ready, а HPA получает числовую CPU-метрику. Если kubectl top или HPA показывают отсутствие метрик, это ограничение наблюдаемости. Оно не подтверждает ни низкую нагрузку, ни исправность autoscaling. Для остановившегося rollout дополнительно смотрите Events и condition Deployment; для Pending — причины планировщика и effective requests.
Представим новую версию, которая прогревает локальный cache 20 секунд. Readiness endpoint начинает отвечать через две секунды, а CPU во время старта высок. Если metrics API отдаёт данные и контроллер учитывает их, HPA может принять решение о масштабировании, но новые Pod повторят тот же startup. Реплик станет больше, а serving capacity не обязательно вырастет. Это не доказательство неисправности HPA: сначала нужно отделить startup work от serving work.
\nОбратная ошибка симметрична. Readiness проверяет внешний сервис, который нужен только части запросов. При кратком отказе все Pod становятся Unready, хотя основной маршрут ещё может работать. Дополнительные реплики проходят ту же проверку и тоже исключаются из Service. Действие находится в контракте readiness и политике деградации, а не в увеличении maxReplicas.
Эта модель не выбирает универсальные CPU и memory values. На результат влияют admission webhooks, ResourceQuota, PodDisruptionBudget, Node allocatable, планировщик, runtime, Metrics Server, custom adapter, очередь, cache и downstream saturation. Официальная документация описывает механизм Kubernetes, но не конфигурацию вашего кластера.
\nReadiness не заменяет liveness и startup probes. HPA не устраняет утечку памяти и не гарантирует доступность внешней зависимости. CPU utilization не равен throughput, а отсутствие роста реплик не всегда означает ошибку autoscaling: контроллер может упереться в min/max, не получить метрику или увидеть, что целевой workload уже соответствует target. Учебную арифметику нельзя объявлять production-результатом без профиля на реальной нагрузке.
\nИзменение можно считать объяснимым, когда для одного workload названы владелец и окно наблюдения; effective requests и limits зафиксированы по container; смысл readiness записан одной фразой; HPA metric имеет источник и знаменатель; rollout имеет timeout и rollback; а выбранный эффект измерен тем же evidence до и после. Формулировки «Pod стал лучше» и «процент выглядит нормально» этого критерия не закрывают.
\n