Как сократить затраты на кластеры Kubernetes? Обзор OpenCost

Как сократить затраты на кластеры Kubernetes? Обзор OpenCost

Владислав Ефименко
Владислав Ефименко Главный редактор
21 февраля 2024

Рассказываем, как добавить щепотку FinOps в жизнь кластера и мониторить потребление ресурсов с помощью OpenCost.

Изображение записи

Что такое FinOps и зачем он в Kubernetes

Вне зависимости от того, кто вы: небольшой стартап или развитая компания — задача управления затратами особенно важна. Модное понятие FinOps, оно же Financial Operations или Cloud Financial Operations, помогает организациям наиболее эффективно и экономически выгодно использовать облачные ресурсы. Другими словами — привязать вычислительные мощности к деньгам и учитывать стоимость IT-инфраструктуры в бюджете проекта.

Одна из наиболее популярных сред развертывания Kubernetes — облако. Например, у нас в Selectel есть сервис управления кластерами Managed Kubernetes. Клиенты могут использовать отказоустойчивые и автомасштабируемые кластеры в облачной платформе. При этом — создавать их в фиксированной или произвольной конфигурации с разными типами групп нод, платить только за услугу и используемые ресурсы облака.

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

В этом выражена мотивация внедрения FinOps. Но то, насколько она сильна, — зависит от команды и зрелости процессов. Можно выделить две основные цели, которые ставят перед собой организации.

  • Прогнозирование и подсчет расхода текущих ресурсов. Это помогает понимать и контролировать затраты на облачные услуги.
  • Создание культуры принятия экономически обоснованных решений. Подход включает в себя понимание затрат и их влияния на бизнес-модель. На основании этой информации компании могут принимать более взвешенные решения.

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

Как применить FinOps? Знакомство с OpenCost

С излишними тратами на инфраструктуру часто сталкиваются при масштабировании кластеров Kubernetes. Наладить ситуацию вот уже с 2021 года пытаются разработчики kubecost. Они разрабатывают разные решения, в том числе OpenCost — «инспектор» для мониторинга контейнеров с открытым исходным кодом.

Дашборд потребления ресурсов.
Интерфейс OpenCost, дашборд потребления ресурсов.

Каждый желающий может сделать вклад в проект, например написать интеграцию со своим облачным провайдером. Сегодня OpenCost нативно работает с AWS, Azure и GCP, то есть может автоматически подгружать и рассчитывать стоимость ресурсов данных облаков. Однако можно также подключить таблицы с ценами для кластеров Kubernetes у собственного провайдера и использовать OpenCost в его «экосистеме». 

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


    {
    "provider": "custom",
    "description": "Default prices based on GCP us-central1",
    "CPU": "0.031611",
    "spotCPU": "0.006655",
    "RAM": "0.004237",
    "spotRAM": "0.000892",
    "GPU": "0.95",
    "storage": "0.00005479452",
    "zoneNetworkEgress": "0.01",
    "regionNetworkEgress": "0.01",
    "internetNetworkEgress": "0.12"
}

Набор параметров может отличаться для облака, в котором вы разворачиваете OpenCost. Например, в Managed Kubernetes от Selectel нет zoneNetworkEgress и regionNetworkEgress, а spotCPU и spotRAM пока не актуальны, наша команда разработки занимается вопросом их поддержки.

Впрочем, это не все нюансы, которые стоит учитывать для более точного расчета ресурсов. Разберемся на практике: посмотрим, как развернуть OpenCost на базе кластера Managed Kubernetes в облачной платформе Selectel.

Установка OpenCost в Managed Kubernetes

В целом, инструмент можно развернуть в любом кластере, начиная с Kubernetes 1.8. Сегодня в облаке Selectel доступны все актуальные версии. Выбрать можно любую — для примера развернем свежий Kubernetes 1.29: Mandala.

Создание кластера Managed Kubernetes

