DarkRiDDeR10 мин

Ёмкость и стоимость: выбрать следующий предел, а не самый большой инстанс

ИнфраструктураНадёжность

В одном tabletop-разборе лежат три fixed synthetic cards. Первая держит 1,4 CPU, p95 248 ms и low operational risk. Вторая поднимается до 1,6 CPU, но p95 уже 296 ms и signal queue-growth. Третья сразу выбирает 4 CPU, получает p95 208 ms и observed CPU 31%. Симптом встречи знакомый: участники хотят перейти от второй карточки к третьей, потому что она «самая быстрая». Цена ошибки — пропустить stop condition, перепутать performance с безопасностью и назвать большой reservation единственным способом убрать тревогу.

Полевая задача уже: не подобрать instance, а выбрать следующий предел, который можно объяснить тремя осями — performance, model cost и operational risk. Причина выбора по максимальному размеру в том, что одна хорошая цифра p95 заслоняет resource contract и return boundary. Проверка должна сравнить cases по одинаковому scope и периоду, назвать stop condition до действия и отделить teaching return card от реального rollback. Действие будет либо compare-next-synthetic-limit, либо stop и human review; fixture не создаёт третьего пути.

Три сценария как материал полевого разбора

Первый сценарий — next limit. next-limit-balanced содержит 1,4 CPU request, 1,8 CPU limit, 2,2 GiB memory и p95 248 ms при SLO 300 ms. Его model cost за 24 часа равен 11,856 u. CPU observed 67%, saturation помечена как watch, а operational risk — low. Fixture позволяет сравнить следующий fixed limit 1,6 CPU и фиксирует delta 0,384 u/24h. Это не рекомендация увеличить request: это единственная карточка, где учебная логика ещё не встретила stop.

Второй сценарий — stop. saturation-stop использует 1,6 CPU request и 2 CPU limit, однако p95 оставляет лишь четыре миллисекунды headroom, а signal уже равен queue-growth. Cost равна 12,288 u/24h, но разница цены не главный факт. Модель останавливается по p95 boundary и просит отдельный review. Ошибка была бы в том, чтобы назвать quota 6 CPU запасом, который автоматически лечит очередь. Quota здесь только допустимая абсолютная граница, а не доказательство поведения следующего limit.

Третий сценарий — большой reservation. large-instance-unjustified задаёт 4 CPU request и 6 GiB memory, поэтому p95 падает до 208 ms. Но observed CPU 31%, model cost равна 17,76 u/24h, а operational risk остаётся medium: в модели не доказано, какой change был безопасен и как его вернуть. Fixture не говорит «уменьшить до 1,4». Она возвращает stop-and-compare-smaller-synthetic-limit. Достаточно этого отрицательного результата: большой instance не получает статус базовой стратегии только потому, что одна latency-точка лучше.

Кривая трёх fixed synthetic точек: current 1,2 CPU, next limit 1,4 CPU и large instance 4 CPU. Линии показывают раздельные performance, model cost и operational risk, а пунктир отмечает stop condition.
Диаграмма не прогнозирует production. Она заставляет спросить, на какой оси принято решение и где модель обязана остановить сравнение.

Матрица выбора: не один лучший показатель

Сравнение three fixed synthetic scenarios
ScenarioPerformanceModel cost / 24hOperational riskFixture outcome
next-limit-balancedp95 248 ms; headroom 52 ms11,856 u; next CPU delta 0,384 ulow; saturation watchcompare-next-synthetic-limit
saturation-stopp95 296 ms; headroom 4 ms; queue-growth12,288 umedium; performance boundary unclearstop-and-review-synthetic-limit
large-instance-unjustifiedp95 208 ms; observed CPU 31%17,76 umedium; return safety not provenstop-and-compare-smaller-synthetic-limit

Матрица не складывает три колонки в score. Это было бы ещё одной скрытой formula без owner-а. Её задача проще: не позволить одной колонке отменить другую. У третьего case лучше p95, но он не объясняет marginal effect между 1,6 и 4 CPU и не имеет доказанного operational path. У второго case нет самого большого cost, но его p95/risk condition уже достаточна для stop. У первого нет успеха; есть только ограниченное право продолжить учить модель на следующем fixed limit и перенести real decision владельцу.

Сначала stop condition, потом следующий предел

Stop condition должна быть записана до сравнения вариантов. В fixture есть три вида. p95-headroom-at-or-below-15ms останавливает scenario, где latency почти касается SLO. saturation-signal-present остановил бы case с очередью даже при другом запасе p95. large-reserve-with-low-observed-utilisation не говорит, что ресурс точно лишний; он запрещает использовать большой reservation как ответ без сравнения меньшего fixed step. Эти условия не равны production policy. Они намеренно узкие и понятные как unit-test reasoning.

Затем формулируется next limit. Он должен отличаться одним основным контролируемым полем и иметь return point. В next-limit-balanced следующая карта меняет CPU request с 1,4 до 1,6 и CPU limit с 1,8 до 2; return point хранит исходные 1,4 и 1,8 как teaching record. Memory, period и unit здесь фиксированы. Если в реальном обсуждении вместе с CPU меняют class instance, network, concurrency, storage и commitment, это уже не «следующий предел». Это несколько решений, для которых одной линейной карточки недостаточно.

