Стоимость и эффективность кластера: FinOps

Рейтинг: 65.7% · 17 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

Стоимость и эффективность кластера: FinOps

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps (вы здесь)
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
Откуда в Kubernetes берётся стоимость, которую никто не заказывал

Картина знакомая до боли. Команда разработки катит сервисы в кластер, всё работает, и в конце месяца прилетает счёт от облака на сумму, от которой у финдиректора дёргается глаз. При этом графики загрузки CPU стабильно показывают 12 процентов. То есть ты платишь за 8 ядер, а реально работаешь одним. Куда уходят деньги - вопрос, на который без инструментов учёта никто в команде ответить не может.

Это и есть тема урока: kubernetes стоимость и дисциплина под названием finops kubernetes. FinOps - это не "урезать всё до боли", а вернуть видимость: кто, за что и сколько платит, и где деньги горят впустую без всякой пользы для прода. Kubernetes по своей природе склонен прятать расходы. Ты просишь у планировщика ресурсы через requests, он находит узел, где они влезают, и резервирует их под тебя - неважно, используешь ты их или нет. Запросил 2 ядра, крутишь на 100 милликорах - полтора с лишним ядра выведены из оборота и оплачены. Умножь на сотню подов, и вот тебе три лишних узла, которые существуют только чтобы держать воздух.

Разберём по полочкам, откуда натекает счёт.
  • Простаивающие узлы. Кластер масштабировали под пиковую нагрузку Чёрной пятницы, пятница прошла, узлы остались. Никто не выключил.
  • Overcommit резерва без лимитов. requests завышены "с потолка", планировщик считает узел занятым, хотя на нём гуляет ветер. Низкий packing density - главный пожиратель бюджета.
  • Лишние реплики. Поставили replicas: 6 "на всякий случай", хватило бы трёх. Каждая реплика тащит за собой свою долю requests.
  • Дорогая обвязка. Отдельный облачный LoadBalancer на каждый сервис вместо общего Ingress/Gateway. Premium SSD-диски там, где хватило бы обычных. Снапшоты, которые никто не чистит. Egress-трафик между зонами.
Дальше - механика, как всё это вычислить и прижать, не сломав прод.

Изображение

Requests и лимиты: фундамент, на котором стоит весь биллинг

Чтобы понять FinOps в Kubernetes, надо твёрдо держать в голове разницу между requests и limits, потому что именно requests определяют, сколько ты платишь.

requests - это бронь. Планировщик (kube-scheduler) при размещении пода смотрит только на requests: суммирует requests всех подов на узле и сравнивает с allocatable-ёмкостью узла. Если новый под со своим request не влезает - узел для него закрыт, нужен новый узел. То есть requests напрямую определяют bin-packing - насколько плотно поды упакованы по узлам. Завысил requests - упаковка рыхлая - узлов нужно больше - счёт растёт. requests это и есть деньги.

limits - это потолок потребления. CPU сверх лимита троттлится (под не убивают, просто притормаживают через CFS-квоту ядра), память сверх лимита приводит к OOMKill. limits на bin-packing и на счёт напрямую не влияют - они про защиту соседей по узлу.

Важный нюанс 2026 года: классический Pod Security и устройство QoS никуда не делись. Класс QoS пода выводится из соотношения requests и limits:
  • Guaranteed - requests равны limits для всех контейнеров. Убивают последним при нехватке памяти.
  • Burstable - requests заданы, но меньше limits (или limits не заданы). Золотая середина для большинства.
  • BestEffort - ни requests, ни limits. Прибьют первым. Для прода - яд.
Для CPU современная практика - задавать request, но не задавать limit на латентно-чувствительных сервисах: лимит CPU только режет производительность троттлингом, не давая взамен ничего полезного, ведь CPU - сжимаемый ресурс. Для памяти лимит наоборот нужен почти всегда - память несжимаема, и под без лимита может сожрать узел целиком и утащить за собой соседей.

Right-sizing kubernetes: ставим requests по факту, а не на глаз

Главная ошибка, из-за которой деньги текут рекой, - requests "от балды". Разработчик не знает реального аппетита сервиса, пишет 1000m CPU и 1Gi памяти "чтобы наверняка", а сервис ест 80m и 200Mi. Это не запас прочности, это сожжённый бюджет.

