{ "index": 115, "slug": "editorial-2024-10-field-capacity-cost", "title": "Ёмкость и стоимость: как выбрать следующий предел, а не самый большой инстанс", "excerpt": "Большой инстанс может улучшить одну latency-метрику и одновременно увеличить расход и операционный риск. Разбираем причинную цепочку, проверку и безопасный критерий следующего шага.", "contentHtml": "
На разборе ёмкости команда видит знакомую картину: после перехода на 4 CPU p95 снизился, а резерв и счёт выросли. Карточка с 1,6 CPU уже показывала очередь и почти касалась SLO, поэтому большой инстанс выглядит очевидным ответом. Цена ошибки — закрепить дорогую конфигурацию без доказанного эффекта, принять одну удачную latency-точку за решение и потерять понятную границу возврата.
\nТезис: следующий размер выбирают не по лучшему p95, а по следующему проверяемому пределу. В одной карточке нужно связать workload, выделенный ресурс, сигнал производительности, единицу тарификации и условие остановки. Если хотя бы одно звено неизвестно, результатом должна быть остановка или отдельный разбор, а не заказ самого большого инстанса.
\nВопрос «сколько CPU нам нужно?» слишком короткий. Для проверки запишите: какой workload измеряем, в каком окружении, за какой период, какой SLO защищаем и что именно меняем. Для сервиса это может быть поток HTTP-запросов, число реплик, requests и limits контейнера, p95 задержки, ошибки и сигнал очереди. Для финансовой части добавьте billing unit: час виртуальной машины, CPU-hour, GiB-hour, запрос или трафик.
Так появляются разные, но связанные факты. Workload описывает работу. Allocation описывает обещанный системе резерв. Usage показывает фактическое потребление. Performance показывает результат для пользователя. Billing expression объясняет, как провайдер переводит использование или резерв в деньги. Низкий usage не заменяет allocation и не доказывает экономию. Зелёный SLO не объясняет тариф.
\nСитуация считается описанной только после фиксации scope. Запись «p95 стал лучше» не воспроизводится без маршрута, нагрузки, числа реплик, периода, версии и способа измерения. Запись «стоимость выросла на 20%» не воспроизводится без счёта, валюты, региона, скидки и правила агрегации.
\nВ Kubernetes эти слова отвечают на разные вопросы. Scheduler использует resource request при выборе узла. Limit задаёт ограничение выполнения контейнера: CPU может быть ограничен throttling, а превышение memory limit может привести к вмешательству OOM-механизма. ResourceQuota ограничивает суммарные ресурсы или объекты в namespace. LimitRange задаёт допустимые minimum, maximum и default для объектов на этапе admission.
\nНи один из этих механизмов не является тарифом. Если в namespace осталось 6 CPU quota, это говорит о политике допуска, но не о свободной ёмкости кластера, состоянии соседних workloads, задержке сервиса или цене часа. Если контейнер использует 31% CPU, это говорит об usage относительно выбранной базы, но не о том, сколько провайдер выставит в счёте. Сначала подтверждают policy и runtime, затем отдельно сверяют billing.
\n| Сигнал | Что он подтверждает | Чего он не подтверждает | Следующая проверка |
|---|---|---|---|
| CPU usage 31% | Наблюдаемое потребление в выбранном окне | Безопасный request и цену | Peak, throttling, p95 и billing unit |
| Quota ещё не исчерпана | Ограничение namespace не достигнуто | Доступный узел и запас по SLO | Admission, cluster capacity и saturation |
| p95 ниже SLO | Метрика укладывается в заданное окно | Экономичность и причинность улучшения | Одинаковый workload, период и allocation |
| Счёт вырос | Изменился итог billing-периода | Причину в одном ресурсе | SKU, rate, replicas, storage и traffic |
Практический вывод отрицательный: нельзя уменьшать request только потому, что средний CPU низкий, и нельзя увеличивать limit только потому, что quota это допускает. Для памяти редкий пик важнее среднего значения. Для CPU полезно посмотреть throttling и очередь. Для latency — выяснить, не сидит ли время в сети, блокировке или внешнем сервисе.
\nФормула нужна не для имитации счёта, а для проверки причинной цепочки. В учебной модели отдельно обозначим фиксированную часть, резерв CPU, резерв памяти и ставки. Если провайдер считает инстанс целиком, а не request контейнера, формула должна отражать инстанс. Если billing unit неизвестна, арифметику нужно остановить, а не заменить процентом utilization.
\nnode - <<'NODE'\nconst model = {\n fixedPerHour: 0.36,\n cpuHour: 0.08,\n memoryGiBHour: 0.01,\n hours: 24,\n replicas: 1\n};\n\nconst variants = [\n { name: 'current', cpu: 1.2, memoryGiB: 2, p95Ms: 248, queueGrowth: false },\n { name: 'next-limit', cpu: 1.4, memoryGiB: 2, p95Ms: 248, queueGrowth: false },\n { name: 'saturation-stop', cpu: 1.6, memoryGiB: 2, p95Ms: 296, queueGrowth: true },\n { name: 'large-instance', cpu: 4, memoryGiB: 6, p95Ms: 208, queueGrowth: false }\n];\n\nfor (const variant of variants) {\n const hourly = model.fixedPerHour\n + variant.cpu * model.cpuHour\n + variant.memoryGiB * model.memoryGiBHour;\n const daily = hourly * model.hours * model.replicas;\n const headroomMs = 300 - variant.p95Ms;\n console.log({ ...variant, hourly, daily, headroomMs });\n}\nNODE\nКоманда не обращается к кластеру, облаку или telemetry. Она воспроизводит только учебную арифметику на Node.js и печатает одинаковые результаты на машине с установленным Node. Для current получится 0,476 условной единицы в час и 11,424 за сутки; для next-limit — 0,492 в час и 11,808 за сутки. Это не валюта и не реальный invoice.
В рабочей карточке рядом с формулой должны лежать источник значения, единица и период. Ставка может быть tiered, а расход — агрегироваться по проекту или аккаунту. Поэтому сравнение двух конфигураций по одной строке «CPU × ставка» безопасно только как черновая модель, пока billing export или rate card не подтверждают остальные компоненты.
\nРассмотрим фиксированный workload за 24 часа, одну реплику и SLO p95 не выше 300 мс. Текущая точка использует 1,2 CPU и 2 GiB памяти. Следующий малый предел меняет CPU до 1,4, сохраняя память и окно. В обоих учебных случаях p95 равен 248 мс, поэтому улучшение от изменения не доказано: новая точка всего лишь остаётся сравнимой.
\nВариант saturation-stop с 1,6 CPU показывает p95 296 мс и рост очереди. Headroom равен 4 мс. Даже если quota позволяет такой request, сравнение нужно остановить. Условная стоимость по скрипту равна 12,192 за сутки. Следующий рост ресурса не объясняет, почему растёт очередь.
Большой вариант резервирует 4 CPU и 6 GiB. p95 равен 208 мс, CPU usage в карточке — 31%, а модельная стоимость — 17,76 за сутки. Это показывает, что latency можно получить за счёт большого резерва. Это не доказывает, что резерв нужен, что применена правильная ставка или что его можно без риска вернуть назад.
\n| Вариант | Ресурс | Performance | Стоимость за 24 ч | Решение |
|---|---|---|---|---|
current | 1,2 CPU; 2 GiB | p95 248 мс; запас 52 мс | 11,424 u | База сравнения |
next-limit | 1,4 CPU; 2 GiB | p95 248 мс; запас 52 мс | 11,808 u | Проверить соседний шаг |
saturation-stop | 1,6 CPU; 2 GiB | p95 296 мс; запас 4 мс; растёт очередь | 12,192 u | Остановить сравнение |
large-instance | 4 CPU; 6 GiB | p95 208 мс; CPU 31% | 17,76 u | Не выбирать по одному p95 |
Таблица не превращает performance, cost и risk в общий score. Для такого score пришлось бы заранее определить веса, владельца и допустимые trade-off. Здесь важна сохранённая отрицательная ветка: хороший p95 при неизвестной цене не даёт согласия, а низкая цена при headroom 4 мс не даёт разрешения продолжать.
\nRequest влияет на планирование, но не обещает, что приложение всегда получит ровно это количество CPU. Limit задаёт верхнюю границу исполнения и имеет собственные последствия. Quota и LimitRange могут не пропустить объект или назначить ему default, но не проверяют пользовательский SLO и не рассчитывают итоговый счёт.
\nИз этого следует последовательность владельцев. Платформенная policy подтверждает, что объект допустим. Scheduler и runtime показывают, что workload размещён и исполняется. Сервисные метрики показывают latency, ошибки и saturation. Финансовый источник показывает SKU, usage unit, base unit, tier и период. Один владелец или один dashboard не заменяют четыре вида evidence.
\nОсобенно опасна фраза «в quota есть запас, значит можно уменьшить инстанс». Quota — это потолок политики, а не рекомендация по размеру и не прогноз поведения. И обратная фраза тоже неверна: превышение request не означает автоматически, что надо покупать большой инстанс. Сначала нужно установить bottleneck и проверить малый соседний шаг.
\nStop condition защищает решение от давления одного удачного графика. Для учебного примера зададим остановку при headroom меньше 15 мс, при росте очереди, при неизвестной billing unit или при одновременной смене CPU, памяти, сети и класса машины. Эти условия не выбирают победителя. Они говорят, когда текущая гипотеза больше не проверяется тем же экспериментом.
\nВозврат тоже имеет границу. Запись «при ухудшении откатить» неполна, пока не названы владелец, изменение, разрешённый способ возврата и сигнал успешного восстановления. Эта статья не даёт команды rollback для чужого кластера: команда зависит от вашего deployment-процесса, прав и политики изменений. Если такой путь не описан, это причина остановить change, а не повод скрыть риск.
\nВсе значения в примере — учебные: 0,36, 0,08, 0,01, p95 248/296/208 мс, 31%, 1,2/1,4/1,6/4 CPU и 2/6 GiB. Они не описывают конкретный provider, cluster, invoice, telemetry или production workload. Их можно использовать, чтобы проверить форму расчёта и смысл stop condition, но нельзя переносить их в бюджет, SLO или capacity plan.
\nМодель не включает автоматически autoscaling, burst, cold start, noisy neighbor, storage, network egress, commitments, скидки, налоги, стоимость лицензий и сопровождения. Они могут изменить решение. Если хотя бы один фактор существенен, добавьте его в ту же billing-модель или остановите сравнение до получения данных.
\nИсточники Kubernetes подтверждают семантику ресурсов и policy, а источник Cloud Billing — форму описания SKU и тарифных ступеней. Ни один источник не знает вашу нагрузку, не обещает нужный p95 и не доказывает экономию. Доказательство для своего решения появляется только после выравнивания scope, измерения и финансового источника.
\nРешение о следующем пределе готово, когда одна карточка связывает workload, allocation, performance signal, billing unit, stop condition, owner и return boundary. В ней видно, какой параметр изменился, за какой период проведено сравнение и какой сигнал остановит эксперимент. Policy не выдана за runtime, usage не выдан за цену, а лучший p95 не выдан за универсальное решение.
\nЕсли хотя бы одного поля нет, полезный результат — не самый большой инстанс, а список недостающих доказательств. Такой отказ от поспешного изменения сохраняет возможность сравнить соседний шаг и делает следующую итерацию проверяемой.
\n