В учебной карточке steady-slo-cost-drift p95 равен 235 ms при synthetic SLO 300 ms. Симптом выглядит спокойным: latency проходит условие. Цена ошибки появляется рядом: модельная стоимость за 24 часа уже равна 11,424 synthetic-unit, потому что в ней есть fixed base и выделенные CPU/memory. Если назвать это «здоровой системой» только по SLO, команда не заметит, какую связь она перестала проверять: нагрузка выросла, reservation остался без владельца, а следующий лимит выбирают по привычке. Это не счёт и не описание чьей-либо среды; это фиксированный object literal из fixture.
Причина не в том, что SLO плох. SLO отвечает на отдельный вопрос о latency, а не на вопрос о том, какая billing unit участвует в модели. Проверка должна разложить карточку на четыре поля: workload → resource → unit cost → limit. Действие тоже маленькое: для одного scope записать период, request, limit, квоту, unit и stop condition, а затем сравнить только два synthetic варианта. Такой порядок не обещает экономию. Он не даёт произвольному проценту загрузки выдать себя за доказательство цены.
Одна capacity-card вместо трёх несвязанных графиков
Capacity-card — не новый формат отчётности. Это короткий contract для одного обсуждения. В ней workload хранит baseline и peak request rate, а также p95 и выбранный SLO. Resource хранит request и limit CPU/memory, а quota показывает доступную границу namespace. Billing хранит fixed base, CPU-hour и GiB-hour как synthetic-unit. Последнее поле — limit — называет, что останавливает сравнение: близость p95 к SLO, saturation signal или слишком большая зарезервированная ёмкость при низком observed utilisation. Если хотя бы одно поле отсутствует, фраза «дорого» ещё не диагностическая.
Документация Kubernetes в снимке мая 2024 помогает не смешать request с limit. Scheduler использует request при размещении, а limit имеет отдельную семантику выполнения. Поэтому в карточке нельзя написать «1,2 CPU» и считать, что это одновременно фактический usage, гарантированная производительность и стоимость. Также нельзя вывести цену из quota: ResourceQuota выражается в абсолютных единицах namespace, а сумма quota может превышать ёмкость кластера. Она не доказывает доступную capacity. Здесь quota нужна только как дисциплина полей. Какая именно единица оплачивается, задаёт отдельный billing contract, а не Pod spec.
Связь workload → resource → unit cost → предел
Начинаем с workload. В примере baseline равен 120 request/s, peak — 180 request/s. Эти значения не умножаются на цену напрямую: они помогают проверить, с каким потоком связаны p95 и reservation. Затем берём resource: request 1,2 CPU и 2 GiB, limit 1,6 CPU и 3 GiB. Условная формула считает CPU по request, а не по observed 58%. Это намеренный выбор модели, чтобы показать вопрос: какую величину считает provider или внутренний allocation rule? В настоящем контуре ответ берут из разрешённого billing contract, а не угадывают по графику utilisation.
Третье поле — unit cost. В fixture fixed base равен 0,36 u/h, CPU равен 0,08 u/core-hour, memory — 0,01 u/GiB-hour. У формулы есть период 24 часа. Она возвращает 0,476 u/h и 11,424 u за период. Никакое число не является валютой, ценой provider или прогнозом. Важна структура: fixed часть не меняется от observed CPU, variable CPU и memory меняются от выбранного allocation, а workload нужен, чтобы посчитать условный cost per thousand requests и сравнить равные интервалы. Если период или scope разный, сравнение завершается до арифметики.
| Case | Workload и p95 | Reservation | Модельная стоимость | Предел/следующий вопрос |
|---|---|---|---|---|
steady-slo-cost-drift | 120/180 req/s; p95 235 ms при SLO 300 ms | 1,2 CPU; 2 GiB; limit 1,6 CPU | 11,424 u; SLO выполнен | Нет stop; сравнить один следующий limit с отдельным evidence. |
next-limit-balanced | 150/205 req/s; p95 248 ms | 1,4 CPU; 2,2 GiB; limit 1,8 CPU | 11,856 u | Нет stop; delta до 1,6 CPU записана как 0,384 u/24h. |
saturation-stop | 172/228 req/s; p95 296 ms | 1,6 CPU; 2,4 GiB; limit 2 CPU | 12,288 u | Stop: p95 headroom 4 ms; не выбирать следующий limit внутри fixture. |
Таблица не выбирает победителя. Она защищает границу сравнения. Первый case показывает, почему «SLO зелёный» и «cost понятен» — разные утверждения. Второй case делает marginal delta явной, но не превращает её в разрешение на изменение. Третий case показывает stop раньше попытки «дать ещё CPU»: p95 почти касается заданной границы, поэтому fixture меняет вопрос с арифметики на отдельный human review. Когда эти три условия записаны рядом, больший instance перестаёт быть рефлекторным ответом.
Компактный воспроизводимый расчёт
Команда ниже запускает только модуль этой ревизии. Вход — закрытый case id, а report строится из embedded literals. Функция не читает manifest, cloud account, invoice, Prometheus, CI и network. Полезный результат запуска — увидеть отдельные fixed и variable части, а не получить рекомендацию к изменению ресурса. Изменение totalFor24h или добавление поля с export в fixture приводит к отрицательной проверке, потому что карточка должна оставаться учебной и детерминированной.
import { createFixedSyntheticCapacityInput, inspectSyntheticCapacityCost } from './upgrade-2024-10.mjs';
const report = inspectSyntheticCapacityCost(
createFixedSyntheticCapacityInput('steady-slo-cost-drift'),
);
console.log({
p95HeadroomMs: report.signals.p95HeadroomMilliseconds,
fixedPerHour: report.cost.fixedPerHour,
variableCpuPerHour: report.cost.variableCpuPerHour,
totalFor24h: report.cost.totalForInterval,
nextQuestion: report.nextHumanQuestion,
});
Внутри расчёта есть ещё один осторожный показатель: unitsPerThousandRequests. Он делит synthetic cost периода на число synthetic requests периода. Это удобно только потому, что обе величины принадлежат одной карте, одному периоду и одному fixed literal. Он не становится unit economics продукта, пока не известны реальные billing rule, retries, cache, network, commitments и границы shared infrastructure. Хорошая проверка здесь звучит так: «мы можем повторить деление по той же карточке», а не «мы получили стоимость запроса».
Что SLO не показывает о росте reservation
SLO не обязан отвечать за стоимость. При p95 235 ms есть как минимум три разных объяснения растущей model cost: был повышен request, изменилась memory reservation или в модели существует fixed base. Observed CPU 58% не отменяет ни одно из них. Он лишь ставит вопрос, соответствует ли allocation характеру нагрузки. И наоборот, высокий процент не устанавливает цену: он может быть следствием маленького request, длинного временного окна, burst или saturation. Поэтому порядок «процент → стоимость» в статье запрещён; нужен промежуточный resource и explicit billing unit.
Quota и LimitRange помогают назвать другой класс границ. Quota может потребовать request/limit и ограничить aggregate value; LimitRange может задать default, minimum, maximum или ratio на admission. Они полезны для договорённости о допустимых значениях, но не являются ценовым калькулятором и не меняют уже запущенные Pod задним числом. В capacity-card мы отмечаем их для того, чтобы не предложить next limit, который уже не пройдёт admission. Сначала проверяется допустимость формы, затем отдельная модель, затем — только при полном разрешённом контексте — изменение.
Маршрут проверки без ложной автоматизации
- Назовите один scope и период. Запишите workload, owner, интервал и SLO. Не складывайте в одну строку разные сервисы, регионы или периоды.
- Разделите resource и usage. Отдельно зафиксируйте request, limit, quota и observed signal. Если какая-то величина неизвестна, не подставляйте процент загрузки вместо неё.
- Назовите billing unit. Укажите fixed/variable часть, базовую единицу и правило tier. Без этого «стоимость растёт» остаётся симптомом, а не расчётом.
- Сравните только одинаковые cards. Период, scope и formula должны совпасть. Затем сравните один следующий limit, а не каталог instance.
- Запишите stop condition и return boundary. Если p95 близок к SLO или есть saturation, остановите подбор. Return card в fixture — запись для review, не rollback command.
- Передайте открытый вопрос владельцу. Реальные billing, telemetry и policy требуют отдельного разрешённого evidence. Положительный fixture не подменяет его.
Ограничения карточки и конкретный следующий шаг
Эта revision не читает облачный счёт, каталог SKU, commitment, скидку, метрики, лог, cluster state или deployment. Она не утверждает, что request оплачивается в конкретном provider, что 0,36 u/h существует как тариф или что меньшая reservation безопаснее. Kubernetes snapshots в конце фиксируют роль request, limit, quota и admission; Google Cloud Billing proto фиксирует форму usage unit и tiered rates. Ни один источник не переносит собственную семантику на synthetic literals.
Следующий проверяемый шаг — провести tabletop review с одной пустой capacity-card. Один человек вписывает вымышленные workload, resource, unit и stop condition; второй пытается найти пропущенную связь, не видя никакой настоящей среды. Успех — не «нашли экономию», а точный ответ: какая величина фиксирована, какая variable, чем подтверждается billing unit, какой предел останавливает сравнение и кто владеет следующим источником evidence. Только после этого форму можно сопоставить с разрешённым процессом организации.
Историческая граница октября 2024
В октябре 2024 уже существовали закреплённые выше documentation snapshots Kubernetes и Google API contract. Из них взяты узкие технические различия: request/limit/quota относятся к ресурсной модели Kubernetes, а billing expression может иметь usage unit, base unit и tiered rate. Остальное — авторская fixed synthetic модель версии synthetic-capacity-cost-model/2024-10-v1. Она объясняет, как держать четыре поля в одной карте, но не измеряет реальную инфраструктуру и не даёт production advice.
Проверяемые источники
- Kubernetes documentation: Resource requests and limits, immutable snapshot 06.05.2024 — Первичный неизменяемый снимок документации Kubernetes. Он подтверждает, что scheduler учитывает request, а CPU/memory limit имеют отдельную семантику исполнения. Он не задаёт цену ресурса, модель счёта или удачный размер instance.
- Kubernetes documentation: ResourceQuota, immutable snapshot 30.05.2024 — Первичный неизменяемый снимок документации Kubernetes. Он подтверждает абсолютные quota для requests и limits; сумма quota может превышать ёмкость кластера, поэтому quota не является обещанием доступной capacity. Он не связывает quota с облачным invoice и не доказывает, что quota снижает расход.
- Kubernetes documentation: LimitRange, immutable snapshot 14.03.2024 — Первичный неизменяемый снимок документации Kubernetes. Он описывает default, minimum, maximum и ratio request/limit на admission stage. Он не является прогнозом saturation, цены или поведения уже запущенного workload.
- Google Cloud Billing Catalog v1 proto, immutable snapshot 18.06.2024 — Первичный неизменяемый API-contract. В нём определены SKU, pricing expression, usage unit, base unit и tiered rates. Он объясняет форму billing formula, но не сообщает тариф, валюту, скидку или счёт конкретного читателя.