Karpenter подбирает нужные инстансы и упаковывает поды плотнее — счёт от облака за месяц падает. Но счёт всё равно приходит одной строкой. Сколько из этой суммы съела команда платформы, сколько — команда данных, а сколько — тестовый namespace, который забыли снести три месяца назад, из инвойса не видно. Отчёт Cast AI «2026 State of Kubernetes Optimization» фиксирует среднюю утилизацию CPU продовых кластеров на уровне 8% — источник, — и даже если автоскейлинг честно поднял эту цифру вдвое, вопрос «чей это счёт» автоскейлинг не решает в принципе: он оптимизирует ноду, а не бюджет команды. OpenCost — CNCF-проект, который встаёт рядом с уже работающим Prometheus и превращает суммарный счёт в чарджбэк по namespace, deployment и label, бесплатно.
Содержание
Открыть содержание
- Автоскейлинг узлов — не то же самое, что аллокация по командам
- Как Kubecost стал OpenCost — и куда делся после IBM
- Модель аллокации: как OpenCost считает стоимость пода
- Сравнение: OpenCost vs Kubecost после покупки IBM
- Что нужно, чтобы поставить OpenCost рядом с Prometheus
- Как проверить, что цифры реальные, а не list price
- Итог
Автоскейлинг узлов — не то же самое, что аллокация по командам
Karpenter (или cluster-autoscaler) решает одну конкретную задачу: подобрать под текущую нагрузку кластера как можно более дешёвый и плотно упакованный набор нод. Он смотрит на requests и limits подов, добавляет или убирает ноды, при возможности переезжает на spot — и делает это хорошо. Но у него в принципе нет понятия «команда» или «продукт»: он видит поды как объекты с ресурсными требованиями, а не как строки в чужом P&L.
Проблема в том, что нода почти всегда общая. На одном c5.2xlarge уживаются под платформенной команды, под команды данных и десяток служебных подов kube-system. Стоимость этой ноды известна — она есть в счёте AWS. А вот как разделить эту сумму между тремя командами — вопрос, на который Karpenter не отвечает, потому что не обязан.
Здесь и появляется OpenCost: он не трогает автоскейлинг и ничего не решает за Karpenter на уровне нод — он добавляет слой аллокации сверху, который берёт ту же ноду и раскладывает её стоимость по labels, namespace и deployment, опираясь на реальное потребление CPU/memory каждым подом.
Это не косметическая надстройка. Без такого слоя единственный способ разобраться, кто проедает бюджет, — вручную сопоставлять теги ресурсов в облачной консоли с namespace в кластере, что работает до первого shared-кластера с десятком команд и разваливается сразу после. С аллокацией по namespace тот же вопрос превращается в один запрос вместо получаса в консоли биллинга.
Как Kubecost стал OpenCost — и куда делся после IBM
OpenCost начинался как внутренний движок аллокации у стартапа Kubecost. В июне 2022 года ядро было выделено и передано в CNCF как отдельный vendor-neutral проект — сейчас это CNCF Incubating Project с открытым кодом, без привязки к какому-либо одному облаку. В сентябре 2024 года IBM купила саму компанию Kubecost, встроив её в свой FinOps-набор рядом с Apptio Cloudability и Turbonomic — источник. Kubecost как коммерческий продукт теперь окончательно ориентирован на enterprise: мультикластерная агрегация, долгое хранение истории, SSO, рекомендации по right-sizing и алертинг — за подписку.
OpenCost при этом никуда не делся и не осиротел: IBM публично подтвердила продолжение инвестиций в проект как в CNCF-инициативу, а сам Kubecost как продукт по-прежнему построен поверх открытого движка OpenCost — источник. Разница теперь предельно прагматичная: OpenCost — это движок аллокации и его собственный UI/API, бесплатно и без ограничений по числу кластеров; Kubecost — тот же движок плюс enterprise-обвязка, которую продают отдельно.
Модель аллокации: как OpenCost считает стоимость пода
OpenCost не изобретает собственную телеметрию — он читает те же метрики, что уже собирает cAdvisor через Prometheus, и превращает их в деньги.
Расчёт идёт в два действия:
- Аллокация ресурсов. Для каждого контейнера OpenCost берёт
max(request, usage)по CPU и памяти — то есть считает по фактически занятому ресурсу, если он больше заявленного request, и по request, если под простаивает ниже заявленного. Это защищает от двух перекосов сразу: команда с завышенными requests не платит за фактически неиспользуемое, а команда, вылезающая за свои requests за счёт burst, не получает счёт по заниженной цифре. - Тариф ноды. Стоимость ноды по умолчанию берётся из статичного прайслиста облака (
node_cpu_hourly_cost,node_ram_hourly_cost), но OpenCost умеет подключаться к реальному биллингу и сверять эти цифры с фактическим инвойсом: AWS CUR через Athena, GCP Billing export через BigQuery, Azure Rate Card API для EA/MCA-контрактов. Сверка учитывает Savings Plans, Reserved Instances и spot-скидки — то есть даёт цену, которую команда реально платит, а не публичный прайс. Лаг сверки — 24–48 часов, пока данные биллинга не выгрузятся с той стороны.
Помимо CPU и памяти OpenCost аллоцирует ещё сетевой egress и persistent volumes — тоже по namespace/label, тем же принципом «фактическое потребление × реальная цена». Для сетевого трафика это особенно важно: internal traffic между availability zones и egress наружу облака часто оказывается заметной статьёй расходов, но по умолчанию в биллинге виден только суммарный трафик аккаунта — без разбивки по тому, какой сервис его сгенерировал.
Аллокация по умолчанию идёт по namespace, но реальный чарджбэк почти всегда завязан на произвольный label — например, team или cost-center, если команды используют общий namespace под несколько поддоменов ответственности. OpenCost умеет группировать и по ним: достаточно, чтобы под нёс нужный label, а не переносить workload в отдельный namespace ради одной строчки в отчёте.
Сравнение: OpenCost vs Kubecost после покупки IBM
| Параметр | OpenCost (CNCF, open source) | Kubecost (IBM/Apptio) |
|---|---|---|
| Лицензия | Apache 2.0, бесплатно | коммерческая подписка |
| Движок аллокации | тот же самый | тот же самый (построен поверх OpenCost) |
| Число кластеров | не ограничено | тарифицируется по кластерам |
| История данных | ограничена ретеншеном Prometheus | долгое хранение в отдельном хранилище |
| Мультикластерная агрегация | нужно собирать вручную | из коробки |
| Right-sizing рекомендации | нет | да |
| Алертинг по бюджетам | нет встроенного (строится на Prometheus-алертах) | встроенный |
| SSO / RBAC для UI | нет | да |
| Поддержка вендора | community, GitHub issues | SLA от IBM |
Для одного кластера или для команды, которая готова сама построить дашборд и алерты поверх метрик, разница практически не ощущается — движок один и тот же. Разница проявляется на масштабе: десятки кластеров, требование SSO для доступа к финансовым данным, необходимость вендорской поддержки с SLA.
Что нужно, чтобы поставить OpenCost рядом с Prometheus
Предполагается, что Prometheus в кластере уже есть (собирает метрики из cAdvisor и kube-state-metrics) — OpenCost не поднимает свой сборщик метрик, а читает существующий.
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost/opencost \
--namespace opencost --create-namespace \
--set opencost.prometheus.external.enabled=true \
--set opencost.prometheus.external.url=http://prometheus-server.monitoring.svc:9090
Дальше ставим kubectl-плагин, чтобы не ходить в UI за каждой цифрой:
kubectl krew install cost
kubectl cost namespace --window 7d
+---------+---------------+--------------------+-----------------+
| CLUSTER | NAMESPACE | MONTHLY RATE (ALL) | COST EFFICIENCY |
+---------+---------------+--------------------+-----------------+
| | team-data | 412.30 | 0.41 |
| | team-platform | 268.90 | 0.63 |
| | staging | 94.10 | 0.09 |
+---------+---------------+--------------------+-----------------+
COST EFFICIENCY здесь — доля реально использованных ресурсов от аллоцированных: staging с эффективностью 0.09 — это ровно тот namespace, который платит за requests, которые почти никогда не выбираются, и первый кандидат на ревью лимитов.
Для чарджбэка по деплойментам вместо плагина удобнее один PromQL-запрос прямо в уже поднятый Grafana поверх метрик, которые OpenCost сам экспортирует на :9003/metrics:
topk(5,
sum by (namespace, deployment) (
container_cpu_allocation * on (node) group_left node_cpu_hourly_cost
+
container_memory_allocation_bytes / (1024*1024*1024)
* on (node) group_left node_ram_hourly_cost
)
)
Готовые дашборды под тот же набор метрик уже есть в Grafana Labs — OpenCost / Overview для картины по кластеру и OpenCost / Workload для разбора внутри namespace по deployment — их достаточно импортировать по ID, отдельный ETL не нужен.
Как проверить, что цифры реальные, а не list price
Пока cloud-биллинг не подключен, OpenCost честно считает по статичному прайслисту — и это стоит явно проверить, а не полагаться на то, что “работает — значит правильно”.
kubectl get pods -n opencost -l app=cost-analyzer
curl -s http://localhost:9003/metrics | grep node_total_hourly_cost
# node_total_hourly_cost{instance="ip-10-0-4-12",...} 0.384
Если после настройки AWS/GCP/Azure-интеграции значение node_total_hourly_cost для конкретной ноды заметно отличается от того, что было до неё — сверка подключена и работает. Если цифра не меняется 48 часов после конфигурации, стоит проверить права доступа к CUR-бакету или к BigQuery-датасету: чаще всего сверка молча не запускается именно из-за них, а не потому что данных ещё нет.
Итог
Автоскейлинг узлов и аллокация затрат по командам — это две независимые задачи, и одна не решает другую. Karpenter экономит на инфраструктуре тем, что плотнее упаковывает ноды и подбирает более дешёвые инстансы, но эта экономия распределена по всему кластеру безлично. Понять, чья команда её на самом деле проедает, требует отдельного слоя аллокации — и хорошая новость в том, что этот слой не нужно покупать: OpenCost делает ровно это поверх метрик, которые Prometheus и так уже собирает.