{ "index": 116, "slug": "editorial-2024-10-mechanism-capacity-cost", "title": "Почему низкая загрузка не означает низкую стоимость", "excerpt": "Процент CPU показывает использование выбранного ресурса, но не цену. Разбираем fixed и variable части, quota и limit, saturation и способ проверить вывод до изменения capacity.", "contentHtml": "

График показывает CPU 31%. p95 равен 208 ms при SLO 300 ms. Команда делает вывод: инстанс слишком большой, его можно уменьшить. Через неделю тот же график используют как доказательство экономии. Но в расчёте нет базовой ставки, единицы тарификации, memory allocation и правила quota. Ошибка стоит дороже одного неверного числа: можно получить очередь, нарушить SLO или принять учебную арифметику за счёт провайдера.

\n

Тезис простой: utilisation, capacity и cost отвечают на разные вопросы. Низкий процент означает только, что наблюдаемая нагрузка мала относительно выбранной базы. Цена зависит от формулы, периода, fixed части и единицы расчёта. Quota ограничивает допустимый объём. Limit задаёт верхнюю границу ресурса. Saturation показывает, что система приближается к отказу. Эти величины связаны, но ни одна не заменяет другую.

\n

Начните с наблюдаемого симптома

\n

Сначала запишите факт без вывода. Например: «CPU 31%, p95 208 ms, model cost 17,76 u/24h». Это одна строка наблюдений, а не рекомендация. Затем разделите вопросы. Почему latency хорошая? Сколько ресурса зарезервировано? Какая часть формулы фиксирована? Какую единицу умножает тариф? Есть ли stop condition для следующего изменения?

\n

Такой порядок защищает от короткой, но неверной стрелки «низкая загрузка → уменьшить инстанс». Usage измеряют относительно allocation. Allocation может влиять на модель стоимости. Saturation зависит от нагрузки и запаса latency. Между наблюдением и действием лежат ещё resource policy, billing expression и граница риска.

\n

Механизм: пять разных объектов

\n

Observed utilisation — измерение использования относительно выбранного ресурса. CPU 31% не говорит, был ли выбранный request разумным и сколько стоит час. Fixed resource — постоянная часть учебной формулы, например base 0,36 u/h. Она остаётся в scope, пока существует сама модель.

\n

Variable resource — часть, которая меняется с allocation. В примере это CPU request и memory request, умноженные на учебные ставки за core-hour и GiB-hour. Billing unit — единица, к которой относится формула: час, GiB-hour или другая unit из конкретного контракта. Без неё разность двух чисел не имеет смысла.

\n

Quota и limit задают ограничения ресурса, а не цену. Quota может ограничивать суммарные requests и limits в namespace. Limit задаёт верхнюю границу для workload. Saturation — сигнал, что очередь, p95 или retry risk приближаются к принятой границе. Saturation может остановить выбор, но не пересчитывает billing formula.

\n
Причинная схема разделяет usage, allocation, quota и limit, saturation и модельную стоимость
Схема разделяет наблюдаемое использование, выделенный ресурс, ограничения, saturation и модельную стоимость. Она не показывает реальный счёт, кластер или реальную telemetry.
\n

Учебная формула и код

\n

Ниже — ограниченный учебный пример. Он не читает облачный счёт и не предлагает менять рабочую систему. Формула нужна, чтобы сделать промежуточные величины видимыми:

\n
const card = {\n  basePerHour: 0.36,\n  cpuRequest: 4,\n  memoryGiB: 6,\n  cpuUnitPerCoreHour: 0.08,\n  memoryUnitPerGiBHour: 0.01,\n  hours: 24,\n};\n\nconst hourly =\n  card.basePerHour +\n  card.cpuRequest * card.cpuUnitPerCoreHour +\n  card.memoryGiB * card.memoryUnitPerGiBHour;\n\nconst modelCost = hourly * card.hours;\n// 17.76 model units for this fixed educational card
\n

Значение 17,76 — результат именно этой формулы. Оно не является тарифом, счётом или прогнозом. Если поменять CPU с 4 до 1,6, нужно сравнить не только cost. Нужны одинаковые period и unit, а также p95, workload, memory, quota и operational risk. В соседней учебной карточке 1,6 CPU и 2,4 GiB дают 0,512 u/h. При p95 296 ms и сигнале queue-growth меньшая цена не доказывает безопасное уменьшение.

\n

Marginal cost — разность двух сопоставимых формул. Например, переход с 1,4 до 1,6 CPU при ставке 0,08 u/core-hour добавляет (1,6 - 1,4) × 0,08 × 24 = 0,384 u/24h. Это размер следующего вопроса, а не команда увеличить request. Если вместе с CPU меняются memory, region, commitment или shared base, одна разность уже не объясняет решение.

\n

Симптом → причина → проверка → действие

\n
Как разбирать вывод о capacity и стоимости
СимптомПричинаПроверкаДействие
CPU ниже 40%Usage сравнили с allocation и сразу назвали ресурс лишнимСверить request, workload, p95 и периодНе менять размер; сформулировать следующий контролируемый вопрос
Model cost вырослаИзменились fixed base, unit или allocationРазложить формулу по полям и одинаковому периодуПроверить billing contract; не называть число счётом
Quota ещё не исчерпанаQuota приняли за доступную безопасную capacityПроверить aggregate rule, admission и saturationОстановить изменение, если p95 или очередь уже у границы
Большой инстанс даёт лучший p95Одну метрику использовали вместо performance, cost и riskСравнить соседний limit и return boundaryСохранить stop и запросить отдельное решение владельца
Два расчёта не совпадаютСмешаны scope, unit или intervalСверить workload, resource, formula и часыНе сравнивать карточки до выравнивания входов
\n

Почему quota и saturation нельзя менять местами

\n

Quota отвечает на вопрос «какой aggregate объём policy допускает?». Она не отвечает на вопрос «выдержит ли сервис следующий request?». В Kubernetes quota может ограничивать суммарные requests и limits, но сама по себе не обещает свободную ёмкость кластера. LimitRange может задать default, minimum или maximum на admission. Это правила формы ресурса, а не доказательство latency.

\n

В учебной карточке quota равна 6 CPU, а request большого варианта равен 4 CPU. Из этого нельзя вывести, что сервис безопасно выдержит 4 CPU или что его следует уменьшить. Если p95 почти касается SLO и растёт очередь, saturation требует остановки даже при доступной quota. Если p95 стабилен, это всё равно не доказывает стоимость: нужна отдельная billing expression.

\n

Отрицательный путь

\n

Хорошая проверка должна уметь остановиться. Для large-instance-unjustified учебная ветка видит CPU 31%, p95 208 ms и большую reservation. Она возвращает stop-and-compare-smaller-limit. Ветка не уменьшает request и не создаёт rollback. Она запрещает два одинаково слабых вывода: «низкая загрузка означает лишний ресурс» и «лучший p95 оправдывает самый большой ресурс».

\n

Остановка нужна и при подмене входа. Если отчёт содержит неизвестное поле вроде invoice, другой scope, sparse array или изменённую model cost, его нельзя молча принять. Сначала нужно вернуть форму к согласованному контракту. Если unit или период не подтверждены, вычисление marginal cost прекращается. Отрицательный путь защищает границу примера, а не реальный API и не рабочую систему.

\n

Порядок действий

\n
  1. Зафиксируйте симптом. Запишите usage, p95, SLO, cost и signal без слов «дёшево», «дорого» или «лишний».
  2. Опишите scope. Назовите workload, resource, период, unit и границу сравнения. Разные интервалы нельзя сводить в одну карточку.
  3. Разложите формулу. Отделите fixed base, variable allocation и billing unit. Если unit неизвестна, остановите расчёт.
  4. Проверьте policy. Сверьте request, limit, quota и admission rules. Ответ «quota позволяет» не закрывает performance branch.
  5. Проверьте saturation. Сопоставьте queue, p95 headroom, retry risk и SLO. При stop condition не выбирайте следующий размер.
  6. Сравните соседний вариант. Меняйте один основной параметр, сохраняйте период и unit, считайте marginal difference только для сопоставимых карт.
  7. Назовите недостающий источник. Для цены это billing contract или export, для performance — telemetry и workload evidence, для policy — действующее правило admission.
  8. Зафиксируйте результат. Выберите compare, stop или human review. Ни один результат учебной модели не становится командой над кластером.
\n

Ограничения и критерий готовности

\n

Все числа в примере учебные. Модель не видит provider invoice, SKU, скидки, commitment, tax, network charge, storage, cluster state, runtime, trace или реальную нагрузку. Она не знает owner, полномочия, rollback policy и последствия изменения. Поэтому статья не обещает savings, не выбирает размер instance и не переносит unit из одного provider в другой.

\n

Проверка готова, если читатель может ответить на четыре вопроса: что наблюдалось; какая формула и unit применены; какое правило остановит изменение; какой реальный источник подтвердит следующий шаг. Дополнительно должны быть видны request, limit, quota, p95 и период. Если ответ держится только на CPU percent или на красивом model cost, вывод не готов к operational решению.

\n

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

" }