Платформенная инженерия Стоимость: учёт расходов, лимиты и разговор с командами о деньгах
0%

Стоимость: учёт расходов, лимиты и разговор с командами о деньгах

Стоимость: учёт расходов, лимиты и разговор с командами о деньгах

В марте счёт за облако был 186 000 USD. В октябре — 264 000 USD. Выручка за это время выросла на 9 %. Финансовый директор приходит к директору по инженерии, тот — к платформенной команде: «разберитесь».

Платформенная команда делает ровно то, что делает большинство: открывает консоль биллинга, час смотрит на график, находит, что «выросло всё», и рассылает в общий инженерный чат письмо: «Коллеги, счёт за облако вырос на 42 %. Просьба всем командам проверить свои ресурсы и удалить лишнее». В ответ приходят четыре сообщения: два «а как посмотреть свои ресурсы?», одно «у нас всё нужное» и один эмодзи.

Через месяц счёт — 271 000 USD.

Это не история про недостаток дисциплины. Это история про разорванный контур обратной связи. Инженер, который в четверг поставил replicas: 12 вместо трёх и memory: 8Gi вместо гигабайта, никогда не увидит последствий своего решения: счёт придёт через сорок дней, будет анонимным, попадёт другому человеку и будет обсуждаться на встрече, куда этого инженера не позовут. Ни одного из четырёх условий работающей обратной связи — адресность, своевременность, соразмерность, возможность действовать — не выполнено (обратные связи, задержки).

Работа платформы со стоимостью — это не «сэкономить деньги компании». Это починить контур: сделать расход адресным, показать его тому, кто принимает решение, в момент решения, и дать возможность решить иначе. Экономия — следствие. Если начинать с экономии, получается кампания по борьбе за экономию: шумная, краткосрочная и заканчивающаяся ровно тогда, когда заканчивается внимание руководства.

Эта глава — про механику: как из счёта провайдера сделать адресный счёт, что делать с общими ресурсами, которые невозможно разметить, как ставить лимиты, не ломая работу и инциденты, как разговаривать с командами так, чтобы разговор заканчивался изменением, и как считать стоимость самой платформенной команды, чтобы она не превратилась в налог. Про цену облачных сервисов как таковых — отдельная глава в треке DevOps; здесь мы не пересказываем прайс-листы, а строим то, что делает платформа: учёт, ограничения и разговор.

Три счёта, которые обычно путают

Первая ошибка почти любого обсуждения — считать, что «стоимость платформы» это одно число. Их три, и живут они в разных местах.

Счёт за инфраструктуру Счёт за платформенную команду Счёт за время пользователей
Что это облако, лицензии, SaaS-инструменты зарплаты, найм, обучение обходы, ожидания, миграции, обучение вашему контракту
Кто видит финансовый отдел, ежемесячно бюджет штата, раз в год никто
Как растёт ступеньками и незаметно ступеньками при найме линейно с числом команд и обязательных шагов
Как сокращается техническими решениями сокращением набора обязательств улучшением опыта, а не документацией
Типичный порядок 60–75 % общей суммы 15–25 % 10–30 %, но не измеряется

Три счёта одной инженерной организации в одном масштабе

Числа на картинке — из сквозного примера этой главы: 18 продуктовых команд, 110 инженеров, платформа из пяти человек, полная стоимость часа инженера 78 USD (зарплата, налоги, оборудование, доля офиса и менеджмента — берите свою цифру у финансистов, а не выдумывайте). Третий столбец получается из простого замера: 2,5 часа в неделю на инженера уходит на то, чего бы не было при идеальной платформе. Это скромная оценка: в реальных замерах опыта разработчика цифра обычно выше (как её мерить).

Смысл картинки не в том, что «время дороже железа». Смысл в асимметрии внимания: первый счёт обсуждают ежемесячно, третий не обсуждают никогда, хотя он больше всего бюджета платформенной команды. Отсюда типовой перекос: платформа месяцами оптимизирует облако на 4 % и одновременно добавляет обязательный шаг, который стоит организации больше, чем вся сэкономленная сумма.

Дальше в главе первые два счёта разбираются подробно, третий возвращается в разделе про налог. Держите в голове все три: любое предложение «давайте запретим командам делать X, чтобы сэкономить» — это перенос денег из первого счёта в третий, и почти всегда с потерями.

Почему анонимный расход всегда растёт

Общий ресурс без адресного счёта деградирует предсказуемо. Каждый участник получает всю выгоду от своего потребления и несёт лишь малую долю затрат — классическая структура, описанная Гарретом Хардином (The Tragedy of the Commons, Science, 1968) и подробно разобранная Элинор Остром, которая показала главное: общий ресурс не обязан деградировать, если у сообщества есть границы, наблюдаемость потребления и понятные правила («Governing the Commons», 1990). Это буквально техническое задание для платформенной команды.

В инженерной практике механизм выглядит так:

Обратите внимание на петлю D → E → F → G → H → I → D: кампании по экономии не меняют структуру, поэтому система возвращается в исходное состояние. Это архетип «подмена проблемы симптоматическим решением» (архетипы). Точка воздействия находится не в кампании, а в правилах и дефолтах (точки воздействия).

