У команды есть Deployment с HPA на средний CPU 70 процентов. После замены container image startup стал тяжелее, Pod коротко переходит Ready, а потом снова становится Unready. В ответ кто-то предлагает увеличить CPU limit и maxReplicas. Цена — спутать HPA signal с traffic eligibility и увеличить число Pod без понимания причины. Если не отделить request от limit и readiness от metric, рост replicas только усложнит разбор.
Другая ситуация ещё тише: в manifest нет CPU request, но стоит utilization target. Проблема скрыта именно в знакомом проценте, поэтому его считают рабочим. Однако в официальной модели Kubernetes CPU utilization HPA вычисляется относительно request; когда у container нет нужного request, controller не предпринимает действие по этой метрике. Цена — не только отсутствие scale. Команда получает число, которое выглядит как policy, но не имеет declared denominator, а затем ищет объяснение в synthetic или случайном графике вместо контракта.
Симптом → причина → проверка → действие
- Симптом. HPA target, resource limits и readiness существуют, но разные участники объясняют ими разные события: placement, traffic, CPU saturation и restart.
- Причина. Один Pod воспринимают как одну шкалу capacity. В действительности scheduler, runtime, Service и HPA отвечают на разные входы и в разные моменты.
- Проверка. Постройте четыре связи: profile → request, limit → resource boundary, readiness → traffic eligibility, metric → replica proposal. У каждой связи должны быть source, owner и failure meaning.
- Действие. Оставьте в policy только значения с объявленным смыслом. Если request отсутствует, utilization target не додумывают; если readiness false, не называют это автоматически resource incident; если metric missing, не рисуют real curve из fixture.
Модель: один manifest, четыре управляющих контура
Контур размещения начинается с request. Scheduler рассматривает requests при выборе Node, а для Pod общая величина по ресурсу складывается из container requests. Это правило не говорит, что container будет всё время потреблять request; оно описывает reservation contract для scheduling. Поэтому request должен соответствовать объявленной единице работы: например, serving unit и допустимому concurrency, а не просто «значению, после которого Pod когда-то перестал Pending». If sidecar, init или main container имеют разные роли, они требуют отдельных строк и явного общего вывода, а не одного красивого числа в ticket.
Контур ограничения начинается с limit. Kubelet и runtime применяют CPU и memory limits, но поведение ресурса не следует смешивать. В исторической версии документации описано, что runtime применяет memory limit с OOM error при попытке выделить больше разрешённого; реализация ограничения может быть reactive или enforcement, а детали зависят от runtime. Отсюда практический вывод скромнее популярных советов: CPU limit не является HPA target, memory limit не является прогнозом peak, а изменение обоих не заменяет проверку application lifetime. В учебном наборе нет реального runtime и потому нет утверждения о throttling, OOM, eviction или restart какого-либо Pod.
Контур traffic использует readiness. Kubelet переводит container в ready по readiness probe; когда Pod не ready, он исключается из Service load balancers. Pod lifecycle добавляет ещё одну границу: readiness gate требует, чтобы containers были ready и все указанные custom conditions стали True. Это удобно, когда приложение действительно нуждается в дополнительном явном соглашении. Но custom condition не даёт автоматической семантики: «feature loaded», «schema usable» и «dependency healthy» должны принадлежать owner-у и иметь failure policy. Gate, который никто не может привести в True при аварии, превращает capacity policy в скрытый single point of delay.
Контур replica proposal — HPA. Он периодически сопоставляет current metric с desired metric и формирует desired replicas. Для resource utilization CPU значение является процентом от requests контейнеров targeted Pod. Для raw target и custom или external metric интерфейс другой, а metrics API должен существовать отдельно. HPA не проверяет бизнес-готовность и не обещает, что новый Pod уже serving. Поэтому accurate design question звучит не «какой процент поставить», а «какое измерение репрезентирует pressure именно после readiness, с какой задержкой и при каком denominator». Если это неизвестно, placeholder target честнее, чем случайная кривая.
| Контур | Вход | Выход | Частая неверная подмена |
|---|---|---|---|
| Placement | CPU и memory request per container | возможность scheduler разместить Pod | request называют фактическим расходом или лимитом |
| Resource boundary | CPU и memory limit | ограничение runtime для container | limit называют запасом HPA либо гарантийным throughput |
| Traffic eligibility | readiness probe и readiness gates | допуск Pod к Service traffic | Ready называют доказательством latency или capacity |
| Replica proposal | metric, target, min/max, HPA algorithm | desired replica count | desired count считают числом ready backend и root-cause diagnosis |
| Evidence | разрешённый metric, condition или controlled test | подтверждение или опровержение profile | fixture или screenshot выдают за telemetry |
Почему процент CPU меняет смысл при изменении request
Представьте declared request 500m и targetAverageUtilization 70. Это формирует target около 350m usage на Pod в модели utilization, а не 70 процентов Node и не 70 процентов CPU limit 1000m. Если request увеличить до 1000m, то тот же target обозначает уже другую рабочую точку. Параллельно scheduler станет резервировать больше. Если request уменьшить, scale signal станет более чувствительным, но placement станет плотнее; это не автоматически хорошо или плохо. Значение можно менять только вместе с hypothesis о workload, потому что один YAML field служит двум механизмам.
Тут важна граница источника. Документация говорит, что HPA при missing CPU request не действует по resource utilization, и объясняет, как controller консервативно учитывает missing и not-yet-ready Pod для направления scale. Она не даёт права считать, что в конкретной среде работают default flags, metrics server доступен или metric собран нужного качества. В частности, `initial-readiness-delay` и CPU initialization period — параметры controller manager, а не свойства manifest. Их нельзя захардкодить в статью как универсальную задержку. Вместо этого profile должен открыть вопрос: когда serving metric становится репрезентативной и кто имеет право подтвердить это в выбранной среде.
Readiness и HPA встречаются, но не становятся одним сигналом
HPA учитывает readiness при обработке CPU metrics во время инициализации. Это не превращает readiness probe в autoscaling metric. Probe может быть корректной для traffic и совершенно непригодной для оценки future load: она отвечает только success или failure конкретного check. А CPU metric может быть корректной для replica proposal и не сообщать, что Pod умеет совершить важный доменный шаг. Полезно держать два вопроса рядом: «можно ли отправить запрос?» и «есть ли pressure, которое оправдывает изменение desired replicas?». Они могут получить разные ответы и всё ещё быть здоровой системой.
Исполнимый объект вместо вымышленных операционных данных
В учебном примере механизм проверяется только на закрытом наборе object literals. Input содержит model version, scope, mode fixed-memory-only и case id. Любой file-like, cluster-like, CI-like, network-like, HTTP-like, trace-like или production-like field отклоняется до анализа. Это намеренно уже, чем реальный capacity tool: fixture должна показать отношения между значениями, а не притвориться CLI. Значение syntheticObservedPodBehavior имеет собственный marker embedded-fixed-js-object-not-telemetry.
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 и отрицательных веток.
В case fixed-warmup-request-missing-v1 есть synthetic CPU 600m и false readiness. Model verdict не говорит «нужно больше Pod». Он говорит, что CPU percentage target нельзя интерпретировать без declared CPU request и что controller startup settings намеренно не читались. В case fixed-memory-growth-v1 false readiness не превращается в OOM event, потому что никаких runtime event не было получено. Это не слабость fixture: отрицание неразрешённого вывода является её главным учебным результатом.
Упорядоченный маршрут проектирования механизма
- Определить application profile. Назовите serving path, startup path, unit of work, dominant resource, concurrency boundary и owner-а выбора.
- Связать profile с request. Для каждого container объясните, зачем ему CPU и memory request. Проверьте effective defaults отдельным разрешённым способом, а не по предположению о limit.
- Сформулировать limits как boundaries. Укажите, какое поведение ожидается при приближении к CPU и memory границе, кто реагирует и чего rollback не восстановит.
- Разделить probes. Напишите отдельные contracts для startup, readiness и liveness; если есть readiness gate, добавьте producer condition и recovery path.
- Выбрать HPA metric. Укажите metric API, target type, denominator, min/max, behavior window и условия, при которых metric не следует считать evidence.
- Проверить одну связь. В согласованной среде сравните один declared input с одним разрешённым observation. Не меняйте request, readiness и HPA одновременно.
- Закрыть change record. Сохраните observed result, limitation, owner, stop condition и rollback. Только затем решайте, стал ли новый profile default.
Ограничения и следующий проверяемый шаг
Этот разбор не выбирает тип metric, values probes, containers count, Node size, CPU/memory ratio или правильный maxReplicas. Он не смотрит в admission controller, resource quota, PodDisruptionBudget, EndpointSlice, runtime cgroups, Metrics Server, custom adapter, dashboard, log, trace или production. Он также не обещает, что autoscaling устраняет queueing, memory retention, downstream saturation или dependency failure. Такой вывод возможен только после разрешённого evidence collection с временной и workload-boundary, а не после чтения historical documentation.
Следующий проверяемый шаг — взять один уже согласованный workload profile и сформировать matrix из пяти строк: per-container requests, per-container limits, readiness and startup conditions, HPA metric denominator, allowed evidence source. Для каждой строки добавьте «что этот сигнал не доказывает». После этого выберите ровно одну uncertainty для проверки в изолированной среде. Если она касается profile, не меняйте policy; если касается metric, не переписывайте probe. Маленькая изолированная проверка лучше общего scale-up, потому что сохраняет причинную связь.
Историческая граница июня 2024
Материал опирается на Kubernetes v1.30 release и snapshots официальной документации, датированные мартом, апрелем и маем 2024. Они были доступны до июня 2024 и ограничивают утверждения о request, limit, probes, readiness gates и HPA. Синтетическая capacity curve, profile cards и outcomes не описывают cluster state. Это fixed in-memory teaching material: без файлов, кластера, CI, сети, HTTP, trace, telemetry и production.
Проверяемые источники
- 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.