right-sizing kubernetes - это подгонка requests под реальное потребление с разумным буфером. Гадать не надо, надо измерить. Инструмент для этого - VPA (Vertical Pod Autoscaler) в режиме рекомендаций. Подчёркиваю: именно рекомендаций (Off / recommendation-only), не авто-применения. Авто-VPA пересоздаёт поды при смене requests и конфликтует с HPA по тем же метрикам - в проде это грабли. А вот как советчик VPA незаменим.

VPA крутится, собирает гистограммы реального потребления (нужно хотя бы 7-14 дней, чтобы поймать суточные и недельные циклы) и выдаёт рекомендации в полях target / lowerBound / upperBound.

Код: Выделить всё

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api-vpa
  namespace: shop
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  updatePolicy:
    updateMode: "Off"   # только рекомендации, поды не трогаем
Через пару недель смотрим, что насоветовал VPA:

Код: Выделить всё

$ kubectl describe vpa api-vpa -n shop
...
  Recommendation:
    Container Recommendations:
      Container Name:  api
      Lower Bound:
        Cpu:     76m
        Memory:  180Mi
      Target:
        Cpu:     110m
        Memory:  256Mi
      Upper Bound:
        Cpu:     480m
        Memory:  610Mi
Разбираем поля, это важно:
  • Target - то, что VPA поставил бы прямо сейчас. Именно сюда целимся при правке requests. Видим 110m против заявленной 1000m - почти десятикратный перезаказ.
  • Lower Bound - ниже этого начнётся нехватка ресурсов. Граница, под которую опускаться нельзя.
  • Upper Bound - оценка пика. Полезна для прикидки limits.
Практический рецепт: ставь request около Target плюс 15-25 процентов буфера на всплески и неточность модели. Memory limit разумно держать в районе Upper Bound. Не урезай requests в ноль по нижней границе - это не запас прочности, это лотерея.

Spot узлы kubernetes: платим в разы меньше за то же железо

spot узлы kubernetes (они же preemptible в Google Cloud, прерываемые ВМ в Yandex Cloud) - это невостребованные мощности облака, которые отдают со скидкой 60-90 процентов. Цена та же машина, дешевле в три-пять раз. Условие одно: облако вправе забрать узел в любой момент, обычно предупредив за 30-120 секунд через сигнал вытеснения.

Звучит страшно, но для stateless-нагрузки это почти бесплатные деньги. Реплика API-сервера за спиной балансировщика умерла на спот-узле - её перепланируют на другой узел, пользователь в худшем случае поймает один retry. Что хорошо живёт на споте: stateless web/API, воркеры очередей, CI-раннеры, батч-джобы, dev/stage целиком. Что туда нельзя: базы данных, stateful-системы с кворумом без серьёзной обвязки, синглтоны без реплик.

Чтобы спот не превратился в источник инцидентов, нужны три вещи.

1. Таргетинг через nodeSelector / tolerations. Спот-узлы обычно несут taint и лейбл капасити-типа. Pod должен явно соглашаться туда ехать:

Код: Выделить всё

spec:
  nodeSelector:
    karpenter.sh/capacity-type: spot
  tolerations:
    - key: "spot"
      operator: "Equal"
      value: "true"
      effect: "NoSchedule"
2. PodDisruptionBudget. Без PDB одновременное вытеснение нескольких спот-узлов может прибить все реплики разом. PDB говорит кластеру: "держи минимум столько живых":

Код: Выделить всё

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
  namespace: shop
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api
3. Диверсификация и анти-аффинити. Размазывай реплики по разным типам инстансов и зонам через topologySpreadConstraints, чтобы вытеснение одного пула спота не унесло сразу весь сервис. Для одновременной работы spot-to-spot консолидации (об этом ниже) Karpenter рекомендует разрешать не менее 15 типов инстансов - чем шире выбор, тем реже облако отбирает узлы и тем стабильнее живёт нагрузка.

Автоскейлинг узлов: Cluster Autoscaler и Karpenter, scale-to-zero

