DarkRiDDeR10 мин

Ёмкость и стоимость: почему процент загрузки не равен цене

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

В synthetic case large-instance-unjustified observed CPU равен 31%, p95 равен 208 ms при SLO 300 ms, а модельная стоимость выше, чем у соседнего case с меньшим reservation. Симптом легко описать неверно: «низкая загрузка — значит инстанс лишний». Цена этой ошибки — другой неверный вывод в следующей встрече: процент загрузки начинают использовать как цену, а фактор fixed base, billing unit или quota исчезает из обсуждения. В этой статье нет настоящего bill и нет рекомендации уменьшать instance. Есть замкнутая модель, которая показывает, каких промежуточных величин не хватает до честного вопроса.

Причина проста, но её часто прячут в один dashboard. Utilisation — наблюдение относительно выбранной базы. Cost — результат formula для allocation и единиц. Quota и limit — разные ограничения ресурса. Saturation — сигнал, что часть системы перестаёт обслуживать поток с нужным запасом. Проверка обязана вынести эти слова в отдельные поля. Действие — сравнить их причинно: usage может подсказать проверку allocation, но нельзя провести стрелку от 31% прямо к «дёшево» или «дорого».

Пять объектов, которые нельзя склеивать

Fixed resource в модели — база 0,36 u/h: она остаётся, пока существует выбранный synthetic scope. Variable resource — CPU request и memory request, умноженные на fixed literals за core-hour и GiB-hour. Billing unit — единица, которой принадлежит formula; у внешнего API она может иметь usage unit, base unit и tiered rates. Quota/limit — ограничения допустимой и исполняемой формы ресурса. Saturation — наблюдаемое условие очереди, p95 или retry risk. Эти пять объектов могут быть связаны, но одинаковыми они не становятся.

Полезно отдельно сказать, чего не делает каждый объект. Fixed base не объясняет latency. CPU percent не сообщает, какова base unit. Quota не доказывает оплату ресурса. Limit не является forecast. Saturation не превращает уравнение в price list. Именно здесь поздний автор 2024 года должен удержать цену решения: чем меньше терминов он смешал, тем меньше будет необратимых действий в ответ на один график. Нужна не большая теория, а контроль над направлением стрелок.

Причинная схема разделяет observed CPU/memory usage, allocated resource request/limit/quota, saturation, billing expression и итоговую synthetic model cost. Стрелки показывают, что utilisation не ведёт прямо к цене.
Схема — причинная дисциплина для модели. Она не отображает provider invoice, реальные SKU, telemetry или действие над cluster.

Причинная диаграмма: что влияет на что

В верхней части схемы observed usage связан с allocated resource: процент появляется только относительно allocation. Но эта стрелка не означает, что usage выставляет цену. Allocation попадает в billing expression вместе с fixed base, unit и возможным tier. Только после этого fixture строит model cost. В нижней части saturation связан с workload и allocation: очередь или малая p95 headroom требуют остановки и отдельной проверки. Они не пересчитывают billing formula. Такая диаграмма полезна, когда один график CPU пытаются использовать и как тест производительности, и как аргумент о бюджете.

Kubernetes snapshot о request и limits создаёт техническую опору для половины схемы. Request участвует в scheduling, limit имеет свою enforcement semantics. ResourceQuota фиксирует aggregate границы, но сумма quota может превышать total capacity cluster; LimitRange может подставить default на admission. Это делает сравнительную карточку точнее: к resource принадлежат как указанные в spec значения, так и правила, от которых они зависят. Но ни один из этих документов не утверждает, какая строка будет в счёте. Внешняя billing formula — отдельный contract, и именно поэтому она стоит в другой колонке.

Таблица различий: вопрос, поле, неправильный вывод

Разделение терминов в capacity/cost review
ОбъектВопрос, на который он отвечаетПример fixed literalЧто он не доказывает
Observed utilisationКаков usage относительно выбранной базы?CPU 31%Не задаёт invoice и не доказывает, что allocation лишний.
Fixed resourceКакая часть model cost не зависит от CPU percent?base 0,36 u/hНе объясняет p95 и не является тарифом.
Variable resourceЧто formula умножает на unit за период?4 CPU × 0,08 u/core-hНе равен фактическому consumption в любой платформе.
Quota / limitКакие значения разрешены или исполняются?quota 6 CPU; limit 4,5 CPUНе являются ценовой шкалой и не меняют прошлый расход.
SaturationНужна ли stop condition до следующего шага?queue-growthНе говорит, какой instance покупать или возвращать.
Marginal costКак меняется formula между двумя сравнимыми cards?+0,384 u/24hНе является savings claim или approval.

В таблице намеренно нет строки «лучший размер». Размер становится результатом другого, authorised процесса, когда известны рабочая нагрузка, реальные правила provider, availability, ownership, rollback policy и последствия ошибки. В fixture есть только smaller question: что изменится между текущим и следующим фиксированным пределом? Если для него нет единицы, периода и return boundary, модель предпочитает stop. Это честнее, чем выводить перспективу на production из процента utilisation.

Fixed и variable части в одной формуле

В large-instance-unjustified formula берёт base 0,36 u/h, CPU request 4 × 0,08 u/core-hour и memory request 6 × 0,01 u/GiB-hour. Получается 0,74 u/h. Для saturation-stop формула с 1,6 CPU и 2,4 GiB даёт 0,512 u/h. Разница не доказывает, что второй case выгоднее: у него p95 296 ms и signal queue-growth. Зато пример не позволяет спрятать цену большого reservation за 31% utilisation. Fixed/variable decomposition делает visible то, что нужен отдельный reason для каждого шага.

