распределять расходы кластера по командам и автоматизировать ограничения по затратам
FinOps для облачно-нативных платформ: от видимости до автоматизированного управления затратами
14 минут
В общем кластере Kubernetes непонятно, кто и сколько тратит, пока финотдел не получит счёт. FinOps связывает потребление пространств имён с деньгами, убирает лишние расходы за счёт корректного размера ресурсов и автоматизирует квоты и оповещения без замедления поставки.
Почему общие кластеры ломают учёт затрат
Платформенная команда знает, что развёрнуто в кластере, но часто не может ответить, сколько стоит то или иное пространство имён и какая продуктовая команда дала скачок расходов за месяц. В мультитенантном Kubernetes счёт приходит на весь кластер, пул нод или облачный аккаунт, а отдельные команды теряют связь между своими сервисами и цифрами в биллинге. Финансы видят одну сумму, инженеры не объясняют разницу между предпродакшеном и продакшеном или между арендаторами. Завышенные запросы CPU и памяти у подов мешают плотно размещать нагрузку на нодах и раздувают их число. Неиспользуемые тома PVC, балансировщики LoadBalancer без трафика и полупустые пулы нод тихо съедают бюджет. Разовые «оптимизации» отдельных команд без согласования бьют по общим сервисам. Без выстроенной практики FinOps организация либо переплачивает из осторожности, либо недодаёт ресурсов и ловит инциденты. FinOps — это общий язык затрат для инженерии, финансов и эксплуатации и три непрерывных цикла: сначала видимость, затем оптимизация, затем автоматический контроль.
Видимость затрат: связать Kubernetes с деньгами через Kubecost или OpenCost
Первый шаг — показать, сколько стоят CPU, память, диски и сеть в разрезе пространств имён, меток, развёртываний и контейнеров. Установите Kubecost или проект CNCF OpenCost в каждом кластере. Подключите выгрузку облачного биллинга — AWS CUR, GCP BigQuery billing, Azure Cost Management — чтобы в отчётах для команд были не только метрики из кластера, но и плоскость управления, отцепленные диски и исходящий трафик. На каждое развёртывание повесьте единые метки team, service и environment и сопоставьте их с категориями затрат в конфигурации Kubecost. Если кластеров несколько, на каждом держите OpenCost и собирайте его метрики в центральный Prometheus или систему удалённой записи метрик. Покажите командам разбивку расходов до того, как вводить жёсткие квоты — иначе ограничения воспринимают как необоснованные.
global:
clusterId: production-us-east-1
kubecostProductConfigs:
clusterName: production-us-east-1
labelMappingConfigs:
enabled: true
productLabelList:
- team
- app
- environment
# Enable cloud billing integration in Kubecost UI for AWS/GCP/Azure
# so node, disk, and egress costs reconcile with CUR or billing exportscrape_configs:
- job_name: opencost
scrape_interval: 1m
static_configs:
- targets:
- opencost.opencost.svc.cluster.local:9003
metric_relabel_configs:
- source_labels: [__address__]
target_label: cluster
replacement: production-us-east-1Оптимизация: подобрать размер подов и убрать мёртвую ёмкость
Отчёты сами по себе деньги не экономят. Чаще всего CPU запрашивают в три–пять раз больше нужного — так проще пройти планирование подов, но дороже содержать кластер. Поставьте Vertical Pod Autoscaler в режим только рекомендаций, раз в неделю сверяйте советы с реальной нагрузкой и меняйте requests осознанно. При плавающем трафике дополняйте VPA горизонтальным автомасштабированием. Регулярно ищите тома PVC, к которым не подключён ни один под, сервисы LoadBalancer без конечных точек и пулы нод, загруженные меньше чем на пятнадцать процентов дольше недели. Сжимайте простой через cluster autoscaler или консолидацию нод в Karpenter. Пакетные задачи выносите на прерываемые spot-ноды, если приложение переживает внезапное выселение. Только за счёт корректного размера ресурсов команды нередко снижают расходы на вычисления на тридцать–пятьдесят процентов — ещё до архитектурных переделок.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
namespace: team-alpha
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 4000m
memory: 8Gi
controlledResources: ["cpu", "memory"]#!/usr/bin/env bash
set -euo pipefail
echo "=== PVCs with no mounting pod ==="
kubectl get pvc -A -o json | jq -r '
.items[] | [.metadata.namespace, .metadata.name] | @tsv
' | while IFS=$'\t' read -r ns name; do
in_use=$(kubectl get pods -n "$ns" -o json | jq --arg n "$name" '
[.items[].spec.volumes[]? | select(.persistentVolumeClaim.claimName == $n)] | length
')
if [[ "$in_use" -eq 0 ]]; then
echo "orphan: ${ns}/${name}"
fi
done
echo "=== LoadBalancer services ==="
kubectl get svc -A -o json | jq -r '
.items[]
| select(.spec.type == "LoadBalancer")
| "\(.metadata.namespace)/\(.metadata.name) -> \(.status.loadBalancer.ingress[0].hostname // "pending")"
'Контроль: квоты, политики admission и регулярные отчёты
На финальном этапе правила срабатывают сами, без ручных «проверок каждого деплоя». ResourceQuota ограничивает суммарное потребление пространства имён: CPU, память, число PVC и балансировщиков. LimitRange задаёт разумные значения по умолчанию, чтобы в манифесте нельзя было забыть блок resources. Kyverno или OPA отклоняют поды с запросами выше потолка команды — это лимит по ресурсам, не по рублям или долларам; денежные пороги закрывайте бюджетными алертами Kubecost. Сначала шлите предупреждение в Slack при восьмидесяти процентах месячного плана, жёсткие квоты вводите после двух–трёх полных биллинговых циклов, когда команды успели подстроиться. Раз в неделю выгружайте топ пространств имён по затратам заинтересованным сторонам. Резкий рост стоимости одного namespace в три раза трактуйте так же серьёзно, как скачок задержки или ошибок — заведите такой сигнал в ту же систему оповещений.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "40"
requests.memory: 80Gi
limits.cpu: "80"
limits.memory: 160Gi
persistentvolumeclaims: "20"
pods: "100"
services.loadbalancers: "2"
---
apiVersion: v1
kind: LimitRange
metadata:
name: team-alpha-defaults
namespace: team-alpha
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: 500m
memory: 512Mi
max:
cpu: "4"
memory: 8GiapiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cap-container-requests
spec:
validationFailureAction: Enforce
background: true
rules:
- name: limit-cpu-memory-requests
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Container requests exceed team ceiling (cpu 4, memory 8Gi)"
pattern:
spec:
containers:
- resources:
requests:
cpu: "<=4"
memory: "<=8Gi"apiVersion: batch/v1
kind: CronJob
metadata:
name: cost-report
namespace: kubecost
spec:
schedule: "0 9 * * 1"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cost-export
image: curlimages/curl:8.5.0
env:
- name: SLACK_WEBHOOK_URL
valueFrom:
secretKeyRef:
name: slack-webhook
key: url
command:
- /bin/sh
- -c
- |
set -euo pipefail
report=$(curl -fsS \
"http://kubecost-cost-analyzer.kubecost:9090/model/allocation?window=7d&aggregate=namespace&accumulate=true" \
| jq -r 'to_entries | sort_by(.value.totalCost) | reverse | .[0:5][] | "\(.key): $\(.value.totalCost | floor)"')
payload=$(jq -n --arg text "Weekly top namespaces (7d):\n$report" '{text: $text}')
curl -fsS -X POST "$SLACK_WEBHOOK_URL" -H 'Content-Type: application/json' -d "$payload"Практические правила: общие расходы, проверки в CI и ежемесячный разбор
Начинайте с прозрачности, а не с запретов — люди сопротивляются правилам, смысл которых не видели на цифрах. Пропишите, как делите общие траты: плоскость управления кластера, входящий трафик и мониторинг можно распределять по доле CPU пространств имён, по числу запросов или фиксированной плате с команды. Встройте проверки в конвейер CI и admission-контроллер, а не только в дашборд: пайплайн, который не пропустит развёртывание без requests и limits, полезнее графика, который никто не открывает. Смотрите не только на сумму по кластеру, а на удельную стоимость — запроса, транзакции, активного пользователя. Аномалии в расходах ставьте рядом с SLO по задержке и ошибкам в одной системе наблюдаемости. Раз в месяц собирайте инженерных лидов на полчаса: пять крупнейших отклонений, три кандидата на уменьшение ресурсов, одна архитектурная задача на следующий цикл. FinOps — это постоянная обратная связь между ценой, скоростью релизов и надёжностью, а не разовая кампания «срезать облако».
Лёгкие привычки учёта затрат без потери скорости разработки разобраны в гайде по контролю облачных расходов без замедления инженерии.
Тегирование на уровне облачного аккаунта и стратегия резервирования мощностей в AWS, GCP и Azure опираются на пошаговое руководство по оптимизации затрат в нескольких облаках.