Right-sizing подогнал requests, спот удешевил железо. Но узлы всё ещё надо добавлять под рост нагрузки и убирать, когда нагрузка спала, - иначе ночью платишь за дневной пик. Этим занимается автоскейлер узлов, и тут два игрока.

Cluster Autoscaler - классика. Работает с заранее заданными node group / managed node group облака. Логика простая: есть Pending-поды, которым не хватило места, - добавь узел в подходящую группу; узел недозагружен дольше таймаута и его поды переедут - убери узел. Минус - он оперирует фиксированными группами одинаковых машин, поэтому упаковка не оптимальна: под просит чуть больше - поднимается целая большая машина.

Karpenter - новое поколение, в 2026-м фактически стандарт на EKS, и подход принципиально другой. Karpenter не привязан к node group. Он смотрит на конкретные Pending-поды, их requests, аффинити и таинты - и подбирает оптимальный по цене тип инстанса прямо под этот набор подов. Конфигурируется через ресурс NodePool (актуальная версия API - karpenter.sh/v1):

Код: Выделить всё

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64", "arm64"]
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
Ключевая фишка - consolidation (консолидация). Karpenter постоянно ищет, как переупаковать поды на меньшее число узлов или заменить дорогой узел дешёвым. Освободился узел - Karpenter сносит его за минуту. Поды разместятся плотнее на оставшихся - он мигрирует их и убьёт лишний узел. Это и есть автоматический bin-packing на уровне узлов, который Cluster Autoscaler делать толком не умеет. Плюс Karpenter из коробки прекрасно работает со спотом и сам подмешивает on-demand как фолбэк.

Scale-to-zero - святой грааль экономии. Если у пула нагрузки нет ни одного пода (например, ночью все dev-деплойменты сжаты до replicas: 0), пул узлов схлопывается до нуля. Платишь строго за то, что работает. Для RU-реалий: в Yandex Managed Kubernetes автомасштабирование групп узлов с нижней границей 0 живёт давно, Deckhouse несёт свой механизм управления узлами, k3s на своём железе экономит иначе - там FinOps это про отказ от облачного оверхеда вообще.

ResourceQuota и учёт: чтобы команды не дрались за бюджет вслепую

Технику починили, теперь нужна организационная дисциплина, иначе одна команда снова закажет 64 ядра "про запас". Инструмент - ResourceQuota на namespace. Это жёсткий лимит на сумму requests/limits в пространстве имён: пробьёшь потолок - новые поды просто не создадутся, получишь ошибку при apply.

Код: Выделить всё

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-payments-quota
  namespace: payments
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    persistentvolumeclaims: "10"
В пару к квоте идёт LimitRange - он задаёт дефолтные requests/limits для подов, которые забыли их указать, и запрещает заказывать абсурдно много на один контейнер. Квота защищает кластер от команды, LimitRange защищает команду от собственной забывчивости.

Но квоты режут будущее, а видеть надо прошлое: куда уже ушли деньги. Тут на сцену выходят инструменты учёта. OpenCost - это CNCF-проект, открытый стандарт распределения затрат: он берёт прайс облака, считает стоимость каждого узла и аллоцирует её на поды, деплойменты и namespace пропорционально их потреблению. kubecost - надстройка над той же моделью с удобным UI, отчётами и алертами (часть функций платная, ядро открытое). Оба отвечают на вопрос "сколько стоит namespace payments в день" и "какой деплоймент жжёт больше всех при нулевой загрузке".

Чтобы отчёты были осмысленными, нужна гигиена меток. Введи обязательные лейблы и добивайся их через политику (Kyverno умеет требовать наличие лейбла на любом workload):

Код: Выделить всё

metadata:
  labels:
    owner: "team-payments"
    cost-center: "fin-2026"
    env: "prod"
Без меток owner/team отчёт о расходах - это безликая куча цифр, по которой нельзя предъявить счёт ни одной команде. С метками FinOps превращается из гадания в управляемый процесс.