Marginal cost в этой модели — разность formula между соседними cards при одинаковой единице и периоде. Например, переход с request 1,4 CPU на 1,6 CPU добавляет 0,384 u за 24 часа: (1,6 − 1,4) × 0,08 × 24. Это не ответ «надо увеличить». Это размер вопроса, который должен лежать рядом с ожидаемым performance effect и operational risk. Если новая card меняет ещё memory, region, commitment или shared base, разность перестаёт быть одной переменной. Тогда нельзя обозначать её как marginal cost одного limit.

Исполнимая negative check вместо имитации billing

Ниже запускается case, где p95 лучше SLO, но reservation намеренно большой. Fixture возвращает stop-and-compare-smaller-synthetic-limit. Она не меняет параметр и не может открыть счёт, чтобы проверить, прав ли автор. В этом и смысл negative branch: она проверяет, что classroom logic не превращает хороший latency в разрешение тратить или плохой percent utilisation — в разрешение сокращать. Вход закрыт: другой case id, extra field похожий на bill, file path или network mode отвергается до расчёта.

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

const report = inspectSyntheticCapacityCost(
  createFixedSyntheticCapacityInput('large-instance-unjustified'),
);

console.log({
  utilisation: report.signals.observedUtilisationPercent,
  modelCostPerHour: report.signals.modelCostPerHour,
  decision: report.decision,
  stopCondition: report.stopCondition,
});

В fixture проверяется и более неприятный случай: report с подменённой model cost, sparse array внутри nested signal, cyclic object и draft с неизвестным field не проходят canonical compare. Отрицательная проверка не защищает реальный API. Она защищает границу примера: если вход нельзя сверить с fixed literal, пример должен остановиться, а не использовать красивые числа caller-а. Это особенно важно для темы стоимости, где очень легко перепутать учебную арифметику с данными access-controlled billing system.

Saturation и quota говорят о разном

Saturation — не тот же limit. В case saturation-stop CPU observed 79%, memory 74%, p95 296 ms, а queue-growth уже задан как signal. Fixture выбирает stop по p95 headroom четыре миллисекунды, до попытки выдать следующий limit. Quota 6 CPU остаётся доступной математической границей, но она не отвечает, будет ли следующий allocation безопасным. Это полезный контрпример для обсуждения: «ещё есть quota» не является причиной увеличивать request, если performance boundary уже требует другого измерения.

Limit тоже не решает вопрос сам по себе. Kubernetes documentation описывает CPU и memory limits как разные механизмы; LimitRange накладывает условия на admission и не меняет существующие Pod. Поэтому строка limit: 4,5 CPU в fixed card не должна читаться как «теперь сервис выдержит 4,5». Она только называет верхнюю конструкцию данного synthetic scenario. При реальном решении понадобятся версия платформы, runtime, request shape, scheduling, data path и способ наблюдать degradation. В статье намеренно нет этого claim, потому что fixture ничего из перечисленного не видит.

Маршрут: симптом → причина → проверка → действие

  1. Симптом. Назовите один наблюдаемый факт: SLO выполняется, model cost растёт, utilisation низкий или p95 близок к границе. Не объединяйте их в один вывод.
  2. Причина-кандидат. Отметьте fixed base, allocation, billing unit, quota/limit или saturation. У каждого кандидата должна быть собственная колонка.
  3. Проверка формы. Сверьте scope, период, unit, request, limit и quota. Если values принадлежат разным interval, stop раньше вычисления.
  4. Проверка причинной стрелки. Для «дорого» потребуйте billing expression; для «медленно» — workload и saturation evidence; для «не пройдёт» — admission/quota rule.
  5. Действие. Сравните только next synthetic limit либо остановите выбор. Сохраните human question и return boundary, не executor command.
  6. Ограничение. Назовите, какие реальные источники отсутствуют. Отсутствие настоящего bill — повод не делать production conclusion, а не повод подставить synthetic unit вместо него.

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

Модель не читает usage export, invoice, SKU catalog, commitment, tax, network charge, storage, trace, metrics, cluster, policy, manifest или CI. Она не содержит реальных ставок, имён продуктов, клиентских объёмов, интервью или actual savings. Source из Google API фиксирует, что pricing expression может содержать usage/base unit и tiered rates; он не даёт rights, чтобы запросить чужой catalog. Kubernetes sources фиксируют ресурсные термины, не оплачиваемый usage. Любое перенесение числа за пределы fixture нарушит её модельный предел.

Следующий проверяемый шаг — нарисовать ту же причинную схему для вымышленной card без чисел: один человек ставит стрелки между workload, allocation, billing unit и saturation, второй убирает хотя бы одну недопустимую стрелку, например utilisation → price. Затем оба добавляют один stop condition и один вопрос владельцу. Если схема остаётся понятной без слова «оптимизация», она готова к сопоставлению с разрешённым процессом. Если нет, сначала уточняют границы, а не выбирают instance.

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

Снимки Kubernetes марта–мая 2024 и Google Cloud Billing Catalog proto июня 2024 существуют до конца октября 2024. Они поддерживают узкий словарь этой статьи: request/limit/quota, admission defaults, usage unit, base unit и tiered rate. Fixed/variable split, cases, percentages, p95, model cost и decisions — авторские versioned synthetic literals. Они не доказывают свойства облака, приложения или команды и не разрешают изменение инфраструктуры.

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

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