На разбор приходит фраза: «Pod стал Unready, увеличим maxReplicas». Она кажется практичной, пока не задать первый вопрос: Unready по какому контракту? Если readiness скрывает Pod от Service traffic во время прогрева, а HPA смотрит на CPU относительно request, то number of replicas и traffic eligibility живут в разных контурах. Цена быстрого решения — новый манифест без понятной гипотезы: один Pod может быть не готов по зависимости, другой ещё стартует, а третьему вообще не определён CPU request. После этого любой график становится поводом спорить, а не доказательством причины.
Ещё один знакомый вывод звучит так: «CPU высок, значит capacity не хватает». Проблема в том, что в этой статье нельзя делать даже такой учебный вывод без declared denominator. HPA percentage — не число само по себе: для CPU utilization ему нужен resource request. Если в декларации request отсутствует, а в synthetic object есть CPU 600m, это не kubectl top, не CI, не telemetry и не команда увеличить replicas. Цена подмены особенно неприятна в warmup: команда тратит время на scale policy, хотя сначала нужно разделить startup work, serving work и metric contract. Полевой разбор начинается не с команды к кластеру, а с карточки сравнения.
Симптом → причина → проверка → действие
- Симптом. Один и тот же признак — high CPU, false readiness или memory near limit — хотят использовать как достаточную причину изменить replicas.
- Причина. Manifest, managed load и observed behavior не записаны рядом; слово «Pod» скрывает разные states и разные механизмы Kubernetes.
- Проверка. Для каждого случая сначала определите type signal: declared field, readiness condition, resource metric, restart event, latency или queue. Затем назовите, что signal не доказывает.
- Действие. Выберите одну проверяемую гипотезу и один разрешённый evidence source. Если signal не имеет contract, зафиксируйте blocker вместо изменения limit, probe или HPA.
Три карточки, не три истории о реальной среде
Ниже нет incident report. Это три versioned fixed synthetic JS-объекта из учебной fixture. У каждого есть declaredManifest, managedLoad и syntheticObservedPodBehavior. Последний явно помечен как embedded-fixed-js-object-not-telemetry. Его numbers нельзя переносить в capacity calculator, использовать как SLO, называть output kubectl или сравнивать с реальным production. Зато на этих карточках можно проверить дисциплину: сначала прочитать заявленную границу, потом profile, потом смысл synthetic observation и только после этого сформулировать next question.
| Case id | Декларация и profile | Synthetic behavior | Допустимый verdict |
|---|---|---|---|
| fixed-steady-http-v1 | CPU request 500m, target 70%, steady HTTP CPU-bound shape | Ready true, synthetic CPU 450m | сверить profile, request и HPA denominator; не делать capacity claim |
| fixed-memory-growth-v1 | memory request 256Mi, limit 512Mi, bounded batch with retention question | Ready false, synthetic memory 470Mi | разделить memory lifetime, readiness semantic и replica action; не называть OOM |
| fixed-warmup-request-missing-v1 | CPU request null, CPU utilization target 65%, warmup before serving | Ready false, synthetic CPU 600m | отклонить percentage conclusion до declared request и startup contract |
| Любой внешний field | file, cluster, CI, network, HTTP, trace или production | не принимается fixture | отклонить input; fixture не является reader или operator |
Карточка 1: стабильный serving не делает число автоматически верным
В fixed-steady-http-v1 declaration фиксирует request 500m, limit 1000m и target 70 процентов request. Managed profile называет нагрузку steady HTTP и dominant resource CPU. Synthetic observation — Ready true, CPU 450m, memory 310Mi. Из этих данных нельзя заключить, что сервис реально выдерживает нагрузку или что 500m достаточно. Но можно увидеть правильный порядок: 70 процентов имеет declared denominator 500m, а limit 1000m не меняет этот denominator. Если команда меняет request, она обязана пересмотреть и scheduling contract, и HPA interpretation, а не только нарисовать новый threshold.
Практическая проверка после согласования прав должна узнать не «какой Pod красивее на графике», а покрывает ли request определённую serving unit и соответствует ли metric именно этой unit. Если CPU usage взят после readiness и во время выбранного сценария, его можно сопоставить с profile. Если usage пришёл из startup, sidecar, stale interval или неизвестного selector, он может не отвечать на вопрос. Фикстура не делает эту проверку, потому что она не читает cluster, network, HTTP, trace или telemetry. Она оставляет owner-у сформулированный вопрос и отказывается открыть данные за него.
Карточка 2: false readiness не является именем причины
В fixed-memory-growth-v1 profile подчёркивает memory retention question, а readiness false означает только «приложение объявило, что не может обслуживать». Documentation Kubernetes говорит, что unready Pod не получает Service traffic; это действие routing layer, а не диагноз. Synthetic memory 470Mi при limit 512Mi не говорит, что в реальном runtime уже произошёл OOM, throttling, eviction или restart. Учебный пример специально хранит строку no-real-restart-or-oom-event-is-represented, чтобы не позволить превратить близость к limit в выдуманный incident.
Рациональная следующая ветка — разложить lifetime памяти: cache, batch buffer, response aggregation, connection pool или другой owner. Затем определить, когда readiness должен false, как он становится true снова и не зависит ли эта condition от traffic, которого Pod уже не получает. Только после этого можно обсуждать request/limit или число replicas. CPU HPA может оставить synthetic memory cause нетронутой: он принимает решение по своему metric, а не собирает memory root cause. Если возникают и memory pressure, и traffic loss, это два сигнала для связи с profile, не приглашение сложить их в одно магическое значение.
Карточка 3: warmup без request блокирует процентный вывод
В fixed-warmup-request-missing-v1 заявлен targetAverageUtilization 65, но CPU request null. Historical HPA documentation прямо связывает CPU utilization с request и указывает, что при отсутствии relevant request autoscaler не действует по этому metric. Следовательно, object не должен пройти через «CPU 600m — значит scale up». Synthetic 600m лишь служит проверкой отрицательного пути. Первым вопросом становится: какая serving unit даёт CPU request смысл, а вторым — какие controller startup settings и metric timing реально подтверждены в отдельной среде.
Исполнимый пример проверяет отказ от скрытого доступа
Фикстура легко запускается локально, потому что не требует credentials, namespace, kubeconfig, filesystem, HTTP или CI. Она не вызывает `kubectl`, не создаёт Pod, не читает manifest и не делает HTTP probe. Case id выбирает только заранее записанный object literal. Внешний field отклоняется: поэтому не получится незаметно передать имя deployment.yaml, cluster name, CI URL, network endpoint, trace ID или production marker. Это не ограничение production-инструмента; это честная граница учебного кода.
import {
createFixedSyntheticContainerLoadInput,
inspectSyntheticContainerLoad,
planSyntheticContainerLoadReview,
runContainerOrchestrationFixture,
} from './upgrade-2024-06.mjs';
const input = createFixedSyntheticContainerLoadInput('fixed-steady-http-v1');
const report = inspectSyntheticContainerLoad(input);
const review = planSyntheticContainerLoadReview(report);
if (!Object.values(runContainerOrchestrationFixture().assertions).every(Boolean)) {
throw new Error('fixed synthetic fixture failed');
}
console.log({ verdict: report.verdict, actions: review.actions });
// Только versioned fixed synthetic JS-объекты в памяти.
// Не читаются файлы, кластер, CI, сеть, HTTP, trace или production.
// syntheticObservedPodBehavior — не kubectl, не CI и не telemetry.
node web/scripts/upgrade-2024-06.mjs --verify-fixture
# PASS подтверждает только согласованность fixed synthetic objects и отрицательных веток.
Проверка сравнивает весь canonical report, а не один verdict. Если подменить syntheticObservedPodBehavior.ready, добавить kubectlOutput или заменить verdict на scale-now, plan отказывает. Rollback принимает только untouched synthetic review draft и возвращает «not read or changed» для manifest и cluster. Такой механизм не доказывает, что реальный rollback безопасен. Он доказывает только, что пример не превратился в канал управления окружением, пока читатель тренирует сравнение трёх слоёв.
Упорядоченный маршрут диагностики после получения разрешения
- Ограничить scope. Назначьте один workload, owner, изолированную среду, период проверки и разрешённые источники. Не начинайте с массового scan Pod.
- Снять declaration. Зафиксируйте per-container requests и limits, readiness/startup configuration, HPA metric type и target. Отличите written field от effective admission result.
- Написать profile. Назовите serving unit, startup unit, concurrency, dominant resource, dependency policy и meaning Ready/Unready.
- Собрать один signal. Получите по разрешённому маршруту condition либо metric либо controlled test. Не склеивайте их в один verdict до interpretation.
- Сравнить с contract. Скажите, согласуется ли signal с одной declared hypothesis, какие unknown остаются и что signal не доказывает.
- Выбрать действие. Разрешите ровно одно изменение, только если есть owner, expected result, stop condition и rollback. Для missing request или unknown readiness semantics action — blocker.
- Повторить тот же вопрос. После изменения проверьте именно первоначальную hypothesis тем же type evidence; иначе результат нельзя сопоставить.
Ограничения, rollback и следующий проверяемый шаг
Материал не подсказывает команду для кластера и не заменяет ручной review production policy. В нём нет наблюдения за actual pod lifecycle, Node pressure, Service endpoints, metrics server, autoscaler behavior, CI job, logs или distributed trace. Там нет утверждения, что range request/limit подходит для чьего-то языка, runtime, image, cache или external dependency. Даже официальный documentation snapshot объясняет generic semantics, но не знает admission webhooks и runtime configuration конкретного владельца. Поэтому нельзя превратить chart, fixture PASS или article table в permission изменить manifest.
Rollback для реального изменения должен содержать snapshot исходной декларации, owner решения, condition для остановки, временную границу и проверку side effect. Откат HPA не исправляет невнятную readiness condition; возврат limit не рассказывает, что произошло с application memory; изменение probe не восстанавливает evidence. Следующий проверяемый шаг — провести review одной profile card с platform owner и application owner. На выходе нужны: declared request denominator, separate definition of Ready, named allowed metric source и explicit blocker для непроверенной части. Если хотя бы одна строка остаётся «кажется», change не готов.
Историческая граница июня 2024
К июню 2024 уже были опубликованы Kubernetes v1.30 и использованные здесь первичные documentation snapshots. Их положения применены узко: request и limit имеют разные роли; readiness управляет traffic eligibility; readiness gates добавляют named conditions; CPU utilization HPA относится к request и имеет обработку not-yet-ready Pod. Три карточки, values и verdict учебной модели являются только fixed synthetic JS-объектами в памяти. Они не имитируют и не выдают себя за cluster query, kubectl, CI, telemetry, HTTP, trace или production data.
Проверяемые источники
- Kubernetes v1.30: Uwubernetes, 17.04.2024 — Первичное объявление Kubernetes Release Team фиксирует доступность v1.30 17 апреля 2024, то есть до июня 2024. Оно помогает задать историческую границу версии, но не говорит, какая версия, feature gate, controller flags или аддоны есть в чужом кластере.
- Kubernetes documentation: Resource Management for Pods and Containers, snapshot 07.03.2024 — Первичный неизменяемый снимок документации до июня 2024: scheduler использует request при выборе Node, kubelet и runtime применяют limit, а limit без request может стать request. Это описание механизма Kubernetes, не рекомендация конкретных чисел CPU или memory.
- Kubernetes documentation: Horizontal Pod Autoscaling, snapshot 26.03.2024 — Первичный снимок HPA до июня 2024: CPU utilization считается относительно request; при отсутствии нужного request controller не действует по этой метрике. Документ также описывает обработку missing и not-yet-ready Pod, но не доказывает наличие metrics API либо controller settings в конкретной среде.
- Kubernetes documentation: Pod Lifecycle and readiness gates, snapshot 24.05.2024 — Первичная документация описывает Ready, ContainersReady и readinessGates: custom condition должна быть True вместе с готовностью контейнеров. Она не задаёт семантику доменной condition, owner-а этой condition или надёжность внешней зависимости.
- Kubernetes documentation: Configure Liveness, Readiness and Startup Probes, snapshot 13.04.2024 — Первичный снимок probes до июня 2024: readiness определяет допуск к Service traffic, startup probe задерживает запуск liveness и readiness. Это не обещание, что проба измеряет latency, throughput, dependency health или реальную capacity.