Files
progcode/editorial/agent-rewrites/127.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
18 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 127,
"slug": "editorial-2024-06-field-container-orchestration",
"title": "Kubernetes без ложных сигналов: как связать ресурсы, readiness и HPA",
"excerpt": "После релиза Pod может быть Unready, CPU — высоким, а HPA — не менять число реплик. Разбираем, какой контур породил симптом, чем его проверить и когда изменение манифеста действительно готово.",
"contentHtml": "<p>После обновления образа часть Pod-ов долго остаётся <code>Unready</code>. В графике растёт CPU. Команда предлагает увеличить <code>maxReplicas</code> и CPU limit. Это может не изменить ни одного симптома. Readiness управляет допуском Pod к трафику Service. HPA рассчитывает реплики по метрике. Scheduler размещает Pod по requests. Runtime применяет limits. Один Pod, четыре контура.</p>\n<p>Цена ошибки — не только лишние ресурсы. Новый Pod может не пройти readiness из-за зависимости. HPA может не считать CPU, если у контейнера нет request. Увеличенный limit может скрыть рост памяти до следующего отказа. Если изменить все поля сразу, команда потеряет причинную связь. Она не узнает, что именно сработало и какой риск остался.</p>\n<p><strong>Тезис.</strong> Сначала нужно назвать контракт сигнала, затем проверить его источником того же типа. Не называйте <code>Ready</code> доказательством capacity. Не называйте CPU percentage самостоятельным числом. Не называйте значение из учебной модели показанием кластера.</p>\n<h2>Механизм: четыре контура вместо одной «нагрузки»</h2>\n<p><strong>Request</strong> задаёт reservation contract. Scheduler учитывает requests контейнеров при выборе Node. Для одного ресурса request Pod складывается из requests его контейнеров. Это не прогноз постоянного потребления. Это условие размещения.</p>\n<p><strong>Limit</strong> задаёт границу ресурса для контейнера. CPU и memory ведут себя по-разному. CPU limit может ограничивать выполнение. Memory limit не превращается в прогноз пикового потребления и не объясняет lifetime cache или batch buffer. Нельзя вывести безопасные значения из одного универсального коэффициента.</p>\n<p><strong>Readiness</strong> отвечает на другой вопрос: можно ли отправлять трафик этому Pod сейчас. Когда Pod не готов, Service не должен использовать его как backend. Readiness probe не обязана объяснять причину. Readiness gate добавляет named condition, но не задаёт ей смысл. Владелец приложения должен определить producer, переход в <code>True</code> и путь восстановления.</p>\n<p><strong>HPA</strong> формирует предложение по числу реплик из метрики и target. CPU utilization — процент относительно CPU request контейнеров, которые попали в выборку. Если relevant request отсутствует, controller не может получить такой utilization для контейнера. Значит, строка <code>target: 65</code> без request не является рабочим scaling contract.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: api\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: api\n image: registry.example/api@sha256:...\n resources:\n requests:\n cpu: 500m\n memory: 256Mi\n limits:\n cpu: \"1\"\n memory: 512Mi\n startupProbe:\n httpGet:\n path: /startup\n port: 8080\n readinessProbe:\n httpGet:\n path: /ready\n port: 8080\n readinessGates:\n - conditionType: example.com/SchemaReady\n---\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n name: api\nspec:\n scaleTargetRef:\n apiVersion: apps/v1\n kind: Deployment\n name: api\n minReplicas: 2\n maxReplicas: 10\n metrics:\n - type: Resource\n resource:\n name: cpu\n target:\n type: Utilization\n averageUtilization: 70</code></pre>\n<p>Фрагмент учебный. Образ, пути probes, custom condition и значения ресурсов нельзя переносить в production без profile приложения и проверки среды. В манифесте видно главное: HPA percentage имеет denominator <code>requests.cpu: 500m</code>. Readiness gate не становится HPA metric. Limit <code>1</code> не меняет denominator HPA.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class=\"table-scroll\"><table><caption>Диагностическая матрица для одного workload</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Возможная причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Pod Unready после релиза</td><td>Probe или gate не выполняет контракт; зависимость ещё не готова</td><td>Сопоставить condition, probe event и смысл Ready</td><td>Исправить owner и recovery path; не увеличивать replicas автоматически</td></tr><tr><td>CPU высокий, HPA не меняет реплики</td><td>Нет CPU request, нет metrics API или выбран другой target type</td><td>Проверить effective request, HPA status и источник метрики</td><td>Сначала восстановить denominator или метрику; не рисовать scale policy по одному графику</td></tr><tr><td>Memory близка к limit</td><td>Рост cache, batch, retained object или неверный limit</td><td>Определить lifetime памяти и проверить runtime evidence</td><td>Ограничить владельца роста; отделить memory action от CPU HPA</td></tr><tr><td>Новые Pod запускаются, но трафик не растёт</td><td>Readiness false, gate не становится True или Service не видит endpoint</td><td>Проверить Pod condition и endpoints разрешённым способом</td><td>Проверить traffic contract; не считать создание Pod доказательством capacity</td></tr><tr><td>Процент выглядит убедительно только в fixture</td><td>Синтетическое число приняли за telemetry</td><td>Проверить источник и marker данных</td><td>Ограничить вывод учебной моделью и запросить реальное evidence отдельно</td></tr></tbody></table></div>\n<h2>Конкретный пример: почему request меняет смысл процента</h2>\n<p>Пусть CPU request равен <code>500m</code>, а HPA target — 70%. В модели controller это означает usage около 350m на Pod для целевой точки. Это 70% request, а не 70% Node и не 70% CPU limit. Если request изменить на <code>1000m</code>, тот же target будет означать другую рабочую точку. Одновременно Scheduler начнёт резервировать больше CPU. Одно изменение затронет placement и interpretation метрики.</p>\n<p>Теперь уберём request. Значение synthetic CPU <code>600m</code> всё ещё выглядит конкретно, но процент больше не имеет объявленного denominator. Корректный verdict — «нельзя интерпретировать utilization», а не «нужно больше Pod». В этом отрицательном пути отсутствие действия HPA — ожидаемый результат проверки контракта. Сначала нужно определить serving unit, для которой request имеет смысл, и подтвердить состояние metrics API в разрешённой среде.</p>\n<p>Другой пример — memory. Если synthetic observation показывает 470Mi при limit 512Mi, это не доказывает OOM, eviction, restart или throttling. Без runtime event это только учебное значение рядом с границей. Следующий вопрос относится к владельцу памяти: cache, batch buffer, response aggregation или connection pool. Readiness false при этом остаётся отдельным сигналом traffic, а не именем причины memory pressure.</p>\n<figure><img src=\"/assets/editorial/2024/container-orchestration-2024-readiness-gate.svg\" alt=\"Схема readiness gate: готовность контейнеров и custom condition вместе формируют Ready и допуск к Service traffic; HPA CPU сравнивает метрику с request отдельно\" loading=\"lazy\" /><figcaption>Учебная схема показывает две границы. Readiness определяет traffic eligibility. HPA использует свою метрику и свой denominator. Asset не описывает состояние реального кластера.</figcaption></figure>\n<h2>Как проверять безопасно</h2>\n<p>Проверка должна отвечать на один вопрос и использовать один тип evidence. Условие Pod, HPA status, metrics API, controlled request и runtime event не взаимозаменяемы. Если источник не разрешён, его отсутствие фиксируют как blocker. Не подменяйте его значением из fixture, screenshot или случайным графиком.</p>\n<ol><li><strong>Ограничьте scope.</strong> Выберите один Deployment, owner, среду, период и разрешённые источники. Не начинайте с массового изменения Pod.</li><li><strong>Снимите декларацию.</strong> Выпишите requests и limits для каждого контейнера. Отдельно зафиксируйте startup, readiness, gates, HPA metric, target и min/max.</li><li><strong>Опишите profile.</strong> Назовите serving unit, startup work, concurrency, dominant resource, dependency policy и точный смысл Ready.</li><li><strong>Проверьте denominator.</strong> Для utilization найдите relevant request и effective values после admission. Если request отсутствует, остановите процентный вывод.</li><li><strong>Разделите сигналы.</strong> Сопоставьте readiness с traffic, metric с replica proposal, limit с runtime boundary, request с placement. Запишите, чего каждый сигнал не доказывает.</li><li><strong>Выберите одну гипотезу.</strong> Назначьте один evidence source, ожидаемый результат и stop condition. Не меняйте request, probe и HPA в одном эксперименте.</li><li><strong>Проверьте отрицательный путь.</strong> Зафиксируйте, что произойдёт при missing request, false gate, stale metric или memory growth. Отсутствие решения иногда и есть корректный результат.</li><li><strong>Закройте изменение.</strong> Сохраните observed result, owner, ограничение и rollback snapshot. Повторите исходный вопрос тем же типом evidence.</li></ol>\n<h2>Что не должна делать учебная fixture</h2>\n<p>Учебный код может держать три фиксированные карточки в памяти: steady serving с request, memory growth с false readiness и warmup без request. Он может проверять, что synthetic value не получила ярлык telemetry и что внешний field отклоняется. Он не должен читать kubeconfig, namespace, файл манифеста, CI, HTTP, trace или production. Комментарий <code>syntheticObservedPodBehavior</code> должен прямо говорить, что это не <code>kubectl</code> и не metrics API.</p>\n<pre><code>const card = {\n id: 'fixed-warmup-request-missing-v1',\n declared: { cpuRequest: null, cpuTarget: 65 },\n observed: {\n kind: 'embedded-fixed-js-object-not-telemetry',\n ready: false,\n cpu: '600m'\n }\n};\n\nconst verdict = card.declared.cpuRequest === null\n ? 'block-utilization-conclusion'\n : 'compare-with-request';\n\nconsole.log(verdict);\n// Учебная модель. Нет cluster, file, CI, HTTP или production data.</code></pre>\n<p>Для этой карточки корректен verdict <code>block-utilization-conclusion</code>. Код не говорит, сколько реплик нужно реальному сервису. Он проверяет только отрицательную ветку: процент без request нельзя честно объяснить. Production-инструмент требует отдельной авторизации, источника данных и правил изменения. Учебный пример эти полномочия не получает.</p>\n<h2>Ограничения и rollback</h2>\n<p>Материал не выбирает размер Node, ratio CPU и memory, значения probes, тип custom metric или безопасный maxReplicas. Он не видит admission webhooks, quotas, EndpointSlice, runtime cgroups, Metrics Server, custom adapter, logs, traces и downstream dependencies. Официальная семантика Kubernetes не заменяет проверку конкретного кластера.</p>\n<p>Rollback должен возвращать snapshot декларации и проверять side effects. Откат HPA не исправляет неверный readiness contract. Возврат limit не объясняет рост памяти. Изменение probe не восстанавливает потерянные evidence. Для каждого изменения укажите owner, условие остановки, временную границу и наблюдаемый критерий отката.</p>\n<p><strong>Критерий готовности.</strong> Изменение готово, если profile назван, у каждого сигнала есть источник и owner, CPU utilization имеет declared request, Ready имеет отдельный traffic contract, synthetic данные не выданы за production, а отрицательная ветка приводит к остановке или явному blocker. Кроме того, команда может повторить исходную проверку тем же типом evidence и получить объяснимый результат. Если хотя бы одна строка отвечает «кажется», манифест не готов.</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 используются при размещении, а request и limit имеют разные роли.</li><li><a href=\"https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Configure Liveness, Readiness and Startup Probes</a> — Ready зависит от состояния контейнеров и readiness gates; readiness определяет доступность Pod для трафика.</li><li><a href=\"https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes: Horizontal Pod Autoscaling</a> — CPU utilization рассчитывается относительно requests, а отсутствие request ограничивает обработку этой метрики.</li></ul>"
}