Из этого следуют три рабочих принципа, на которых держится вся глава.

  1. Расход должен быть адресным. У каждой строки счёта есть владеющая команда, иначе разговаривать не с кем. Доля неразнесённого расхода — метрика качества вашей платформы, а не бухгалтерии.
  2. Цена должна приходить в момент решения. Не в квартальный отчёт, а в план изменений в пул-реквесте, в шаблон сервиса, в ответ CLI. Отсрочка в 40 дней превращает данные в исторический факт.
  3. У получателя должна быть возможность действовать. Сообщение «ваш сервис стоит 12 000 USD» без ответа на вопрос «и что мне с этим сделать» — просто источник тревоги.

Учёт: как из счёта провайдера сделать адресный счёт

Разметка — контракт платформы, а не просьба к людям

Все системы учёта расходов в облаке в конечном счёте опираются на метки ресурсов: теги в AWS (cost allocation tags), labels в GCP, tags в Azure, labels и namespace в Kubernetes. Отсюда правило, которое надо принять до начала работы: разметка, которую проставляют люди руками, всегда неполна. Не потому, что люди плохие, — потому что это работа без немедленной пользы для того, кто её делает.

Работающая схема ровно одна: метки проставляет платформа в момент создания ресурса, а человек их не видит. Шаблон сервиса, модуль IaC, контроллер — любой из механизмов инфраструктуры как API знает, кто владелец, потому что владелец указан в описании сервиса.

Минимальный набор ключей, который окупается:

# Обязательные метки, проставляются шаблоном и модулями IaC, не человеком.
# Ключи стабильны навсегда: переименование ключа обнуляет всю историю расходов.
labels:
  owner-team:   payments          # команда-владелец из каталога сервисов
  service:      payments-api      # логический сервис, а не имя ресурса
  environment:  prod              # prod | staging | preview | dev
  cost-center:  cc-4102           # то, что понимает финансовый отдел
  criticality:  tier-1            # влияет на допустимый запас и класс хранилища
  managed-by:   platform          # платформенный ресурс или созданный командой сам

Шесть ключей, не двадцать. Каждый лишний обязательный ключ — это ещё одна причина, по которой ресурс не будет создан, и ещё один повод для команды обойти платформу. criticality в этом списке стоит не ради красоты: он связывает деньги с надёжностью — сервису класса tier-3 не нужен горячий резерв в трёх зонах, и это решение принимается один раз в описании, а не каждым инженером заново (SLO и классы обслуживания).

Три механизма, которые удерживают разметку в живом состоянии:

  • Запрет на создание без меток. Политика в admission-контроллере или в проверке плана IaC: ресурс без owner-team не создаётся. Важно, чтобы отказ был человеческим — с примером правильного описания и ссылкой, а не admission webhook denied the request.
  • Ежедневный поиск сирот. Отчёт «ресурсы без владельца, старше 7 дней» с автоматическим тикетом на команду, чья учётная запись их создала (это видно в журнале аудита облака, даже когда меток нет).
  • Метка неразнесённого как метрика. Долю расхода без владельца показывают на том же дашборде, где стоимость. Разумная цель — ниже 5 % и снижающаяся. Пока эта доля 40 %, любые разговоры о деньгах с командами будут упираться в «а это точно наше?».

Общие ресурсы: то, что разметить невозможно

Дальше начинается трудная часть. Значительная доля счёта не принадлежит никому конкретно: control plane кластера, NAT-шлюз и egress, балансировщики, реестр образов, стек наблюдаемости, CI-раннеры, резервные мощности, сама инфраструктура платформы. В типичном кластере картина выглядит так:

Разбор счёта за узлы кластера: полезное потребление, зарезервированное впустую, накладные расходы

Здесь два вывода, которые стоит проговорить.

Первый: разговор о requests касается 41 % счёта, а разговор о «выключите лишнее» — почти ничего. Между «сколько запрошено» и «сколько используется» лежит основная сумма, и она полностью в руках продуктовых команд — но только если они видят обе цифры рядом. Именно это платформа обязана показывать по умолчанию: не «ваш сервис стоит X», а «вы платите за 8 ГБ, используете 1,4 ГБ на 95-м процентиле».

Второй: 21 % — это запас под пики и обновления, и он не ошибка. Часть неиспользуемой ёмкости — плата за надёжность и за возможность выкатываться без простоя (ёмкость и запас). Платформа обязана назвать эту сумму вслух и защищать её, иначе первая же кампания по экономии срежет именно её — как самую беззащитную статью.

Общий расход надо разнести. Способов три, и выбор между ними — политический, а не технический.

Способ Как считается Что поощряет Где ломается
По потреблению доля от фактического использования (CPU-секунды, ГБ, запросы) точность, снижение реального потребления требует данных потребления; малые команды платят мало и не замечают
По запросу (requests) доля от зарезервированного, а не использованного реалистичные requests, снятие запаса команда, честно поставившая запас под пик, платит больше «оптимистов»
Поровну между командами общая сумма делится на число команд простота, никто не спорит с формулой крупный потребитель субсидируется мелкими; сигнал бесполезен

Практика, которая работает лучше всего: разносить по max(потребление, requests) — это одновременно честно (вы платите за то, что зарезервировали) и полезно (мотивирует приводить requests в соответствие с реальностью). Накладные расходы, которые не привязать вообще (control plane, платформенная команда), либо оставляют на общем центре затрат, либо разносят пропорционально уже разнесённой части.

