ADG Оставить заявку
Блог DevOps 5 мин чтения

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. Не в том смысле что там нет шероховатостей, а в том что основные части ведут себя предсказуемо и ломаются предсказуемо.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.