Кластер можно легко создать с помощью нашего Terraform-провайдера. Но мы покажем на примере панели управления.

Новость для тех, кто давно не пользовался Managed Kubernetes. Мы упростили создание кластеров. Разбили форму на три простых шага: выбор конфигурации кластера, добавление групп нод и настройка автоматизации.

1. Перейдем в панель управления, откроем раздел Облачная платформа → Kubernetes и настроим кластер.

Инструкция по установке OpenCost в Managed Kubernetes.
Панель управления. Первый этап создания кластера, настройка.

2. Следующим этапом настроим группы нод.

Инструкция по установке OpenCost в Managed Kubernetes.
Панель управления. Второй этап создания кластера, конфигурация ноды.
В параметрах OpenCost для каждого ресурса можно указать только один тип. Если вы используете один тип дисков или GPU, проблем не будет — можно указать стоимость, инспектор ее «съест» и примет в расчет. Но если вы используете два разных диска или GPU, OpenCost не сможет точно посчитать потребление. Это нужно учитывать при выборе конфигурации кластера.

3. Далее настроим процессы автоматизации: автовосстановление и автообновление — и нажмем кнопку Создать.

Инструкция по установке OpenCost в Managed Kubernetes.
Панель управления. Третий этап создания кластера, настройка автоматизации.

Готово — спустя пару минут кластер будет создан. Теперь к нему можно подключиться, установить Helm и OpenCost.

Установка и настройка OpenCost

1. Установим Prometheus — он нужен для сбора данных о потреблении ресурсов. Для этого инструмента нужно указать Storage Class или объявить значение по умолчанию.


    kubectl get sc

В выводе будет название существующего Storage Class. В нашем случае оно такое:


    NAME         PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
fast.ru-2c   cinder.csi.openstack.org   Delete          Immediate           true                   15m

Используйте данное имя, чтобы задать Storage Class по умолчанию.


    kubectl patch storageclass fast.ru-2c -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Установите Prometheus в репозиторий и загрузите его.


    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts &&\  
helm repo update


    helm install my-prometheus --repo https://prometheus-community.github.io/helm-charts prometheus \
  --namespace prometheus --create-namespace \
  --set prometheus-pushgateway.enabled=false \
  --set alertmanager.enabled=false \
  -f https://raw.githubusercontent.com/opencost/opencost/develop/kubernetes/prometheus/extraScrapeConfigs.yaml

Если имя my-prometheus, порт или namespace поменяются, не забудьте заменить значения в values.yaml, описанном в п.4.

2. Добавим репозиторий opencost-charts в Helm.


    helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart &&\
helm repo update

3*. Если планируете публиковать дашборды с потреблением ресурсов в интернет, создайте Ingress Controller и укажите необходимый хост файла values.yaml в п.4.


    ingress:
      enabled: true
      ingressClassName: ""
      annotations: {}
      hosts:
        - host: opencost.example.com
          paths:
            - /
      servicePort: http-ui
      tls: []

opencost.example.com — имя вашего домена со значением А-записи облачного балансировщика, который использует Ingress Controller.

4. Создадим файл values.yaml для OpenCost со следующим содержимым:


    networkPolicies:
  enabled: false
 
  prometheus:
    namespace: prometheus
    port: 9090
    labels:
      app.kubernetes.io/name: prometheus
 
updateStrategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
  type: RollingUpdate
 
service:
  enabled: true
  annotations: {}
  labels: {}
  type: ClusterIP
  extraPorts: []
 
rbac:
  enabled: true
 
