Очень полезное дело – мониторить то, насколько эффективно используется кластер, особенно, если приложения деплоятся девелоперами, которые не сильно вникают в requests, и устанавливают завышенные значения “про запас”. Запас, конечно, нужен – но и просто так реквестить ресурсы идея плохая.
К примеру, у вас есть WorkerNode у которой 4 vCPU (4.000 milicpu) и 16 GB RAM, и создаёте Kubernetes Deployment, в котором для подов задаёте requests 2.5m и 4gb памяти. После запуска одного пода – он зареквестит более половины доступного процессорного времени, и для запуска второго под Kubernetes сообщит о нехватке ресурсов на доступных нодах, что приведёт к запуску ещё одной WorkerNode, что, разумеется, отразится на общей стомости кластера.
Что бы этого избежать есть несколько утилит, такие как Kubernetes Resource Report и Kubecost.
Содержание
Kube Resource Report
Kubernetes Resource Report – самая простая в запуске и возможностях: просто выводит ресурсы, группируя их по типу, и отображает статистику – сколько CPU/MEM requested, и сколько реально используется.
Мне она нравится именно простотой – просто запускаем, раз в пару недель смотрим что происходит в кластере, и при необходимости пингуем девелоперов с вопросом “А вам в самом деле надо 100500 гиг памяти для этого приложения?”
Есть Helm-чарт, но он обновляется редко, поэтому проще установить из манифестов.
Создём Namespace:
[simterm]
$ kk create ns kube-resource-report namespace/kube-resource-report created
[/simterm]
Загружаем репозиторий с kube-resource-report
:
[simterm]
$ git clone https://codeberg.org/hjacobs/kube-resource-report $ cd kube-resource-report/
[/simterm]
В каталоге deploy
уже есть файл Kustomize – добавим в него установку в наш неймспейс:
[simterm]
$ echo "namespace: kube-resource-report" >> deploy/kustomization.yaml
[/simterm]
Проверяем:
[simterm]
$ cat deploy/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - rbac.yaml - service.yaml - configmap.yaml namespace: kube-resource-report
[/simterm]
И устанавливаем:
[simterm]
$ kubectl apply -k deploy/ serviceaccount/kube-resource-report created clusterrole.rbac.authorization.k8s.io/kube-resource-report created clusterrolebinding.rbac.authorization.k8s.io/kube-resource-report created configmap/kube-resource-report created service/kube-resource-report created deployment.apps/kube-resource-report created
[/simterm]
Открываем себе доступ к Service:
[simterm]
$ kubectl -n kube-resource-report port-forward svc/kube-resource-report 8080:80
[/simterm]
И открываем репорт в браузере:
Далее переходим например в Namespaces, сортируем колонки по CR (CPU Requested):
При наведении курсора на ползунок Kube Resource Report подскажет оптимальное с его точки зрения значение.
Далее думаем сами, учитывая ваше приложение – действительно ли требуется только requested, или его можно уменьшить.
В данном случае у нас Apache Druid с 16 подами, в каждой крутится JVM, которая процессор и память любит, и процессора желательно выделять по 1 ядру на каждый поток выполнения Java, поэтому ОК – пусть будет 14.65 запрошенного процессора.
Kubecost
Kubecost – это Kube Resource Report на стероидах. Умеет считать трафик, слать алерты, генерирует метрики для Prometheus, имеет свои Grafana дашборды, может подключаться к нескольких кластерам Kubernetes, и многое другое.
Местами не без багов, но в целом штука приятная и мощная.
Правда, стоимость Business лицензии в 499 долларов немного завышена, как мне кажется. Впрочем, для базовых вещей вполне хватает Free версии.
“Под капотом” использует свой Prometheus для хранения данных. Можно отключить, и использовать внешний – но не рекомендуется.
Установка
Основные доступные опции описаны в документации на Github, плюс можно посмотреть дефолтные values его Helm-чарта.
Требует регистрации для получения ключа – заходим на https://www.kubecost.com/install.html, указываем почту – и сразу переадресует на инструкцию по установке с вашим ключём:
Helm chart values
Спешить с установкой не будем – сначала создадим свой values.
Если увас уже развернут Kube Prometheus Stack, есть Grafana и NodeExporter – то в Kubecost их имеет смысл отключить. Кроме того, отключим kube-state-metrics
, что бы не дублировались данные в мониторинге.
Что бы Prometheus из нашего собственного стека начал собирать метрики Kubecost – укажем на создание ServiceMonitor и добавим ему labels
– тогда можно будет генерить свои алерты и использовать Grafana dashboard.
А вот если отключить запуск встроенной Grafana – под с kubecost-cost-analyzer
не стартует. Не знаю – баг, или фича. Но в ней есть свои дашборды, которые могут быть полезны, так что можно оставить.
Ещё можно включить networkCosts
, но мне так и не удалось увидеть адекватных расходов на трафик – возможно, неправильно готовил.
networkCosts
может быть достаточно прожорливым по ресурсам – надо помониторить использование ЦПУ.
Собственно, сам values.yaml
:
kubecostToken: "c2V***f98" kubecostProductConfigs: clusterName: development-qa-data-services prometheus: kube-state-metrics: disabled: true nodeExporter: enabled: false serviceAccounts: nodeExporter: create: false serviceMonitor: enabled: true additionalLabels: release: prometheus networkCosts: enabled: true podMonitor: enabled: true config: services: amazon-web-services: true
Устанавливаем в Namespace kubecost
:
[simterm]
$ helm repo add kubecost https://kubecost.github.io/cost-analyzer/ $ helm upgrade --install -n kubecost --create-namespace -f values.yaml kubecost kubecost/cost-analyzer
[/simterm]
Открываем порт:
[simterm]
$ kubectl -n kubecost port-forward svc/kubecost-cost-analyzer 9090:9090
[/simterm]
Проверяем поды – завёлся ли kubecost-cost-analyzer
:
[simterm]
$ kubectl -n kubecost get pod NAME READY STATUS RESTARTS AGE kubecost-cost-analyzer-5f5b85bf59-f22ld 2/2 Running 0 59s kubecost-grafana-6bd995d6f9-kslh2 2/2 Running 0 63s kubecost-network-costs-22dps 1/1 Running 0 64s kubecost-network-costs-m7rf5 1/1 Running 0 64s kubecost-network-costs-tcdvn 1/1 Running 0 64s kubecost-network-costs-xzvsz 1/1 Running 0 64s kubecost-prometheus-server-ddb597d5c-dvrgc 2/2 Running 0 6m49s
[/simterm]
Открываем доступ к kubecost-cost-analyzer
Service:
[simterm]
$ kubectl -n kubecost port-forward svc/kubecost-cost-analyzer 9090:9090
[/simterm]
И переходим на http://localhost:9090.
Тут скрин с Kubecost, который уже почти неделю запущен на одном из наших кластеров:
Кратко пройдёмся по основным пунктам меню.
Assets
Для понимания расходов лучше начать с пункта Assets, где выводится стоимость “железа”:
Видим, что в день наш кластер стоит 43 доллара.
Можно провалиться глубже в детали кластера, и увидеть разбивку по ресурсам – WorkerNodes, лоад-балансерам, дискам и стоимости самого AWS Elastic Kubernetes Service:
Проходим ещё дальше, в Nodes:
И посмотреть детали стоимости по конкретной ноде:
Проверим:
0.167 в час, как Kubecost и реппортит в Hourly Rate.
Для настройки расходов на AWS Spot Instances – см. Spot Instance data feed и AWS Spot Instances.
Cost Allocation
Показывает куда в самом Кубере расходуются ресурсы:
Kubecost считает стоимость CPU и RAM на WorkerNodes, и соответственно выводит стоимость каждого неймспейса в зависимости от его requests и usage.
Тут наш Apache Druid имеет реквестов CPU на целых 48 долларов за неделю или 4.08 в день.
Проваливаемся глубже – и получаем разбивку по конкретным контроллера – StatefulSet, Deployment:
Колонки тут:
- CPU, RAM: стоимость используемых ресурсов в зависимости от стоимости ресурсов WorkerNode
- PV: стоимость PersistentVolume в выбранном контроллере, т.е. для StaefulSet MiddleManager у нас есть PV, который явлется AWS EBS, который стоит нам денег
- Network: надо проверять, как-то странно он считает, сильно мало
- LB: LoadBalancers по стоимости в AWS
- Shared: общие ресурсы, которые не будут считаться отдельно, например неймспейс
kube-system
, настраивается в http://localhost:9090/settings > Shared Cost - Efficiency: утилизация vs реквесты по формуле:
((CPU Usage / CPU Requested) * CPU Cost) + (RAM Usage / RAM Requested) * RAM Cost) / (RAM Cost + CPU Cost))
основной показатель эффективности ресурса, см. Efficiency and Idle
Если провалиться ещё глубже – будет ссылка на Grafana, где можно посмотреть использование ресурсов конкретным подом:
Правда, “из коробки” не отображаются метрики по RAM Requested.
Для проверки метрик можно зайти на локальный Pometheus:
[simterm]
$ kubectl -n kubecost port-forward svc/kubecost-prometheus-server 9091:80
[/simterm]
И да – kube_pod_container_resource_requests_memory_bytes
пустая:
Потому что метрика теперь называется kube_pod_container_resource_requests
с resource="memory"
, надо обновлять запрос в этой Графане:
avg(kube_pod_container_resource_requests{namespace=~"$namespace", pod="$pod", container!="POD", resource="memory"}) by (container)
__idle__
Расходы __idle__
– разница между стоимостью ресурсов выделенных под существующие объекты (поды, деплойменты) – их реквесты и реальный usage, и “простаивающего железа”, на котором они работают, т.е. не занятые ЦПУ/память, которые могут быть использованы под запуск новых ресурсов.
Savings
Тут собраны советы по оптимизации расходов:
К примеру, в “Right-size your container requests” собраны рекомендации по настройкам реквестов для ресурсов – аналог репортов в Kubernetes Resource Report:
Глянем тот же Apache Druid:
Тут явный овер-реквест по CPU, и Kubecost рекомендует уменьшить эти реквесты:
Но про Druid уже писалось выше – JVM, на каждый под MiddleManager мы запускаем один Supervisor с двумя Tasks, а под каждую Task желательно выделять по полному ядру. Поэтому оставляем, как есть.
Полезная штука “Delete unassigned resources” – у нас, например, нашлась пачка неиспользуемых EBS:
Health
Тоже полезная вещь, отображающая основные проблемы с кластером:
kubecost-network-costs
активно использует CPU, выше своих реквестов, и Kubernetes его тротлит.
Alerts
Тут можем настроить алерты, но у меня получилось настроить отправку только через Slack Webhook:
Документация – Alerts.
Prometehus Alertmanager можно настроить через values, но используется свой, локальный, который запускается вместе с Prometheus, а как ему настроить роуты – не нашёл.
Пример алерта, который можно настроить в Kubecost:
global: notifications: alertConfigs: alerts: - type: budget threshold: 1 window: 1d aggregation: namespace filter: druid alertmanager: enabled: true fqdn: http://prometheus-kube-prometheus-alertmanager.monitoring.svc
Тут добавляем алерт с типом budget
, в котором проверяем стоимость неймспейса druid
за последний 1 день, и алертим, если он становится дороже 1 доллара.
Обновляем сетап:
[simterm]
$ helm upgrade --install -n kubecost -f values.yaml kubecost kubecost/cost-analyzer
[/simterm]
Алерт появляется в списке:
Но на нажатие кнопки Test не реагирует, и в локальном Alertmanager алерт не появляется.
Slack webhook
Попробуем через Slack webhook, документация тут>>>.
Создаём Application:
Переходим в Webhooks:
Активируем и кликаем Add New Webhook:
Выибраем канал:
Добавляем URL в Kubecost, и тестируем:
Final values.yaml
В конце-концов для теста собрал такой values:
kubecostToken: "c2V***f98" kubecostProductConfigs: clusterName: development-qa-data-services global: notifications: alertConfigs: globalSlackWebhookUrl: https://hooks.slack.com/services/T03***c1f alerts: - type: assetBudget threshold: 30 window: 1d aggregation: type filter: 'Node' - type: assetBudget threshold: 4 window: 1d aggregation: type filter: 'LoadBalancer' - type: assetBudget threshold: 3 window: 1d aggregation: type filter: 'Disk' - type: assetBudget threshold: 40 window: 3d aggregation: cluster filter: 'development-qa-data-services' - type: spendChange relativeThreshold: 0.01 # change relative to baseline average cost. Must be greater than -1 (can be negative). window: 1d baselineWindow: 7d # previous window, offset by window aggregation: namespace filter: default, druid - type: spendChange relativeThreshold: 0.01 window: 1d baselineWindow: 7d aggregation: cluster filter: 'development-qa-data-services' - type: health # Alerts when health score changes by a threshold window: 10m threshold: 1 prometheus: kube-state-metrics: disabled: true nodeExporter: enabled: false serviceAccounts: nodeExporter: create: false #serviceMonitor: # enabled: true # additionalLabels: # release: prometheus networkCosts: enabled: true podMonitor: enabled: true config: destinations: direct-classification: - region: "us-west-2" zone: "us-west-2c" ips: - "10.0.64.0/19" - "10.0.160.0/20" - "10.0.208.0/21" - region: "us-west-2" zone: "us-west-2d" ips: - "10.0.216.0/21" - "10.0.96.0/19" - "10.0.176.0/20" services: amazon-web-services: true
Тут тестовые алерты, которые можно будет в принципе тянуть в продакшен.
ServiceMonitor для получения метрик во внешнем Prometheus отключил, ибо смысла пока не вижу – алертить будет через Slack Webhook своими алертами, а дашборда для Grafana во встроенной Графане лучше, и их там несколько.
Добавил direct-classification
для networkCosts
– посмотрим, возможно покажет более правильные данные по трафику.
#TODO
С чем пока не получилось разобраться:
- алерты через Alertmanager
- Kubecost не видит Node Exporter (проверять на странице http://localhost:9090/diagnostics), но это вроде ни на что не влияет – основные метрики получает от cAdvisor
- расходы на нетворкинг слишком маленькие
Не делал/не тестил:
- не настраивал Cost Usage Reports для AWS, см. AWS Cloud Integration
- не настраивал AWS Spot Instances прайсинг
- не добавлял Ingress, так как у нас AWS ALB Controller, и надо делать авторизацию, а SAML в Kubecost доступен только в Premium
В целом, на этом всё.
Система интересная и полезная, но есть баги и сложности, с которыми надо разбираться.