Prometheus 1.0: переводим production k8s-кластер на pull-мониторинг полностью
Вышел Prometheus 1.0 - переводим production Kubernetes-кластер на Prometheus+Alertmanager. cAdvisor на каждом хосте отдаёт метрики контейнеров без лишних движений.
Prometheus 1.0 вышел в июле 2016 - зрелый релиз системы мониторинга на основе метрик временных рядов с pull-моделью, ориентированной на контейнерные среды
В феврале мы писали про первый опыт с Prometheus 0.17 на Docker-хостах. Тогда в конце поста была оговорка: «молодой проект, версия 0.17, и это ощущается». С тех пор вышел Prometheus 1.0, и это не просто смена цифры в версии - команда объявила стабильным API, формат хранения данных и конфигурации. Это хороший повод перестать держать его только на нетребовательных стендах.
На одном из production-кластеров у нас на сопровождении как раз было всё готово к переходу: Kubernetes 1.2, три рабочих ноды, около 60 контейнеров, старый Zabbix поверх всего этого, который скрипел и требовал всё больше ручного внимания.
Почему именно сейчас
До 1.0 мы держали Prometheus на мониторинге Docker-хостов, но не трогали production. Причина простая - нестабильный API хранения. Несколько раз при обновлении версии данные приходилось стирать: формат TSDB менялся несовместимо, и старые метрики не читались. Для трендов за квартал это неприемлемо.
С 1.0 объявлена стабильность формата хранения между минорными версиями. Это значит можно планировать retention на месяц и не ждать сюрпризов при апгрейде.
Второй момент: Alertmanager к 1.0 тоже вырос. В 0.x конфигурация была немного хаотичной - маршруты, получатели и ингибиторы в одном файле без чёткой структуры. Сейчас это выглядит прилично и читается.
Архитектура стека
Что поставили на каждый хост и в кластер:
- node_exporter 0.12 на каждой ноде - CPU, RAM, диски, сеть на уровне ОС. Systemd-юнит, порт 9100.
- cAdvisor 0.23 на каждой ноде как контейнер - метрики по всем контейнерам на хосте, порт 8080.
- kube-state-metrics - отдельный под в кластере, экспортирует состояние объектов k8s: Deployment, ReplicaSet, Node, Pod. Это то, чего нет в cAdvisor: количество желаемых реплик против фактических, статусы подов, pending-поды.
- Prometheus 1.0 - central scraper, хранение метрик, правила алертов.
- Alertmanager 0.3 - маршрутизация алертов, дедупликация, отправка в Slack и email.
- Grafana 3.0 - дашборды.
cAdvisor - это отдельная тема. Контейнер поднимается одной командой, монтирует /sys и /var/lib/docker в read-only, и через несколько секунд на порту 8080 уже отдаёт метрики всех контейнеров на хосте - без регистрации, без агентов внутри контейнеров, без какой-либо настройки под конкретные образы. Для Kubernetes это особенно важно: поды перезапускаются, переезжают между нодами, появляются и исчезают. Prometheus просто опрашивает cAdvisor на каждой ноде и видит актуальную картину.
Конфигурация Prometheus
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'kubernetes-nodes'
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'cadvisor'
static_configs:
- targets:
- 'node1.internal:8080'
- 'node2.internal:8080'
- 'node3.internal:8080'
- job_name: 'node-exporter'
static_configs:
- targets:
- 'node1.internal:9100'
- 'node2.internal:9100'
- 'node3.internal:9100'
- job_name: 'kube-state-metrics'
static_configs:
- targets:
- 'kube-state-metrics.kube-system:8080'
Kubernetes Service Discovery в 1.0 поддерживается нормально - для role: node Prometheus сам находит ноды через k8s API. Мы его пока не используем для scrape нод, потому что kube API у клиента закрыт, и сертификаты настраивать с наскока не получилось. Временно оставили static_configs - работает, можно жить.
Alertmanager: что алертим
global:
slack_api_url: 'https://hooks.slack.com/services/...'
route:
receiver: 'slack-ops'
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 3h
routes:
- match:
severity: critical
receiver: 'pagerduty-prod'
receivers:
- name: 'slack-ops'
slack_configs:
- channel: '#monitoring'
text: '{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}'
- name: 'pagerduty-prod'
pagerduty_configs:
- service_key: '...'
Правила алертов живут в отдельном файле. Три базовых, которые работают уже с первого дня:
node_down - нода не отвечает больше 2 минут. Это up == 0 по job node-exporter.
container_oom - контейнер получил OOMKill. cAdvisor отдаёт container_oom_events_total, считаем через increase() за 5 минут.
deployment_replicas_mismatch - количество запущенных реплик не совпадает с желаемым больше 5 минут. Это из kube-state-metrics: kube_deployment_status_replicas_available против kube_deployment_spec_replicas.
Последний алерт - самый ценный. Zabbix нам такого не давал вообще: он видел хосты, но не понимал концепцию Deployment и желаемого состояния.
Что сразу нашлось
Через день после перевода прилетел алерт по deployment_replicas_mismatch на один из сервисов. Деплой застрял: новые поды не поднимались, старые продолжали работать. Без мониторинга это бы заметили через несколько часов - когда кто-то из разработчиков спросил бы почему его изменения не применились. С алертом - через 5 минут.
Причина оказалась банальная: образ в registry был с неверным тегом, pullPolicy: Always, поды не могли стартовать. Классика.
PromQL: входной порог
PromQL поначалу непривычен, но паттерны берутся за день. То, что используем чаще всего:
# CPU по контейнерам за 5 минут
rate(container_cpu_usage_seconds_total{image!=""}[5m])
# Память контейнеров относительно лимита
container_memory_usage_bytes / container_spec_memory_limit_bytes
# HTTP-запросы с кодом 5xx
rate(nginx_http_requests_total{status=~"5.."}[5m])
Если человек знаком с SQL и понимает временные ряды - он разберётся с PromQL за полдня. Главное не пытаться мыслить в терминах последнего значения: большинство запросов строятся вокруг rate() и increase() поверх счётчиков.
Что не решено
Долгосрочное хранение - та же проблема что и была. Prometheus хранит данные локально, retention 30 дней мы поставили, диск под это выделили. Для трендов на месяц этого хватает. Если понадобится год - это уже вопрос.
Автодискавери подов для кастомных метрик из приложений пока не настроили. Приложения могут экспортировать свои метрики на /metrics, но Prometheus должен знать куда ходить. Annotations на подах для этого есть (prometheus.io/scrape: "true"), но настройку через kubernetes_sd_configs мы отложили до следующего спринта.
По ощущениям 1.0 - это проект, которому можно доверять production. Не в том смысле что там нет шероховатостей, а в том что основные части ведут себя предсказуемо и ломаются предсказуемо.