8 lines
19 KiB
JSON
8 lines
19 KiB
JSON
{
|
||
"index": 128,
|
||
"slug": "editorial-2024-06-mechanism-container-orchestration",
|
||
"title": "Контейнерная нагрузка без догадок: как связать requests, readiness и HPA",
|
||
"excerpt": "После обновления образа Pod может стать Unready, а HPA — не изменить число реплик. Разбираем, какой механизм отвечает за placement, traffic и масштабирование, как проверить гипотезу и когда изменение манифеста действительно готово.",
|
||
"contentHtml": "<p>После обновления образа часть Pod может долго оставаться на старой версии, а новая версия — перейти в <code>Unready</code>. Команда увеличивает <code>maxReplicas</code>, поднимает CPU limit и запускает rollout повторно. Симптомы меняются, но причина не становится яснее. Цена такой подмены — лишние реплики, перегруженные Node и откат, который трудно объяснить.</p>\n<p>У Kubernetes здесь не одна «ручка нагрузки», а несколько независимых контуров. Scheduler размещает Pod по requests. Runtime применяет limits. Readiness определяет, можно ли отправлять Pod трафик Service. HPA рассчитывает желаемое число реплик по выбранной метрике. Эти контуры встречаются в одном манифесте, но не доказывают друг друга.</p>\n<h2>Сначала разделите симптом и механизм</h2>\n<div class=\"table-scroll\"><table><caption>Диагностическая матрица для четырёх похожих симптомов</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Вероятный контур</th><th scope=\"col\">Проверка</th><th scope=\"col\">Следующее действие</th></tr></thead><tbody><tr><td>HPA показывает <code><unknown></code> или не меняет реплики</td><td>Метрика недоступна либо для Pod нет нужного resource request</td><td>Проверить HPA conditions, <code>metrics.k8s.io</code> и request каждого container</td><td>Восстановить контракт метрики; не поднимать <code>maxReplicas</code> вслепую</td></tr><tr><td>Pod запущен, но <code>Unready</code></td><td>Readiness не проходит или приложение ещё прогревается</td><td>Сопоставить probe, Pod conditions, события и endpoint</td><td>Исправить условие готовности или добавить startup-защиту</td></tr><tr><td>Pod остаётся <code>Pending</code></td><td>Сумма requests не помещается на доступные Node</td><td>Сравнить requests с allocatable, quota и admission-правилами</td><td>Пересмотреть профиль ресурсов или размещение</td></tr><tr><td>CPU limit увеличили, но задержка не исчезла</td><td>Доминирует память, очередь или внешняя зависимость</td><td>Разделить CPU, memory, latency, queue и dependency signals</td><td>Изменить один подтверждённый фактор и повторить замер</td></tr></tbody></table></div>\n<h2>Четыре контура и их границы</h2>\n<p><strong>Request отвечает за планирование.</strong> Scheduler учитывает requests контейнеров при выборе Node. Для одного ресурса Pod получает сумму requests своих контейнеров. Поэтому изменение request может перевести Pod из «размещается» в «не помещается», даже если текущее потребление на Node пока невелико. Request — заявленная потребность для планирования, а не обещание постоянной скорости.</p>\n<p><strong>Limit задаёт потолок контейнера.</strong> CPU limit может ограничивать долю CPU-времени, а превышение memory limit может привести к срабатыванию механизма out-of-memory. Limit не сообщает, сколько запросов выдержит приложение, и не является автоматически целью HPA. Между «контейнер не превысил лимит» и «у сервиса есть запас по latency» нет логического равенства.</p>\n<p><strong>Readiness управляет маршрутизацией.</strong> При неуспешной readiness probe Kubernetes помечает контейнер неготовым, а адрес Pod перестаёт быть готовым endpoint для соответствующих Service. Это сигнал «можно ли принимать этот трафик сейчас», а не проверка всех зависимостей системы. Если endpoint опрашивает необязательную базу или внешний API, краткий сбой этой зависимости может убрать из трафика исправное приложение.</p>\n<p><strong>HPA меняет желаемое число реплик.</strong> Для resource metric с <code>averageUtilization</code> процент считается относительно соответствующего request. Target 70 процентов — это 70 процентов request, не Node и не limit. Для CPU request 500m учебная точка 70 процентов равна 350m на Pod. Это объяснение знаменателя, а не рекомендация профиля.</p>\n<figure><img src=\"/assets/editorial/2024/container-orchestration-2024-capacity-curve.svg\" alt=\"Учебная диаграмма показывает CPU usage как процент request, target HPA 70 процентов и отдельный CPU limit\" loading=\"lazy\" /><figcaption>Схема разделяет target HPA и CPU limit. Точки синтетические: они объясняют арифметику и не являются telemetry или прогнозом capacity конкретного кластера.</figcaption></figure>\n<h2>Контролируемая замена версии</h2>\n<p>Deployment отвечает ещё за один отдельный вопрос: как заменить Pod старой версии на Pod новой. При RollingUpdate параметры <code>maxUnavailable</code> и <code>maxSurge</code> ограничивают число временно недоступных и дополнительных Pod. Readiness влияет на то, когда новая реплика считается пригодной для трафика, но сама по себе не гарантирует, что rollout завершится: новый образ может не стартовать, не пройти probe или не поместиться по requests.</p>\n<p>Поэтому «новые Pod появились» и «новая версия обслуживает нагрузку» — разные проверки. Для отката нужно заранее знать имя Deployment, границу времени и состояние, которое считается безопасным. Команда <code>kubectl rollout status</code> подтверждает наблюдаемое состояние rollout, но не заменяет проверку ошибок приложения и latency.</p>\n<h2>Полный учебный манифест</h2>\n<p>Ниже один минимальный пример для HTTP-сервиса. В нём добавлены обязательные для Deployment selector и labels, startup probe для долгого старта и readiness probe для допуска к трафику. Значения ресурсов, пути и образ проектные: их нельзя переносить в production без измерения.</p>\n<pre><code>apiVersion: 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</code></pre>\n<p>При таком request target 70 процентов означает условные 350m среднего CPU на Pod. Но манифест не доказывает, что 500m достаточно, endpoint действительно отделяет startup от serving, а metrics API доступен. Если у контейнера, который участвует в CPU utilization, нет CPU request, HPA не сможет определить эту utilization для метрики. Это повод проверить конфигурацию и condition HPA, а не подставить процент вручную.</p>\n<h2>Команды для воспроизводимой проверки</h2>\n<p>Эти команды предполагают доступ к namespace и установленный <code>kubectl</code>. Первая команда проверяет манифест сервером без изменения объекта; остальные читают состояние или запускают обычный rollout после явного применения. В тестовом кластере замените namespace и имя образа на свои.</p>\n<pre><code>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</code></pre>\n<p>Ожидаемый результат нужно формулировать заранее: например, rollout завершается за 10 минут, две минимальные реплики находятся в состоянии Ready, а HPA получает числовую CPU-метрику. Если <code>kubectl top</code> или HPA показывают отсутствие метрик, это ограничение наблюдаемости. Оно не подтверждает ни низкую нагрузку, ни исправность autoscaling. Для остановившегося rollout дополнительно смотрите Events и condition Deployment; для <code>Pending</code> — причины планировщика и effective requests.</p>\n<h2>Почему HPA не лечит неготовность</h2>\n<p>Представим новую версию, которая прогревает локальный cache 20 секунд. Readiness endpoint начинает отвечать через две секунды, а CPU во время старта высок. Если metrics API отдаёт данные и контроллер учитывает их, HPA может принять решение о масштабировании, но новые Pod повторят тот же startup. Реплик станет больше, а serving capacity не обязательно вырастет. Это не доказательство неисправности HPA: сначала нужно отделить startup work от serving work.</p>\n<p>Обратная ошибка симметрична. Readiness проверяет внешний сервис, который нужен только части запросов. При кратком отказе все Pod становятся Unready, хотя основной маршрут ещё может работать. Дополнительные реплики проходят ту же проверку и тоже исключаются из Service. Действие находится в контракте readiness и политике деградации, а не в увеличении <code>maxReplicas</code>.</p>\n<h2>Порядок расследования</h2>\n<ol><li><strong>Ограничьте объект.</strong> Запишите namespace, Deployment, owner, serving path, startup path, единицу работы и окно наблюдения. Один вывод — один workload.</li><li><strong>Снимите effective-конфигурацию.</strong> Зафиксируйте requests и limits каждого container, probes, selector, rollout strategy и HPA metric. Отделяйте YAML в репозитории от объекта после admission.</li><li><strong>Сформулируйте смысл Ready.</strong> Одной фразой опишите, после какого условия Pod может принимать Service traffic. Отдельно перечислите, чего Ready не доказывает: throughput, latency и здоровье необязательных зависимостей.</li><li><strong>Проверьте источник метрики.</strong> Для CPU utilization свяжите target с request и проверьте resource metrics API. Для custom или external metric укажите target type, selector и владельца pipeline. При отсутствии данных остановите вывод.</li><li><strong>Соберите одно evidence.</strong> Выберите condition, метрику или контролируемый тест. Сохраните источник, время, workload и ограничение интерпретации. Учебный манифест не заменяет наблюдение.</li><li><strong>Измените одну гипотезу.</strong> Выберите request, limit, probe, rollout policy или HPA policy. Запишите ожидаемый сигнал, stop condition и команду отката.</li><li><strong>Повторите ту же проверку.</strong> Сравните одинаковое окно, workload и тип evidence. Если изменились сразу несколько контуров, эффект нельзя приписать одному решению.</li></ol>\n<h2>Ограничения применимости</h2>\n<p>Эта модель не выбирает универсальные CPU и memory values. На результат влияют admission webhooks, ResourceQuota, PodDisruptionBudget, Node allocatable, планировщик, runtime, Metrics Server, custom adapter, очередь, cache и downstream saturation. Официальная документация описывает механизм Kubernetes, но не конфигурацию вашего кластера.</p>\n<p>Readiness не заменяет liveness и startup probes. HPA не устраняет утечку памяти и не гарантирует доступность внешней зависимости. CPU utilization не равен throughput, а отсутствие роста реплик не всегда означает ошибку autoscaling: контроллер может упереться в min/max, не получить метрику или увидеть, что целевой workload уже соответствует target. Учебную арифметику нельзя объявлять production-результатом без профиля на реальной нагрузке.</p>\n<h2>Критерий готовности изменения</h2>\n<p>Изменение можно считать объяснимым, когда для одного workload названы владелец и окно наблюдения; effective requests и limits зафиксированы по container; смысл readiness записан одной фразой; HPA metric имеет источник и знаменатель; rollout имеет timeout и rollback; а выбранный эффект измерен тем же evidence до и после. Формулировки «Pod стал лучше» и «процент выглядит нормально» этого критерия не закрывают.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Resource Management for Pods and Containers</a> — requests при планировании, limits и поведение контейнера при ограничении ресурсов.</li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Horizontal Pod Autoscaling</a> — resource metrics, расчёт utilization относительно request и ограничения HPA.</li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/pods/probes/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Liveness, Readiness, and Startup Probes</a> — назначение probes и удаление неготового Pod из Service traffic.</li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Deployments</a> — rolling update, selector и параметры maxUnavailable/maxSurge.</li></ul>"
|
||
}
|