Три правила, которые экономят месяцы споров:

  1. Метод разнесения — публичный документ на одну страницу, а не логика внутри скрипта. Команда должна уметь воспроизвести свой счёт.
  2. Метод не меняют чаще раза в год. Каждое изменение обнуляет сравнимость с историей и запускает новый круг споров.
  3. Спор о методе почти всегда дороже спорной суммы. Если разногласие касается 2 % счёта, зафиксируйте любой вариант и вернитесь к работе.

Конвейер учёта

Технически всё это — конвейер данных, у которого есть свои отказы, своя стоимость и свой владелец.

Полезные опоры, которые не надо изобретать:

  • FOCUS — открытая спецификация формата данных о расходах, снимающая различия между провайдерами (focus.finops.org). Если строите витрину — стройте сразу в её терминах: иначе первый же второй провайдер потребует переписать модель.
  • FinOps Framework — словарь и набор практик, полезный в первую очередь тем, что даёт общий язык с финансистами (finops.org/framework).
  • OpenCost — открытая спецификация и реализация расчёта стоимости нагрузки в Kubernetes (opencost.io); коммерческая надстройка — Kubecost. Полезно, но помните: это ещё один компонент в проде со своими метриками, обновлениями и потреблением.
  • Экспорты биллинга: AWS Cost and Usage Report, GCP billing export в BigQuery, Azure Cost Management exports.
  • Книга «Cloud FinOps» (J. R. Storment, M. Fuller, O’Reilly) — единственная известная мне книга, где подробно разобран именно организационный слой: кто с кем разговаривает и о чём.

Цена самого учёта. Экспорт CUR за год для организации среднего размера — десятки-сотни гигабайт; запросы к нему в BigQuery или Athena стоят денег; витрина требует ежедневного обновления и присмотра; модель разнесения ломается при каждом изменении инфраструктуры. Реалистичная оценка — 0,2–0,5 человека постоянно плюс несколько сотен долларов в месяц. Это нормально, если счёт измеряется сотнями тысяч, и явно избыточно, если счёт 6 000 USD в месяц: там достаточно тегов и встроенного отчёта провайдера.

Разнесение в коде

Модель разнесения полезно держать в виде читаемого кода, а не в кликах по интерфейсу: её ревьюят, воспроизводят и покрывают тестами.

"""Разнесение месячного счёта по командам: прямые ресурсы плюс доли в общих пулах."""
from collections import defaultdict
from dataclasses import dataclass

@dataclass(frozen=True)
class Line:
    """Строка счёта после нормализации. team=None — ресурс без владельца."""
    resource: str
    team: str | None
    amount: float                     # USD за период
    shared_pool: str | None = None    # имя пула, если ресурс общий

@dataclass(frozen=True)
class Usage:
    """Потребление команды в общем пуле за период, нормированные единицы."""
    team: str
    pool: str
    used: float
    requested: float

def allocate(lines: list[Line], usage: list[Usage]) -> dict:
    direct, pools = defaultdict(float), defaultdict(float)
    unallocated = 0.0

    # 1. Прямые ресурсы — по метке владельца, общие складываем в пулы. O(L)
    for ln in lines:
        if ln.shared_pool:
            pools[ln.shared_pool] += ln.amount
        elif ln.team:
            direct[ln.team] += ln.amount
        else:
            unallocated += ln.amount        # сирота: разговаривать не с кем

    # 2. Веса в пулах по max(использовано, запрошено). O(U)
    weight, pool_total = defaultdict(float), defaultdict(float)
    for u in usage:
        w = max(u.used, u.requested)        # платит тот, кто зарезервировал
        weight[(u.pool, u.team)] += w
        pool_total[u.pool] += w

    shared = defaultdict(float)
    for (pool, team), w in weight.items():
        if pool_total[pool] > 0:
            shared[team] += pools.get(pool, 0.0) * w / pool_total[pool]

    # Пул без данных о потреблении не «растворяется» тихо, а честно виден как ничей.
    unallocated += sum(a for p, a in pools.items() if pool_total.get(p, 0.0) == 0)

    per_team = {t: {"прямые": round(direct.get(t, 0.0), 2),
                    "общие": round(shared.get(t, 0.0), 2)}
                for t in sorted(set(direct) | set(shared))}
    billed = sum(v["прямые"] + v["общие"] for v in per_team.values()) + unallocated
    return {"по командам": per_team,
            "неразнесено": round(unallocated, 2),
            "доля неразнесённого": round(unallocated / billed, 4) if billed else 0.0}

Сложность — O(L + U) по времени и O(T + P) по памяти, где L — строк счёта, U — записей потребления, T — команд, P — пулов; сортировка ключей в выводе добавляет O(T log T). Для месячного CUR это миллионы строк, поэтому в проде агрегация делается запросом на стороне хранилища, а этот код служит исполняемой спецификацией модели, по которой можно проверить любой отчёт.

Отдельный запрос, который стоит настроить в первую неделю, — не «кто самый дорогой», а «что изменилось»:

