8 lines
16 KiB
JSON
8 lines
16 KiB
JSON
{
|
||
"index": 128,
|
||
"slug": "editorial-2024-06-mechanism-container-orchestration",
|
||
"title": "Контейнерная нагрузка без догадок: как связать requests, readiness и HPA",
|
||
"excerpt": "После обновления образа Pod может стать Unready, а HPA — не изменить число реплик. Разбираем, какой механизм отвечает за placement, traffic и масштабирование, как проверить гипотезу и когда изменение манифеста действительно готово.",
|
||
"contentHtml": "<p>После обновления образа часть Pod долго остаётся на старой версии. Другие Pod переходят в <code>Ready</code>, но сразу теряют готовность. В ответ команда увеличивает <code>maxReplicas</code>, поднимает CPU limit и запускает rollout ещё раз. Симптомы меняются, а причина остаётся. Цена ошибки — лишние реплики, неуправляемая нагрузка на Node и более длинный путь отката. В худшем случае Service получает Pod, который ещё не готов обслуживать запросы.</p>\n<p>Тезис простой: Kubernetes не управляет контейнерной нагрузкой одной ручкой. Scheduler учитывает requests. Runtime ограничивает container по limits. Readiness решает, можно ли отправлять Pod трафик. HPA предлагает число реплик по своей метрике. Эти контуры связаны, но не заменяют друг друга. Пока у каждого значения нет явного смысла, процент CPU и статус <code>Ready</code> легко принять за доказательство capacity.</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 показывает процент, но реплики не растут</td><td>У container нет CPU request или метрика не имеет нужного знаменателя</td><td>Проверить request каждого container, тип metric и источник metrics API</td><td>Сначала исправить контракт метрики; не увеличивать replicas вслепую</td></tr><tr><td>Pod Unready после старта</td><td>Readiness проверяет недоступную зависимость, либо probe срабатывает раньше прогрева</td><td>Сопоставить probe, startup path, condition и Service endpoints</td><td>Изменить семантику readiness или порядок старта, затем повторить проверку</td></tr><tr><td>Pod Pending после изменения ресурсов</td><td>Сумма requests не помещается на доступные Node</td><td>Сравнить requests с allocatable, quota и правилами admission</td><td>Пересмотреть профиль workload или размещение</td></tr><tr><td>CPU limit увеличен, но задержка не исчезла</td><td>Причина находится в памяти, очереди или внешней зависимости</td><td>Разделить CPU, memory, queue, latency и dependency signals</td><td>Проверять один доминирующий ресурс, а не менять все поля сразу</td></tr></tbody></table></div>\n<h2>Как работают четыре контура</h2>\n<p><strong>Requests отвечают за размещение.</strong> Scheduler использует CPU и memory requests при выборе Node. Для Pod учитывается сумма requests контейнеров. Request не обещает постоянное потребление и не задаёт верхнюю границу. Это объявленная потребность, по которой система решает, может ли Pod быть размещён.</p>\n<p><strong>Limits задают границу ресурса.</strong> CPU и memory limit принадлежат container. Они не являются целью HPA. Memory limit не описывает безопасный размер cache, а CPU limit не обещает throughput. При изменении limit нужно знать, какое поведение ожидается после достижения границы: ограничение CPU, ошибка выделения памяти или другой runtime effect. Без этого число в YAML не объясняет проблему.</p>\n<p><strong>Readiness управляет допуском к трафику.</strong> Когда readiness probe возвращает failure, Kubernetes не считает Pod готовым backend для Service. Это полезный сигнал маршрутизации. Он не говорит, почему приложение не готово, насколько высока latency и хватит ли ему CPU при пике. Probe должна проверять короткий факт, которым владеет приложение или его платформа. Проверка десятка внешних зависимостей превращает краткий сбой одной зависимости в удаление Pod из трафика.</p>\n<p><strong>HPA предлагает desired replicas.</strong> Для CPU utilization процент рассчитывается относительно CPU request целевых Pod. Поэтому target в 70 процентов — не 70 процентов Node и не 70 процентов limit. При request 500m такой target имеет другой смысл, чем при request 1000m. Изменение request одновременно влияет на placement и на интерпретацию HPA. Это одна причина, чтобы менять оба решения в одной проверяемой гипотезе.</p>\n<figure><img src=\"/assets/editorial/2024/container-orchestration-2024-capacity-curve.svg\" alt=\"Учебная кривая связывает CPU utilization с declared request и отделяет target HPA от CPU limit\" loading=\"lazy\" /><figcaption>Кривая показывает отношения между величинами, а не состояние конкретного кластера. Target HPA имеет знаменателем request; limit — отдельная граница container.</figcaption></figure>\n<h2>Конкретный пример</h2>\n<p>Пусть приложение обслуживает HTTP-запросы. Для одного container объявлены <code>cpu request: 500m</code> и <code>cpu limit: 1000m</code>. HPA использует <code>targetAverageUtilization: 70</code>. В учебном примере значение 70 процентов относится к 500m request. Условная точка сравнения равна 350m usage на Pod. Это арифметика для объяснения знаменателя, а не наблюдение из кластера и не рекомендация для реального сервиса.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: orders\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: app\n image: registry.example/orders:sha256-example\n resources:\n requests:\n cpu: 500m\n memory: 384Mi\n limits:\n cpu: 1000m\n memory: 768Mi\n readinessProbe:\n httpGet:\n path: /ready\n port: 8080\n periodSeconds: 5\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>Этот фрагмент показывает форму контракта. Он не доказывает, что 500m достаточно, что endpoint отвечает быстро или что HPA получит метрики. В реальной системе нужно отдельно проверить effective manifest после admission, состояние metrics API, readiness transitions и фактический workload. Если CPU request убрать, процентный target теряет ожидаемый знаменатель. Если readiness отвечает <code>200</code> до завершения прогрева, Service направит трафик слишком рано. Если readiness зависит от внешней базы, краткий сбой базы может убрать все backend.</p>\n<h2>Отрицательный путь: почему масштабирование не лечит готовность</h2>\n<p>Рассмотрим запуск новой версии. Приложение мигрирует локальный cache 20 секунд, но readiness endpoint начинает отвечать успехом через две секунды. HPA видит CPU прогрева и предлагает больше реплик. Новые Pod повторяют тот же тяжёлый startup. Число реплик растёт, а полезная ёмкость не появляется. Это не доказательство, что HPA сломан. Сначала нужно отделить startup work от serving work.</p>\n<p>Обратная ошибка тоже опасна. Readiness проверяет внешний сервис, который не нужен каждому запросу. При коротком отказе зависимости все Pod становятся Unready, хотя основная функция могла бы продолжать работу. Увеличение replicas не помогает: новые Pod проходят ту же проверку и исключаются из Service. Действие находится в контракте readiness и failure policy, а не в capacity curve.</p>\n<h2>Упорядоченный маршрут проверки</h2>\n<ol><li><strong>Ограничьте workload.</strong> Назовите Deployment, owner, serving path, startup path, единицу работы и период наблюдения. Не смешивайте два сервиса в одну гипотезу.</li><li><strong>Зафиксируйте декларацию.</strong> Выпишите requests и limits каждого container, probe, startup settings, HPA metric, minReplicas и maxReplicas. Отделите написанное в manifest от effective values после admission.</li><li><strong>Назовите смысл Ready.</strong> Запишите короткое условие, после которого Pod действительно может принимать Service traffic. Отдельно запишите, что readiness не доказывает: например, throughput, latency или здоровье всех зависимостей.</li><li><strong>Проверьте знаменатель метрики.</strong> Для CPU utilization свяжите target с CPU request. Для raw, custom и external metrics укажите target type, selector и источник. При missing data остановите вывод, а не подставляйте число.</li><li><strong>Соберите одно evidence.</strong> Выберите разрешённый condition, metric или controlled test. У evidence должны быть источник, время, workload и ограничение интерпретации. Учебные значения не заменяют этот шаг.</li><li><strong>Измените одну гипотезу.</strong> Выберите request, limit, probe или HPA policy. Запишите ожидаемый сигнал, stop condition и rollback. Не меняйте четыре контура одним commit.</li><li><strong>Повторите ту же проверку.</strong> Сравните результат с первоначальной гипотезой. Если изменился тип evidence или workload, результат нельзя считать подтверждением.</li></ol>\n<h2>Ограничения</h2>\n<p>Эта модель не выбирает универсальные значения CPU и memory. Она не учитывает автоматически admission webhooks, ResourceQuota, PodDisruptionBudget, Node allocatable, runtime, Metrics Server, custom adapter, queueing, cache retention и downstream saturation. Официальная документация описывает общий механизм Kubernetes, но не сообщает конфигурацию конкретного кластера. Нельзя переносить учебную арифметику в production profile без измерения.</p>\n<p>Readiness не заменяет liveness и startup probes. HPA не устраняет утечку памяти и не гарантирует доступность внешней зависимости. Limit не превращается в SLO. Если причина не разделяется одним evidence, правильное действие — остановить изменение и уточнить контракт. Это отрицательный результат, но он дешевле массового rollout без объяснимого эффекта.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Изменение готово, когда для одного workload выполнены все условия: владелец назван; effective requests и limits зафиксированы по container; смысл readiness записан одной фразой; HPA metric имеет объявленный знаменатель и источник; выбранное evidence получено в указанном периоде; ожидаемый эффект измерим; stop condition и rollback проверяемы. Если после изменения команда всё ещё говорит только «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/tasks/run-application/horizontal-pod-autoscale/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Horizontal Pod Autoscaling</a> — расчёт resource utilization относительно request и условия работы HPA.</li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Pod Lifecycle and probes</a> — смысл readiness и её влияние на допуск Pod к Service traffic.</li></ul>"
|
||
}
|