ловить скачки стоимости namespace и безопасно автоматизировать реакцию
Обнаружение аномалий стоимости в Kubernetes: оповещения и автоматическое исправление с OpenCost
14 минут
Ежемесячный счёт приходит, когда перерасход уже случился. OpenCost отдаёт метрики в Prometheus, скользящие базовые линии ловят скачки по namespace, а Alertmanager при критическом превышении бюджета запускает Argo Workflows — после того как команды две–четыре недели смотрели дашборды.
Почему ежемесячный биллинг не ловит скачки в Kubernetes
Расходы в Kubernetes растут незаметно, пока финансы не откроют счёт. Отложенные отчёты не успевают за нагрузками, которые днём масштабируются, а ночью сжимаются. К моменту скачка в выписке деньги уже потрачены. Типичные сценарии: один namespace разворачивает неограниченные реплики и забирает большую часть кластера; завышенные requests резервируют CPU и память, которые не используются; HPA или KEDA наращивают узлы без сигнала в долларах; эфемерные preview-окружения не удаляются. Оповещения только по CPU или памяти не отражают бизнес-сторону: узел на девяноста процентах CPU — это эксплуатация, namespace в три раза дороже бюджета — это FinOps. Kubecost и его ядро OpenCost в CNCF используют одну модель аллокации: OpenCost — открытый путь к метрикам, Kubecost добавляет UI, API бюджетов и корпоративные интеграции. Здесь — OpenCost с Prometheus, Alertmanager и Argo Workflows.
Три слоя: метрики, обнаружение и исправление
Цепочка остаётся внутри кластера. OpenCost сопоставляет аллокацию контейнеров с почасовыми тарифами узлов и экспортирует метрики Prometheus — container_cpu_allocation, node_cpu_hourly_cost и аналоги для памяти. Recording rules Prometheus считают почасовую стоимость по namespace, скользящее среднее и стандартное отклонение. Alertmanager шлёт предупреждения в Slack, а критическое превышение бюджета — в сценарий исправления. Argo Workflows уменьшает реплики некритичных Deployment с нужными метками и уведомляет владельцев — только идемпотентные действия, без удаления namespace. Сначала покажите дашборды две–четыре недели, затем включайте автоматическое принуждение, чтобы команды доверяли цифрам.
Развёртывание OpenCost и сбор метрик в Prometheus
Установите OpenCost в отдельный namespace и настройте Prometheus на endpoint метрик на порту 9003. Задайте облачные тарифы или custom rates, чтобы node_cpu_hourly_cost отражал on-demand или reserved pricing. OpenCost читает kube-state-metrics и данные использования, уже попадающие в Prometheus; он их не заменяет. В мультикластере скрейпьте каждый экземпляр OpenCost и добавьте метку cluster в metric_relabel_configs. В Kubecost при включённом cost analyzer доступны совместимые метрики аллокации с теми же именами.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: opencost
namespace: opencost
spec:
interval: 1h
chart:
spec:
chart: opencost
sourceRef:
kind: HelmRepository
name: opencost
namespace: flux-system
values:
opencost:
exporter:
defaultClusterId: prod-us-east-1
prometheus:
external:
enabled: true
url: http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090scrape_configs:
- job_name: opencost
scrape_interval: 1m
scrape_timeout: 15s
metrics_path: /metrics
static_configs:
- targets:
- opencost.opencost.svc.cluster.local:9003
metric_relabel_configs:
- target_label: cluster
replacement: prod-us-east-1Базовые линии и алерты на PromQL из OpenCost
Не опирайтесь на несуществующую серию kubecost_namespace_cost_daily — считайте почасовую стоимость namespace из аллокации и тарифов узлов. Запишите это значение, затем семидневное среднее и стандартное отклонение по каждому namespace. Алерт срабатывает, если текущая почасовая стоимость выше среднего плюс две сигмы в течение часа. Жёсткий дневной лимит сравнивайте через почасовую стоимость, умноженную на двадцать четыре. Метрики аллокации отражают requests, а не фактическое использование — дополняйте алерты рекомендациями VPA по right-sizing. Для месячной отчётности используйте тридцатидневное окно на тех же recording rules.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cost-anomaly-detection
namespace: monitoring
spec:
groups:
- name: cost.baseline
interval: 5m
rules:
- record: namespace:cost_hourly_usd
expr: |
sum by (namespace) (
container_cpu_allocation
* on (node) group_left()
node_cpu_hourly_cost
+
container_memory_allocation_bytes
* on (node) group_left()
node_ram_hourly_cost
/ 1024 / 1024 / 1024
)
- record: namespace:cost_hourly_avg_7d
expr: avg_over_time(namespace:cost_hourly_usd[7d])
- record: namespace:cost_hourly_stddev_7d
expr: stddev_over_time(namespace:cost_hourly_usd[7d])
- name: cost.anomaly_alerts
rules:
- alert: NamespaceCostAnomalyHigh
expr: |
namespace:cost_hourly_usd
> (namespace:cost_hourly_avg_7d + 2 * namespace:cost_hourly_stddev_7d)
for: 1h
labels:
severity: warning
annotations:
summary: "Аномалия стоимости в namespace {{ $labels.namespace }}"
description: >
Почасовая стоимость ${{ $value | humanize }} превышает
семидневную базу более чем на две сигмы.
- alert: NamespaceBudgetExceeded
expr: namespace:cost_hourly_usd * 24 > 10000
for: 30m
labels:
severity: critical
annotations:
summary: "Превышен дневной бюджет в {{ $labels.namespace }}"
description: >
Прогнозируемые суточные расходы превышают лимит $10 000 для namespace.Исправление через Argo Workflows при нарушении бюджета
Автоматическое уменьшение реплик оставьте для критических алертов, когда уже есть каналы уведомления людей. Пометьте Deployment меткой cost-tier=critical для баз и шлюзов, cost-tier=best-effort для пакетных задач и dev-инструментов. Workflow ставит best-effort Deployment на одну реплику — действие идемпотентно и обратимо после разбора бюджета. В продакшене Alertmanager обычно подключают через Argo Events Sensor; webhook на events API Argo Server здесь иллюстрирует передачу. Не удаляйте namespace и PVC из cost-автоматизации. Сохраняйте прежнее число реплик в журнале, если нужен ручной откат.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: cost-remediation-
namespace: argo-workflows
spec:
entrypoint: remediate
arguments:
parameters:
- name: namespace
- name: current_cost
templates:
- name: remediate
dag:
tasks:
- name: scale-down-non-critical
template: scale-deployments
- name: notify-team
template: send-notification
dependencies: [scale-down-non-critical]
- name: scale-deployments
container:
image: bitnami/kubectl:1.30
command: [sh, -c]
args:
- |
set -euo pipefail
echo "Уменьшаем некритичные deployment в ${NAMESPACE}"
kubectl scale deployment \
-n "${NAMESPACE}" \
-l 'cost-tier=best-effort' \
--replicas=1
echo "Исправление завершено для ${NAMESPACE}"
env:
- name: NAMESPACE
value: "{{workflow.parameters.namespace}}"
- name: send-notification
container:
image: curlimages/curl:8.5.0
command: [sh, -c]
args:
- |
curl -fsS -X POST "${SLACK_WEBHOOK}" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Исправление по стоимости для ${NAMESPACE}: некритичные deployment уменьшены до 1 реплики. Почасовой сигнал: {{workflow.parameters.current_cost}}\"}"
env:
- name: NAMESPACE
value: "{{workflow.parameters.namespace}}"
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
name: cost-alerts-slack
key: webhook-urlМаршрутизация Alertmanager и предохранители ResourceQuota
NamespaceCostAnomalyHigh направляйте в Slack команды-владельца. NamespaceBudgetExceeded — в сценарий исправления и на дежурного FinOps платформы. continue: true оставляет дублирующее уведомление в Slack для критических алертов. ResourceQuota задаёт жёсткие потолки CPU, памяти и объектов — долларовый бюджет в нём хранить нельзя. Согласуйте лимиты квот с целевой стоимостью и дополняйте их dollar-алертами Prometheus. Kyverno может требовать метку cost-tier при admission, чтобы селекторы исправления были предсказуемыми.
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
name: cost-alerts
namespace: monitoring
spec:
route:
receiver: slack-cost-team
routes:
- matchers:
- name: alertname
value: NamespaceBudgetExceeded
receiver: argo-workflows
continue: true
- matchers:
- name: alertname
value: NamespaceCostAnomalyHigh
receiver: slack-cost-team
receivers:
- name: argo-workflows
webhookConfigs:
- url: http://argo-events-eventsource-svc.argo-events.svc:12000/cost-remediation
sendResolved: true
- name: slack-cost-team
slackConfigs:
- apiURL:
name: slack-webhook
key: url
channel: "#cost-alerts"
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'apiVersion: v1
kind: ResourceQuota
metadata:
name: cost-guardrails
namespace: team-backend
labels:
team: backend
cost-center: "cc-2201"
spec:
hard:
requests.cpu: "40"
requests.memory: 80Gi
limits.cpu: "80"
limits.memory: 160Gi
pods: "100"
services.loadbalancers: "2"Практика: уровни порогов и обратимая автоматизация
Три уровня алертов: info при полутора сигмах — только аннотация на дашборде; warning при двух сигмах час — уведомление команде; critical при прогнозе суточных расходов выше лимита тридцать минут — уменьшение реплик и вызов дежурного. Обновляйте базы: семь дней для оперативных аномалий, тридцать — для месячного обзора. Атрибуируйте расходы метками team и cost-center на namespace и Deployment; OpenCost агрегирует по меткам для отчётности командам. Сопоставляйте стоимость на запрос с SLO по задержке — финансы и инженеры смотрят в один стек наблюдаемости. Рост стоимости от HPA разбирайте вместе с алертами на saturation. Обнаружение аномалий — операционная дисциплина: наблюдайте через дашборды OpenCost, оповещайте статистикой Prometheus, действуйте осторожной автоматизацией Argo — и подгоняйте пороги под допустимый риск.
Жёсткие лимиты namespace и метки cost-center дополняют обнаружение аномалий из гайда по FinOps на уровне namespace с ResourceQuota и атрибуцией.
Цикл «наблюдение → оповещение → действие» вписывается в фазы FinOps из гайда по FinOps для облачно-нативных платформ.