-- Неделя к неделе по командам: ищем не самых дорогих, а тех, у кого поменялось.
-- Абсолютный размер счёта — известный факт; изменение — новость.
WITH weekly AS (
    SELECT owner_team,
           service,
           date_trunc('week', usage_date) AS wk,
           sum(amortized_cost)            AS usd
    FROM billing_normalized
    WHERE usage_date >= current_date - INTERVAL '9 weeks'
    GROUP BY 1, 2, 3
),
paired AS (
    SELECT owner_team, service, wk, usd,
           lag(usd) OVER (PARTITION BY owner_team, service ORDER BY wk) AS prev,
           avg(usd) OVER (PARTITION BY owner_team, service
                          ORDER BY wk ROWS BETWEEN 8 PRECEDING AND 1 PRECEDING) AS base
    FROM weekly
)
SELECT owner_team, service, wk,
       round(usd)              AS usd,
       round(usd - prev)       AS delta_usd,
       round(100 * (usd - prev) / nullif(prev, 0)) AS delta_pct
FROM paired
WHERE wk = date_trunc('week', current_date - INTERVAL '1 week')
  AND usd - prev > 400            -- порог в деньгах, а не в процентах
  AND usd > 1.4 * nullif(base, 0) -- и это не обычная сезонность
ORDER BY delta_usd DESC
LIMIT 20;

Два порога здесь принципиальны. Порог в деньгах отсекает шум вида «сервис подорожал на 300 %, с 4 до 16 USD». Сравнение с собственной базой отсекает нормальные недельные колебания. Без них вы получите отчёт, который через две недели перестанут открывать.

Юнит-экономика: абсолютный счёт почти ничего не значит

«Счёт вырос на 42 %» — не факт о проблеме. Если за то же время число запросов выросло вдвое, стоимость единицы работы упала, и это успех. Если выручка выросла на 9 % — это проблема. Абсолютное число не значит ничего без знаменателя.

Поэтому платформа должна публиковать не сумму, а набор удельных величин. Каждая из них измерима на существующих данных, и у каждой есть известный способ соврать — фиксируйте его сразу.

Метрика Знаменатель Что показывает Как врёт
Стоимость 1 млн запросов запросы к прод-сервисам эффективность рантайма падает при росте нагрузки сама по себе, из-за постоянных затрат
Стоимость сервиса в месяц живые сервисы из каталога цену «ещё одного сервиса» мёртвые сервисы в каталоге занижают показатель
Стоимость выката успешные выкаты в прод цену конвейера и непрода ухудшается при редких выкатах: постоянные затраты делятся на меньшее число событий
Стоимость окружения в сутки активные превью-стенды цену изоляции не учитывает стенды, забытые включёнными
Инфраструктура на инженера инженеры в продуктовых командах масштабируемость модели растёт при найме продуктовых команд медленнее, чем кажется
Доля непрода в счёте весь счёт дисциплину окружений легко «улучшить», перенеся нагрузку в прод-аккаунт
Доля неразнесённого весь счёт качество учёта улучшается «настройкой правил по умолчанию», а не разметкой

Хороший набор для дашборда руководителя — три строки: общий счёт, стоимость единицы бизнес-работы и доля неразнесённого. Всё остальное — рабочие метрики платформы, не для еженедельного показа (что вообще считать метрикой продукта, юнит-экономика).

Отдельно стоит сказать про стоимость надёжности. Разница между 99,9 % и 99,99 % — это не «чуть больше усилий», это вторая зона доступности, горячий резерв, дублирование хранилища и, часто, удвоение части счёта. Разговор о деньгах и разговор об SLO — один разговор:

запрошенный уровень   →   архитектурное следствие   →   строка в счёте
99,0 %  (tier-3)          одна зона, восстановление из бэкапа      1,0×
99,9 %  (tier-2)          две зоны, тёплый резерв, реплика         1,6×
99,99 % (tier-1)          две-три зоны, горячий резерв, мультирегион 2,4–3,0×

Когда команда просит «сделайте нам как у платежей», правильный ответ — не «нельзя», а «можно, это добавит примерно столько-то в месяц; вот три варианта, выбирайте». Это превращает спор о качестве в решение с ценой (SLO, бюджет ошибок).

Лимиты и квоты: ограничивать, не ломая работу

Учёт показывает, ограничения удерживают. Инструментов здесь пять, и они очень разные по силе побочных эффектов.

Механизм Как работает Сила Побочный эффект
Разумные значения по умолчанию шаблон сервиса задаёт скромные requests, срок жизни, класс хранилища очень высокая почти нет, если легко переопределить
Мягкий бюджет с уведомлением порог на команду, уведомление при 80 % и 100 % высокая привыкание, если уведомление приходит всем и всегда
Автоматический срок жизни (TTL) превью-стенды и временные ресурсы умирают сами высокая ярость, если умерло во время демо: нужна кнопка продления
Жёсткая квота нельзя создать ресурс сверх лимита средняя блокирует работу, включая срочную; требует канала эскалации
Перевыставление счёта (chargeback) сумма списывается с бюджета команды зависит от компании политика, торг, оптимизация отчётности вместо расхода

Главный вывод, который стоит запомнить: самый сильный рычаг — не запрет, а значения по умолчанию. Если шаблон сервиса даёт requests: 200m/256Mi, TTL превью-стенда 48 часов и класс хранилища «стандартный», то большинство сервисов останутся в этих рамках просто потому, что менять их незачем. Это работа на уровне правил игры, а не параметров, и она в разы дешевле любой системы согласований (точки воздействия).