Грабли и антипаттерны из практики
  • requests с потолка "чтобы наверняка". Самая дорогая привычка. Лечится только VPA-рекомендациями и культурой "request = факт плюс буфер".
  • Лимиты CPU на латентном сервисе. Сервис мистически тормозит при низкой нагрузке - это троттлинг по CFS-квоте. Часто лучше снять CPU limit совсем, оставив request.
  • Забытые ресурсы. Висящие LoadBalancer от снесённых сервисов, осиротевшие PVC и Released-тома, старые снапшоты, dev-кластер, который "временно" подняли полгода назад. Регулярный аудит kubectl get pvc,svc --all-namespaces и чистка дисков - прямые деньги.
  • Спот без PDB и диверсификации. Облако забрало пул - сервис лёг. Спот это инструмент для тех, кто положил PDB и размазал реплики, а не просто включил галочку "дешевле".
  • Dev работает 24/7. Разработчики спят 8 часов и не работают в выходные, а dev-кластер крутится. Простой CronJob, гасящий dev по ночам (kubectl scale --replicas=0 ... плюс scale-to-zero узлов), режет счёт за непрод почти вдвое.
  • HPA + auto-VPA на одних метриках. Два автоскейлера дёргают requests и реплики по одной и той же метрике и входят в резонанс. VPA на проде - только в режиме рекомендаций.
Мини-лаба: найди и потуши перезаказ

Повтори руками на тестовом namespace:
  • Подними простой Deployment с заведомо завышенными requests (например, cpu: 1, memory: 1Gi) и нагрузи его слегка (любой ab/hey на 5-10 RPS).
  • Поставь VPA в updateMode: "Off" на этот Deployment. Подожди хотя бы 20-30 минут (для боевой оценки - дни, но для лабы хватит увидеть тренд).
  • Выполни kubectl describe vpa и сравни Target с тем, что заказано. Посчитай, во сколько раз перезаказал.
  • Поправь requests по формуле Target плюс 20 процентов, примени, убедись что под жив и не OOM.
  • Навесь ResourceQuota на namespace и попробуй заказать больше потолка - поймай ошибку отказа в создании пода.
  • Бонус: посчитай на калькуляторе облака, сколько в месяц стоит разница между старыми и новыми requests, если таких подов 50.
Контрольные вопросы
  • Почему на счёт за кластер влияют именно requests, а не limits? Что планировщик использует при bin-packing?
  • В каком режиме запускают VPA для right-sizing на проде и почему нельзя авто-применение вместе с HPA?
  • Какие три обязательных условия нужны, чтобы безопасно вынести stateless-нагрузку на spot узлы?
  • Чем подход Karpenter к выбору узлов принципиально отличается от Cluster Autoscaler и что такое consolidation?
Итог

FinOps в Kubernetes - это не разовая чистка, а петля: измерь (OpenCost/Kubecost плюс метки owner/team), подгони requests под факт (VPA-рекомендации), удешеви железо (spot для stateless с PDB), масштабируй узлы под реальную нагрузку с консолидацией и scale-to-zero (Karpenter), огради бюджет команд квотами и погаси непрод по ночам. Каждый шаг проверяемый и обратимый. Деньги в кластере не исчезают магически - они утекают в зарезервированный, но не используемый ресурс. Верни видимость - и счёт перестанет быть сюрпризом.
👍5 ❤️2 🔥2 😄 🤔1
Аватара пользователя
tor_chan
Сообщения: 1
Зарегистрирован: 23 май 2026, 22:25

Re: Стоимость и эффективность кластера: FinOps

Сообщение tor_chan »

Поставил VPA в Off на наш платёжный сервис, через неделю target по памяти оказался в 6 раз меньше заказанного. Шесть! Сижу и думаю сколько мы переплатили за полгода. Спасибо, прям открыло глаза.
👍1 ❤️ 🔥 😄 🤔2
Аватара пользователя
mbradtke
Сообщения: 1
Зарегистрирован: 08 июн 2026, 01:26

Re: Стоимость и эффективность кластера: FinOps

Сообщение mbradtke »

А не страшно выносить воркеры очереди на спот? У нас задачи долгие, по 10-15 минут. Если узел заберут на середине - задача потеряется или переедет? Или тут как раз PDB не спасёт и нужен идемпотентный ретрай на стороне самой очереди?
👍 ❤️ 🔥1 😄 🤔1
Ответить
← Предыдущая глава
Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
Следующая глава →
CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость