В манифест нового HTTP-сервиса часто попадает знакомая пара: CPU request 500m, limit 1000m, readiness probe и HPA с целью 70 процентов. В день выпуска это выглядит как законченная настройка. Но у приложения есть прогрев, очередь, память на рабочий набор и момент, когда оно действительно способно отвечать. Если числа пришли из соседнего сервиса, а не из профиля этой работы, scheduler резервирует одно, HPA считает процент от другого, а Service отправляет трафик по третьему сигналу. Цена — неверная граница ресурсов и неверная реакция на false readiness.
Вторая дорогая ошибка появляется после первой тревоги: limit объявляют запасом производительности, а readiness — универсальной проверкой здоровья. В Kubernetes request участвует в размещении, limit задаёт границу исполнения, а readiness решает допуск к Service traffic. Эти механизмы не заменяют друг друга. CPU target HPA в процентах также имеет конкретный знаменатель — request. Поэтому начальная инженерная схема должна сравнить три записи: что декларация разрешает Pod, какую нагрузку приложение обещает выдерживать как единица работы и что наблюдение должно означать, прежде чем его использовать.
Симптом → причина → проверка → действие
- Симптом. Pod помещается на Node, но после включения трафика растёт ожидание; либо HPA меняет replica count, а доступных backend не прибавляется.
- Причина. Request, limit, readiness и autoscaling настроены как независимые фрагменты. Профиль приложения — CPU-bound serving, memory-retaining batch или warmup — не назван.
- Проверка. Для каждого container выпишите request и limit отдельно, затем объясните одной фразой, что делает readiness, какую работу отражает metric и от какого request считается percentage target.
- Действие. Сначала зафиксируйте маленький profile contract. Затем отдельно решите, какая проверка допускает трафик, какая граница останавливает ресурс и какие evidence разрешены для изменения значения. Не увеличивайте replicas как ответ на ещё не классифицированный симптом.
Три слоя одного решения
Первый слой — декларация. В ней container получает CPU и memory request и limit. Официальная документация Kubernetes для исторического среза v1.30 говорит, что scheduler использует request при выборе Node, а kubelet и runtime применяют limit. Это уже достаточно, чтобы не называть limit «резервом для размещения». У container может оказаться больше свободного ресурса, чем request, если Node позволяет; limit относится к границе, а не к обещанию пропускной способности. Отдельно опасен незаметный случай: если limit задан, а request нет, admission может использовать limit как request. Его нельзя угадывать по шаблону — нужно записать, что именно считается effective request в разрешённой проверке.
Второй слой — управляемая нагрузка. Для web-сервиса это не строка «CPU 70%», а договор: что считается одной обслуживаемой единицей, как ведут себя startup и steady serving, какой ресурс ограничивает путь первым и где заканчивается область owner-а. Для batch-пути полезнее назвать время жизни рабочего набора, чем копировать CPU target из HTTP-сервиса. Для долгого warmup важно отделить инициализацию от готовности принимать запросы. Profile не обязан предсказывать всю production-кривую; его задача — сделать явным, какое предположение сейчас проверяется и какое наблюдение могло бы его опровергнуть.
Третий слой — наблюдаемое поведение Pod. Оно должно быть именовано точно: condition Ready, результат readiness probe, полученная метрика, restart, latency или очередь — не взаимозаменяемые слова. В этой статье нет снятых показаний. Учебная fixture ниже хранит значения только как versioned fixed synthetic JS-объекты в памяти и прямо называет их не-telemetry. Такой пример годится для проверки логики сопоставления, но не для вывода о живом Pod, kubectl, CI или HPA. Реальные evidence требуют отдельного разрешённого пути, владельца и временной границы.
| Слой | Рабочий вопрос | Что не доказывает | Следующее решение |
|---|---|---|---|
| Request | какой ресурс scheduler должен зарезервировать для объявленной единицы работы | не доказывает фактический расход, latency или готовность приложения | сверить с profile и effective admission result в отдельной проверке |
| Limit | какую верхнюю границу исполнения допускает container | не является target HPA и не обещает, что memory ошибка проявится мгновенно | разделить CPU boundary, memory boundary и recovery policy |
| Readiness | когда Pod допускается к Service traffic | не измеряет capacity, throughput и не заменяет liveness | определить один обслуживаемый контракт и owner failure semantics |
| HPA metric | какая измеряемая величина меняет desired replicas | не превращает отсутствующий request в meaningful percentage | зафиксировать denominator, source metric и handling missing data |
| Observed behavior | какой факт разрешённо собран и что он означает | не может быть заменён fixture, screenshot или случайным логом | связать с гипотезой до действия над manifest |
Readiness — сигнал допуска, а не диагноз
Readiness probe сообщает kubelet, когда container готов начать принимать трафик; unready Pod не должен быть backend для Service. Это полезный, но узкий смысл. Проверка может сказать «не обслуживаю» во время прогрева, во время controlled drain или когда приложению нужна обязательная зависимость. Она не обязана сказать, почему это произошло. Если readiness начинает проверять каждую внешнюю систему без принятого failure policy, краткая проблема зависимости способна вынуть сразу все backend из трафика. Если, наоборот, probe отвечает success до завершения прогрева, Service получает Pod, который ещё не держит целевой контракт. В обоих случаях увеличение maxReplicas не исправляет неверный смысл самого сигнала.
Requests, limits и autoscaling нельзя читать в одиночку
CPU request влияет и на размещение, и на resource utilization HPA. Для targetAverageUtilization controller берёт usage относительно relevant CPU request; если у Pod нет требуемого request, utilization не определён и autoscaler не действует по этой метрике. Поэтому target 70 процентов не означает «70 процентов Node» и не означает «70 процентов limit». Это 70 процентов именно выбранного request. Изменение request без пересмотра profile одновременно меняет placement economics и числовой смысл HPA target. Это не аргумент никогда не менять request; это аргумент менять их одной review-карточкой.
Memory требует ещё большей аккуратности. Request помогает scheduler решить, можно ли разместить Pod; limit задаёт предел container. Но память не всегда сообщает проблему той же формой и тем же временем, что CPU. Нельзя нарисовать универсальный коэффициент «memory limit = request × два» и назвать его безопасным. Сначала надо назвать владельца cache, batch или retained object, способ ограничить рост и действие после превысившего договор события. Autoscaling по CPU может быть полезен для serving workload, но сам по себе не объясняет memory lifetime. Если synthetic observation показывает false readiness и высокую synthetic memory величину, корректный следующий вопрос — о границе памяти и значении readiness, не о немедленном scale-up.
Исполнимый учебный пример без доступа к системе
Следующий код не читает манифест и не выполняет API-вызов. Его вход — только известный case id. После него report хранит fixed declaration, fixed managed load и fixed synthetic observed behavior, а plan создаёт лишь текстовые review-actions. Extra field, похожее на имя файла, cluster, CI, network, HTTP, trace или production, fixture отклоняет. Так пример не становится скрытой заменой разрешённой диагностики.
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-steady-http-v1 synthetic CPU 450m не является usage из metrics server и не разрешает менять request 500m. Он нужен лишь чтобы показать отношения внутри заданного объекта: HPA percentage относится к request, а не к limit. Case fixed-memory-growth-v1 намеренно не выдаёт synthetic memory за OOM event. Case fixed-warmup-request-missing-v1 отклоняет percentage conclusion, потому что CPU request в object равен null. PASS fixture означает только то, что эти границы не были случайно стёрты кодом.
Упорядоченный маршрут для настоящей проверки
- Назвать workload. Запишите owner, serving или batch shape, startup work, dominant resource и условие, при котором работа должна перестать принимать трафик.
- Разложить manifest. Выпишите per-container request и limit; не суммируйте их в одно «мощность Pod». Отдельно выясните разрешённым способом admission defaults и effective values.
- Определить readiness contract. Скажите, какой дешёвый факт означает «можно обслуживать», а какой не должен делать Pod permanently unready. Назначьте owner изменения semantic.
- Проверить metric contract. Для HPA укажите metric type, denominator или raw target, scope, min/max и поведение при missing values. Не выводите это из fixture.
- Собрать ограниченное evidence. Выберите разрешённую среду, период и source. Отличите Pod condition, usage, restart, latency и queue, прежде чем сопоставлять их с profile.
- Менять одну гипотезу. Измените request, limit, probe или autoscaling policy только с измеримым вопросом, stop condition и rollback. Затем повторите проверку того же контракта.
Ограничения, rollback и следующий проверяемый шаг
Эта схема не назначает CPU и memory для реального сервиса, не читает feature gates, controller flags, metrics API, admission policy, Node allocatable, logs или trace. Она не подтверждает, что Service, EndpointSlice, HPA или конкретная probe настроены в чьей-либо среде. В частности, историческая документация HPA описывает default окна initial readiness delay и CPU initialization period, но они являются cluster-wide controller settings; учебная модель их не предполагает и не подменяет доступом к control plane. Для реального изменения нужны отдельные права, security review, dry run, evidence, owner и rollback plan.
Rollback здесь должен быть таким же конкретным, как изменение. Для request или limit это не «вернуть числа назад» вслепую: сначала нужно понять, какие Pods уже получили новую декларацию и что считалось успехом. Для readiness изменение может вернуть traffic раньше или позже ожидаемого, поэтому его проверяют отдельно от HPA. Следующий проверяемый шаг — создать одностраничную profile card для одного workload: declaration per container, one serving contract, one dominant resource hypothesis, metric denominator, evidence source и stop condition. Пока карточка не заполнена, это design task, а не настройка capacity.
Историческая граница июня 2024
Материал использует только первичные Kubernetes источники, которые существовали до июня 2024: release v1.30 от 17 апреля и зафиксированные commits документации от марта до мая. Из них взяты ограниченные факты о requests, limits, probes, readiness gates и HPA. Все числа, Pod states и outcomes в учебном наборе — versioned fixed synthetic JS-объекты в памяти. Они не являются манифестом, kubectl, CI result, HTTP response, trace, telemetry или production observation.
Проверяемые источники
- 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.