Технические опоры в Kubernetes — ResourceQuota (потолок на namespace) и LimitRange (значения по умолчанию и границы для пода). Вторая штука интереснее первой: она задаёт дефолты, то есть работает без конфликта.

# Бюджет команды: описание, из которого генерируются квота, алерты и строка в отчёте.
apiVersion: platform/v1
kind: TeamBudget
metadata:
  name: payments
spec:
  costCenter: cc-4102
  monthlyBudgetUSD: 18000        # согласовано с владельцем команды, не спущено сверху
  notify:
    channel: "#team-payments"    # в канал команды, а не в общий чат платформы
    thresholds: [0.8, 1.0, 1.25] # предупреждение, достижение, серьёзное превышение
  environments:
    prod:
      hardQuota: false           # прод жёстко не режем никогда
      overspendAction: notify
    staging:
      hardQuota: true
      cpu: "120"
      memoryGi: "480"
    preview:
      hardQuota: true
      maxConcurrent: 12
      ttlHours: 48               # продлевается кнопкой, продление логируется
  emergencyOverride:
    allowed: true
    maxDurationHours: 24         # аварийный подъём лимита без согласований
    autoTicket: true             # запись создаётся автоматически, ревью на следующий день

Три правила, нарушение которых превращает квоты во врага.

Первое: квота никогда не мешает тушить инцидент. Дежурный в три часа ночи должен уметь поднять лимит одной командой, без согласований, а система обязана сама создать запись об этом и напомнить через сутки. Любой механизм, который мешает восстановлению, будет обойден — и правильно сделает (разбор инцидентов).

Второе: прод и непрод — разные режимы. Жёсткие квоты уместны на превью-стендах и стейджинге, где цена ошибки — неудобство. На проде жёсткая квота означает, что в момент пикового трафика платформа запретит автомасштабирование, и вы получите инцидент, за который заплатите больше, чем сэкономили (окружения и их цена).

Третье: бюджет согласуется с владельцем, а не спускается. Число, которое команда не признала своим, не работает как ограничение — оно работает как повод для спора при первом же превышении.

Жизненный цикл бюджета команды удобно держать как явный автомат: он определяет, какие уведомления и какие действия существуют вообще.

Заметьте, чего в автомате нет: состояния «платформа сама срезала ресурсы команде». Такое действие допустимо ровно в двух случаях — ресурс без владельца старше согласованного срока и превью-стенд с истёкшим TTL. Всё остальное решает команда: платформа предлагает, показывает цену и напоминает, но не удаляет чужое.

Команда, которой стандартная модель не подходит

У части команд профиль расходов не описывается общими классами: команда машинного обучения с GPU и пиковыми обучениями, аналитика с ночным batch на сотни узлов, команда с арендованным железом под лицензию. Месячный потолок для них бесполезен: расход по своей природе неровный, и любой порог будет либо пробит всегда, либо бессмысленно велик.

Правильный ход — не втискивать их в общую форму и не оставлять без учёта, а дать третий вариант: отдельный класс с собственным правилом. Бюджет на задачу или на эксперимент вместо месячного потолка; разнесение по GPU-часам вместо CPU; отдельный проект в облаке с обязательством по разметке и еженедельной выгрузкой в общую витрину. Требований к такой команде остаётся ровно два: расход виден в общей витрине и у него есть владелец. Всё остальное — их дело.

Это тот же принцип, что и с золотым путём в целом: путь остаётся путём, пока у съезда есть обозначенный вход, а не забор (золотой путь). Команда, которую вынудили изображать соответствие чужой модели, начнёт прятать расход — заводить ресурсы в личных аккаунтах, класть тяжёлые задачи в общий кластер под видом обычных подов. Вы потеряете и учёт, и доверие, причём в таком порядке.

Разговор с командами о деньгах

Техническая часть — процентов сорок работы. Остальное — разговор, и именно на нём чаще всего всё разваливается.

Три режима видимости

Режим Что происходит Эффект Издержки
Невидимо счёт общий, команды не знают своих цифр расход растёт, оптимизирует только платформа и только техническими средствами ноль политики, максимум расхода
Показ расхода (showback) команда видит свой счёт, деньги списываются централизованно 70–80 % возможного эффекта нужна витрина и разговоры
Перевыставление (chargeback) сумма уходит в бюджет команды сильнейший стимул торг, оптимизация отчётности, попытки уйти в «серые» ресурсы, месяцы согласований

Для подавляющего большинства компаний правильный ответ — showback: он даёт основную часть эффекта при малой политической цене. Chargeback оправдан там, где у команд есть настоящие бюджеты и настоящая свобода ими распоряжаться, — иначе вы получите не экономию, а перекладывание строк между центрами затрат и очень много встреч.

И зеркальное предупреждение: chargeback без возможности влиять на расход — чистое зло. Если команда не может выбрать класс хранилища, потому что платформа спрятала выбор за абстракцией, а счёт за это хранилище ей выставляют, вы наказываете людей за решения, которые приняли сами (где скрывать сложность).