opencost:
  cloudIntegrationSecret: ""
  exporter:
    apiPort: 9003
    cloudProviderApiKey: ""
    defaultClusterId: 'default-cluster'
    image:
      registry: quay.io
      repository: kubecost1/kubecost-cost-model
      tag: ""
      pullPolicy: IfNotPresent
      fullImageName: null
    extraArgs: []
    replicas: 1
    resources:
      requests:
        cpu: '10m'
        memory: '55Mi'
      limits:
        cpu: '999m'
        memory: '1Gi'
    livenessProbe:
      enabled: true
      path: /healthz
      initialDelaySeconds: 120
      periodSeconds: 10
      failureThreshold: 3
    readinessProbe:
      enabled: true
      path: /healthz
      initialDelaySeconds: 120
      periodSeconds: 10
      failureThreshold: 3
    securityContext: {}
    extraVolumeMounts: []
    env: []
    extraEnv: {}
  customPricing:
    enabled: false
    configmapName: custom-pricing-model
    configPath: /tmp/custom-config
    createConfigmap: true
    provider: custom
    costModel:
      description: Modified pricing configuration.
      CPU: 0.9
      spotCPU: 0
      RAM: 0.33
      spotRAM: 0
      GPU: 38.4
      storage: 0.05
      zoneNetworkEgress: 0
      regionNetworkEgress: 0
      internetNetworkEgress: 0.52
 
  dataRetention:
    dailyResolutionDays: 15
 
  cloudCost:
    enabled: true
    refreshRateHours: 6
    runWindowDays: 3
    monthToDateInterval: 6
    queryWindowDays: 7
 
  prometheus:
    internal:
      enabled: true
      serviceName: my-prometheus-server
      namespaceName: prometheus
      port: 80
 
  ui:
    enabled: true
    image:
      registry: quay.io
      repository: kubecost1/opencost-ui
      tag: ""
      pullPolicy: IfNotPresent
      fullImageName: null
    resources:
      requests:
        cpu: '10m'
        memory: '55Mi'
      limits:
        cpu: '999m'
        memory: '1Gi'
    uiPort: 9090
    livenessProbe:
      enabled: true
      path: /healthz
      initialDelaySeconds: 30
      periodSeconds: 10
      failureThreshold: 3
    readinessProbe:
      enabled: true
      path: /healthz
      initialDelaySeconds: 30
      periodSeconds: 10
      failureThreshold: 3
 
    ingress:
      enabled: true
      ingressClassName: "nginx"
      annotations: {}
      hosts:
        - host: opencost.example.com
          paths:
            - /
      servicePort: http-ui
      tls: []
 
  extraContainers: []
 
extraVolumes: []

Полный список возможных параметров доступен по ссылке.

5. Установим OpenCost через менеджер пакетов Helm с использованием конфигурации values.yaml.


    helm install opencost opencost-charts/opencost --namespace opencost --create-namespace -f values.yaml

6. Подождите, пока поды OpenCost перейдут в состояние Ready. Статус можно проверить с помощью команды:


    kubectl get pods -n opencost -w

7. Выполняем запрос к веб-странице по нашему домену — например, opencost.example.com — в браузере. На этом этапе вы должны увидеть интерфейс инспектора.

Примечания

1. Если необходимо обезопасить доступ к данным, в поле extraContainers конфигурации values.yaml можно добавить дополнительный контейнер, который будет проксировать запросы на сервер авторизации. Пример доступен по ссылке.

2. По умолчанию валюта выбирается в долларах (USD), но ее можно изменить в правом верхнем углу на RUB. Или использовать специальную ссылку, чтобы потребление сразу отображалось в рублях. Вот так:


    http://opencost.example.com/?currency=RUB

3. Стоимость ресурсов кластера задается в customPricing.costModel конфигурации values.yaml. В нашем примере указаны цены для быстрого SSD и Tesla T4. Стоимость ресурсов облачной платформы Selectel указана в рублях для пула ru-2. Значения также можно задать через ConfigMap с ценами ресурсов и указание его в values.yaml. О том, как это сделать, написано в документации.

OpenCost на практике: тестирование с нагрузкой

Попробуем проверить, как поведет себя OpenCost, если на кластер оказать дополнительную нагрузку. 