Компактный прогон decision и return boundary

Пример ниже строит report, затем decision draft и return card для next-limit-balanced. Return card существует только после canonical report и только для branch без stop. Она содержит фразу does-not-change-a-workload-or-prove-a-real-rollback. Такая формулировка нужна, чтобы во время напряжённого разбора никто не перепутал зафиксированную точку возврата с командой к API. Файл не импортирует cloud client и не знает имя cluster, manifest, namespace, account или deployment.

import {
  createFixedSyntheticCapacityInput,
  inspectSyntheticCapacityCost,
  planSyntheticCapacityDecision,
  draftSyntheticCapacityRollback,
} from './upgrade-2024-10.mjs';

const report = inspectSyntheticCapacityCost(
  createFixedSyntheticCapacityInput('next-limit-balanced'),
);
const draft = planSyntheticCapacityDecision(report);
const returnCard = draftSyntheticCapacityRollback(draft);

console.log({ decision: draft.decision, returnCard: returnCard.reason });

Проверка fixture покрывает не только happy case. Если в report изменить cost, добавить billingExport, передать sparse nested array или получить cyclic object, plan отвергается. Если в draft изменить actions или добавить field, похожий на cloud command, return card тоже не создаётся. Эти assertions не делают rollback безопасным в production. Они доказывают лишь узкое правило: учебный record не должен принять мутировавший object как разрешение на действие. Для темы capacity это важнее красивого примера с одной зелёной веткой.

Роль quota и limit в полевом маршруте

Quota и limit находятся рядом с выбором, но не заменяют его. Kubernetes ResourceQuota позволяет ограничивать aggregate requests и limits в namespace, но не превращает разрешённый aggregate в доступную ёмкость кластера. LimitRange может применить default/min/max на admission и не пересобирает уже существующие workload. Поэтому field reviewer сначала спрашивает: пройдёт ли новая форма policy? Затем: какой resource она резервирует? Потом: какую billing formula мы вообще собираемся сопоставить? И только после этого — какое performance evidence допустимо сравнить. Перестановка вопроса «а quota позволяет?» на конец обсуждения создаёт лишний риск уже на стадии формы.

В synthetic cards quota 6 CPU одна и та же. Это сделано специально: она не становится объяснением разницы между 1,4 и 4 CPU. Если держать quota одинаковой, видно, что performance, cost и operational risk меняются не из-за самой quota. В реальном контуре quota может быть важной policy, но она не превращает capacity change в harmless change. У неё собственный owner, admission semantics и blast radius. Статья не выдаёт учебный literal за готовое правило namespace.

Маршрутизированный чек-лист полевого разбора

  1. Старт: зафиксируйте case. Выберите один workload, один период и одну formula. Если карточка смешивает разные units или scope, маршрут заканчивается до сравнения.
  2. Ветка performance. Запишите p95, SLO и headroom. При заданном stop condition не переходите к размеру instance; сформулируйте недостающий authorised measurement.
  3. Ветка resource policy. Проверьте request, limit, quota и admission constraint. Ответ «quota ещё есть» не закрывает performance или cost branch.
  4. Ветка cost model. Отделите fixed base, variable allocation, unit и period. Если unit не подтверждена источником, не вычисляйте marginal effect.
  5. Ветка operational risk. Назовите owner, допустимый scope и return boundary. Если return означает непонятное действие над workload, останьтесь в review.
  6. Финиш: выберите outcome. Только compare-next-synthetic-limit ведёт к новой учебной карточке. Любой stop ведёт к отдельному human question, а не к большому instance.

Ограничения и следующий проверяемый шаг

Ни один case не является нагрузочным тестом, cloud quote, счётом, telemetry snapshot, анализом scheduler, интервью или incident report. Все request, limit, quota, rates, request/s, p95, risk labels и return points — fixed versioned JS literals. Модель не знает, как реальный runtime использует CPU, как provider применяет commitment/tier/discount, какие dependencies находятся за latency и кто уполномочен менять capacity. Поэтому она не заявляет cost saving, не выбирает provider и не советует реальный rollback.

Следующий проверяемый шаг — пройти таблицу втроём на бумаге. Первый читает только performance column, второй — только cost model, третий — только resource/operational column. Каждый обязан назвать одну stop condition или недостающий источник. Затем группа выбирает один из трёх outcomes fixture и сверяет, нет ли решения, основанного на единственной колонке. Успех — короткая доказуемая причина остановки или следующего synthetic limit, а не согласие на самый большой instance.

Историческая граница октября 2024

Источники в конце закреплены датами марта–июня 2024 и существовали до конца октября 2024. Kubernetes docs поддерживают различение request, limit, quota и admission constraints. Google API proto поддерживает различение usage/base unit и tiered rate в billing expression. Три scenarios, cost values, stop conditions, decisions и return cards принадлежат только synthetic-capacity-cost-model/2024-10-v1. Они не описывают неизвестный production и не исполняют никаких действий.

Проверяемые источники

  • 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, но не сообщает тариф, валюту, скидку или счёт конкретного читателя.