Что не работает

  • Письмо «всем командам сократить расходы на 20 %». Адресат — все, значит никто. Команды с малым счётом потратят время впустую, крупные потребители проигнорируют.
  • Рейтинг «топ-10 самых дорогих команд». Самый дорогой сервис часто и есть самый нагруженный — публичный рейтинг наказывает за успех. Плюс порождает локальную оптимизацию: команды начинают улучшать свою строчку, а не общий результат (локальная оптимизация).
  • Общий дашборд без адресата. Красивая витрина, куда заходят три человека из платформы. Данные должны приходить к людям, а не ждать их.
  • Требование обосновать каждый ресурс. Превращает платформу в бюрократию и убивает самообслуживание, ради которого всё строилось (самообслуживание).
  • Разговор в терминах процентов. «Ваш расход вырос на 60 %» — непонятно. «Ваш расход вырос на 3 400 USD в месяц, это две трети стоимости стажёра» — понятно.

Что работает

Хороший разговор о деньгах устроен как разговор о продукте: адресно, с ценой, с вариантами и с уважением к чужим приоритетам.

Разберём, что именно сделало этот разговор рабочим.

  1. Разбор до предъявления. Платформа пришла не с вопросом «почему у вас дорого», а с готовым диагнозом. Требовать от команды самостоятельно расследовать счёт — перекладывать свою работу.
  2. Сравнение с похожими. «8 200 против 1 800–2 400 у похожих сервисов» действует сильнее любого абсолютного числа, потому что отвечает на вопрос «а это вообще много?».
  3. Названа причина отказа и снят страх. «Боимся снижать после OOM» — реальная и уважительная причина. Правильный ответ — не «всё равно снижайте», а инструмент, снимающий риск: рекомендации VPA, алерт, постепенное снижение.
  4. Готовый PR. Разница между «сделайте» и «вот изменение, посмотрите» — это разница между задачей в чужом бэклоге и десятью минутами ревью.
  5. Закрытие петли. Показать результат и сказать спасибо — то, что делает следующий разговор возможным. Без этого шага у команды остаётся ощущение, что её отчитали.

Шаблон сообщения, который стоит держать под рукой:

Сервис: payments-worker (команда payments)
Сейчас:  8 200 USD/мес — 3,1 % всего счёта организации
Похожие сервисы того же класса: 1 800–2 400 USD/мес
Что видим: requests 8Gi при p95 потребления 1,4Gi; 12 реплик при средней загрузке 11 %
Варианты:
  A. requests 2Gi, HPA 3–12 реплик       → примерно −5 600 USD/мес, риск: нужен алерт на OOM
  B. requests 4Gi, реплик 6              → примерно −3 100 USD/мес, риск минимальный
  C. оставить как есть                   → это осознанное решение, зафиксируем причину
PR с вариантом B готов: <ссылка>. Нужно ваше решение до конца недели, дальше не напоминаем.

Последняя строка важна: «дальше не напоминаем». Платформа предлагает и показывает цену, но не преследует. Вариант «оставить как есть» — легитимный: у команды может быть контекст, которого у вас нет. Зафиксированная причина превращает расход из бесхозного в осознанный, а это и была цель.

Куда приходит цена

Отчёт раз в месяц — слабейшая форма обратной связи. Сильные — те, что встроены в момент решения:

  • План изменений в пул-реквесте показывает дельту стоимости: «+2 узла, примерно +410 USD/мес» (инфраструктура как API).
  • Шаблон сервиса при генерации называет ожидаемую стоимость выбранного класса.
  • Ответ CLI после создания ресурса содержит строку с ценой и ссылкой на способ её снизить.
  • Комментарий бота в PR превью-стенда: «стенд живёт 48 часов, продление — кнопкой».
  • Страница сервиса в каталоге показывает расход рядом с SLO и владельцем — там, где люди и так бывают.

Соотношение примерно такое: одно сообщение в момент решения стоит десяти строк в квартальном отчёте.

Стоимость платформенной команды: граница между сервисом и налогом

Пять инженеров в платформе — это пять инженеров, которые не пишут продукт. Общая арифметика окупаемости разобрана в главе про платформу как продукт и в главе про самообслуживание; здесь добавим то, что обычно теряется, — вычитаемое.

чистая выгода = экономия
              − стоимость платформенной команды
              − инфраструктура самой платформы
              − цена входа (обучение + миграции)
              − цена обходов (сколько команды делают мимо платформы)
              − цена ожиданий (заявки, согласования, ответы поддержки)

Три последних слагаемых — это и есть тот самый третий счёт с картинки в начале главы. Их не надо оценивать «в процентах продуктивности», их надо считать по операциям: сколько раз в год команда проходит миграцию, сколько часов занимает обучение новому контракту, сколько инженеро-часов ушло на собственные скрипты в обход платформы (это видно по репозиториям — см. принятие).

Практика, которая делает разговор честным: квартальный публичный отчёт платформы на одну страницу.

Q3, платформенная команда (5 человек)
1. Стоимость команды                                 187 500 USD
2. Инфраструктура платформы                           31 200 USD
3. Сэкономлено времени команд (по операциям)   1 420 ч ≈ 110 760 USD
4. Сэкономлено на счёте за облако (проверяемо)         96 000 USD
5. Цена входа: 2 миграции × 18 команд            310 ч ≈ 24 180 USD
6. Оценка обходов: 4 команды со своими пайплайнами 260 ч ≈ 20 280 USD
   Итого: 206 760 − 218 700 = −11 940 USD за квартал
   Вывод: около нуля. Сокращаем поддержку gRPC-шаблона (2 пользователя)
   и переносим ёмкость на приёмку конвейера, где очередь.

Отчёт с отрицательным итогом — признак зрелой команды, а не провала. Он предъявляет два числа, которые обычно прячут: цену миграций и цену обходов. И он ведёт к правильному действию: не «добавим ещё функций, чтобы оправдаться», а «сократим то, чем не пользуются». Функция с двумя пользователями почти всегда убыточна: экономия делится на двоих, поддержка постоянна и навсегда.

Отсюда — гейт для любой новой платформенной функции, три вопроса перед началом:

  1. Сколько команд будут пользоваться этим через полгода? Если ответ «одна-две», это работа самой команды, а платформа даёт точку расширения.
  2. Сколько часов в месяц это экономит и откуда взята цифра? Не «ускорит разработку», а измеримая базовая линия и способ проверки.
  3. Сколько это стоит в поддержке через год? Обновления, дежурство, обратная совместимость. Если ответа нет — цена принимается равной 0,25 человека, и решение пересматривается с этой цифрой.

И самое неприятное следствие: если платформа не окупается, правильный ход — сокращать её набор обязательств, а не расширять. Расширение увеличивает знаменатель издержек, а принятие растёт медленнее. Подробнее — в главе про платформенную команду.

Типовые провалы

Обёртка над облаком, скрывающая цену. Платформа спрятала выбор типа инстанса и класса хранилища за абстракцией tier: standard, но не показала, во что это обходится. Команда не может ни узнать стоимость, ни повлиять на неё. Через год никто в компании не в состоянии объяснить счёт, а любая попытка оптимизации упирается в «это платформа так решила». Правило: абстракция может убрать выбор, но обязана показать цену.

Портал с дашбордом расходов, которым никто не пользуется. Классика: полгода работы, красивые графики, восемь заходов в месяц, из них шесть — от автора. Причина всегда одна: данные ждут человека вместо того, чтобы приходить к нему. Лечится не редизайном, а доставкой цены в момент решения.

«Мы сделали абстракцию» поверх трёх разных потребностей. У аналитики почасовые пики и терабайты холодных данных, у платежей — постоянная нагрузка и жёсткие требования к задержке, у внутренних инструментов — почти нулевой трафик. Единая модель tier не описывает ни одну из трёх: платежи получают избыточное хранилище, аналитика — недостаточную ёмкость, внутренние инструменты платят за резерв, который им не нужен. Разнесение расходов по такой модели ещё и врёт, потому что удельные величины несравнимы между группами. Признак вовремя: если для трёх ваших крупнейших потребителей приходится делать три исключения — это не одна абстракция, а три продукта (где скрывать сложность).

Оптимизация ради оптимизации. Три недели инженера платформы (около 9 400 USD полной стоимости), чтобы сэкономить 400 USD в месяц. Окупаемость — два года, при условии что решение доживёт. Такие задачи надо приоритизировать явно, как продуктовые (приоритизация):

Резервирование под то, что скоро мигрирует. Годовые Savings Plans или зарезервированные инстансы (что это такое) дают скидку 20–40 %, но фиксируют вас в конкретном профиле потребления. Купить резерв за месяц до переезда на другой тип инстансов — оплатить скидку дважды. Правило: резервируется только та часть базовой нагрузки, про которую вы уверены, что она не изменится в горизонте обязательства, обычно 50–70 % минимума за прошлый квартал. Остальное — по требованию.

Учёт без полномочий. Появляется человек «по FinOps», который делает отчёты и рассылает их по почте. Полномочий менять шаблоны, дефолты и политики у него нет. Через два квартала отчёты перестают открывать. Учёт расходов работает только когда он встроен в платформу, а не пристроен сбоку (границы команд).

Уплотнение узлов как ответ на всё. Bin-packing и консолидация действительно снижают счёт за узлы, но они же увеличивают связность отказов: одна нода теперь несёт больше сервисов, вытеснение затрагивает больше пользователей, а всплеск потребления одного пода задевает соседей. Это сделка, а не улучшение: считайте её вместе с надёжностью, а не отдельно (ёмкость и запас).

Цена владения инструментами учёта

Никакой инструмент здесь не является ни решением, ни признаком зрелости. У каждого есть цена, и назвать её надо до внедрения.

Инструмент Что реально получаете Чем платите Когда не нужен
Встроенные отчёты провайдера ноль усилий, приемлемая точность по тегам нет разнесения общих ресурсов, слабо про Kubernetes почти никогда — начинать надо с них
Экспорт биллинга в хранилище (CUR, BigQuery) полный контроль, свои модели, история хранение и запросы стоят денег; нужен владелец витрины счёт меньше нескольких десятков тысяч в месяц
OpenCost / Kubecost стоимость нагрузки в кластере по namespace и поду ещё один компонент в проде: обновления, метрики, ресурсы один кластер и десяток сервисов: хватит usage-метрик
Коммерческие FinOps-платформы готовые дашборды, рекомендации, поддержка подписка, обычно процент от счёта; чужая модель разнесения пока не сделана разметка — инструмент не починит её за вас
Плагин расходов в портале цена там, где люди уже бывают ещё один плагин портала со своими обновлениями если портал не используется, ничего не изменится