В только что созданном кластере «все спокойно». Можно посмотреть, какие процессы оказывают большую нагрузку и сколько она стоит в рублях.

Тестирование нагрузки OpenCost.
Дашборд OpenCost до оказания нагрузки.

Окажем небольшую нагрузку на тестовое веб-приложение в соседнем пространстве имен (web-test-app) и посмотрим, как OpenCost оценит эффективность загрузки ресурсов.

Получились следующие результаты по запущенным процессам и потреблению вычислительных ресурсов:

Тестирование нагрузки OpenCost.
Дашборд OpenCost после оказания нагрузки. Примечание: кластер был под непрерывной нагрузкой целые сутки.

Метрики можно частично проверить в выводе kubectl top pod -A. Видим, что пространство имен web-test-app действительно забирает большую часть по CPU в кластере:

Вывод команды kubectl top pod -A.
Вывод команды kubectl top pod -A.

На графике выше также видно, сколько процессы потребляют в денежном эквиваленте. О том, как все это считается, можно прочитать в официальной документации OpenCost.

Оптимизация потребления ресурсов

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

Подберите оптимальную конфигурацию

Обсудим на примере видеокарт. Для начальных задач необязательно брать самые топовые видеокарты. Та же GTX 1080 до сих пор актуальна в задачах инференса — наравне с T4 и A2000. CUDA-ядер меньше, но зато они все используются вашим кластером, а вы — не переплачиваете.

В тот же момент, если вы планируете одновременно запускать несколько инстансов на одной видеокарте, нужно выбирать GPU с возможностью шеринга на партиции. Тогда выбор ограничивается видеокартами, которые поддерживают технологии TimeSlicing или MIG.

Сократите потребление сети

Оцените, как много данных сети использует ваш кластер. Все ли необходимо загружать через сеть? Или лучше один раз их загрузить, кэшировать и сэкономить на трафике? 

Эти вопросы особенно актуальны, если вы работаете с большими объемами данных, например дата-сетами, и постоянно их скачиваете или загружаете в облако. На этом процессе можно не только сэкономить бюджет, но и время, настроив кэширование данных в кластере. В облаке Selectel предоставляются 3 ТБ внешнего трафика бесплатно, но если его больше, то такая аналитика может быть особенно полезна.

Освобождайте неиспользуемые ресурсы

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

С такой проблемой столкнулись и мы, когда настраивали тестирование микросервисов на базе OpenStack. О том, как нам удалось утилизировать остаточные ресурсы, рассказали в отдельной статье.

Что нужно учитывать

В нашем случае OpenCost хорошо подходит для оценки эффективности потребления ресурсов в кластере, но не более. Почему так? Давайте разбираться. 

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

  • OpenCost не учитывает стоимость балансировщика, Managed Kubernetes как услуги, дисков самих воркер-нод и публичных IP-адресов. Это связано с тем, что они являются сущностями облачной платформы, а не кластера, в котором развернут инспектор. Этот вопрос могла бы легко решить полная интеграция OpenCost с API облака Selectel.
  • OpenCost считает стоимость балансировщика через стоимость пройденного через него трафика, что неверно, так как она фиксирована. То есть, если взять проект облачной платформы, в котором нет ничего кроме кластера, все равно не получится посчитать полную сумму за потребление. Опять же, это решается интеграцией с нашим API.

Проще говоря, широкий выбор конфигураций в Managed Kubernetes — это как преимущество, так и некий «ограничивающий фактор» для OpenCost, когда он пытается посчитать потребление ресурсов.

Потребление ресурсов в OpenCost.
Панель управления. Разделы с информацией по потреблению облачной платформы.

Разумеется, все расходы можно посмотреть в панели управления, в том числе — потребление ресурсов нодами кластера. Однако инструменты вроде OpenCost отлично дополняют статистику и помогают анализировать расходы на уровне отдельных процессов, запущенных в кластере.