Общее правило: инструмент не создаёт данные, он их показывает. Если 40 % расхода не размечено, покупка платформы за 30 000 USD в год покажет вам те же 40 % неизвестности в более красивом виде. Порядок работ всегда один: разметка → модель разнесения → доставка в момент решения → и только потом, если объём оправдывает, инструмент.

С чего начать: первые шесть недель

Практический маршрут для команды, у которой сейчас нет ничего.

  1. Неделя 1. Включить экспорт биллинга в хранилище. Посчитать одну цифру — долю расхода без метки владельца. Это ваша базовая линия и первый разговор с руководством.
  2. Неделя 2. Договориться о шести обязательных ключах меток. Внести их в шаблон сервиса и модули IaC. Не рассылать просьбу проставить теги руками — это не сработает.
  3. Неделя 3. Собрать простейшую витрину: команда × сервис × день. Опубликовать модель разнесения на одну страницу. Показать трём командам их цифры и спросить, узнают ли они себя.
  4. Неделя 4. Настроить алерт на изменение, а не на абсолютное значение, с порогом в деньгах. Отправлять в каналы команд.
  5. Неделя 5. Взять три самых дорогих сервиса и провести три личных разговора по шаблону из этой главы, с готовыми PR. Не рассылать письмо всем.
  6. Неделя 6. Поставить скромные значения по умолчанию в шаблоне и TTL на превью-стенды. Это даст больше, чем всё остальное вместе взятое, и не потребует ни одного согласования.

Чего в этом списке нет: покупки инструмента, введения chargeback, жёстких квот на проде и общего письма про экономию. Всё это может понадобиться позже — но не в первые шесть недель, когда нет данных.

Типичные ошибки

  • Начинать с экономии, а не с видимости. Кампания без данных даёт разовый эффект и стойкое раздражение.
  • Требовать разметку от людей. Метки ставит платформа при создании ресурса, иначе их не будет.
  • Считать абсолютный счёт без знаменателя. Рост счёта при росте бизнеса нормален; рост удельной стоимости — сигнал.
  • Жёсткие квоты на проде и публичные рейтинги «самых дорогих команд». Первое покупает инцидент за небольшие деньги, второе наказывает за нагрузку и поощряет локальную оптимизацию.
  • Предъявлять счёт без диагноза и без вариантов. Перекладывание своей работы на пользователя.
  • Прятать цену за абстракцией и одновременно выставлять её команде. Ответственность без возможности влиять — самый быстрый способ потерять пользователей платформы.
  • Не считать цену миграций и обходов. Без этих слагаемых модель окупаемости всегда положительна и всегда неверна.
  • Оптимизировать то, что легко измерить, вместо того, что дорого. Разрыв между requests и потреблением обычно интереснее, чем весь длинный хвост мелких сервисов.
  • Менять модель разнесения каждый квартал. История становится несравнимой, доверие к цифрам — нулевым.

Мини-итог

Стоимость платформы — это три разных счёта: инфраструктура, платформенная команда и время продуктовых команд. Первый обсуждают каждый месяц, третий не считают никогда, хотя он часто больше бюджета всей платформенной команды.

Работа со стоимостью — это не экономия, а починка контура обратной связи. Расход должен быть адресным (метки ставит платформа, а не человек), общие ресурсы должны разноситься по публично описанному правилу (лучше всего по max(потребление, requests)), а доля неразнесённого — быть метрикой качества платформы, а не бухгалтерской деталью. Абсолютное число ничего не значит без знаменателя: считайте удельные величины и следите за ними, а не за суммой.

Ограничения работают в обратном порядке к своей заметности: скромные значения по умолчанию и TTL сильнее любых жёстких квот, а жёсткая квота на проде — способ купить инцидент за небольшие деньги. Квота никогда не должна мешать тушить пожар.

Разговор о деньгах устроен как продуктовый: адресно, с готовым диагнозом, со сравнением с похожими, с вариантами и ценой каждого, с готовым PR и с правом команды сказать «оставим как есть». А сама платформенная команда обязана публиковать честный счёт своей окупаемости — вместе с ценой миграций и обходов. Отчёт, из которого следует «сокращаем то, чем не пользуются», полезнее любого отчёта, из которого следует «нам нужно больше людей».

Что дальше

Мы научились считать деньги и разговаривать о них. Следующая тема — самая дорогая операция платформы из всех: перевод десятков команд с одного решения на другое, где цена входа из абстрактного слагаемого превращается в реальные месяцы чужой работы.

Миграции: как переводить десятки команд и не сорвать поставку

Источники

  • FinOps Foundation: FOCUS — открытая спецификация данных о расходах, и FinOps Framework — общий язык с финансистами.
  • J. R. Storment, M. Fuller. Cloud FinOps, O’Reilly — организационный слой: кто с кем разговаривает и о чём. OpenCost — расчёт стоимости нагрузки в Kubernetes.
  • Kubernetes: ResourceQuota, LimitRange. AWS: CUR, cost allocation tags, Savings Plans. GCP billing export, Azure Cost Management exports.
  • G. Hardin. The Tragedy of the Commons, Science, 1968; E. Ostrom. Governing the Commons, Cambridge University Press, 1990 — почему общий ресурс не обязан деградировать, если есть границы, видимость потребления и правила.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов