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

Prometheus Operator в prod: ServiceMonitor CRD и monitoring-as-code на практике

Разворачиваем Prometheus Operator в production-кластере: ServiceMonitor CRD автоматически подхватывает новые деплойменты, конфиг Prometheus больше не правится вручную.

Контекст момента

Prometheus Operator - управление Prometheus через Kubernetes CRD (ServiceMonitor, PrometheusRule) в production-кластере

В марте мы разбирали концепцию Kubernetes Operators и отметили, что Prometheus Operator выглядит как кандидат на реальное использование - в отличие от Etcd Operator, который пока осел в тестовом стенде. Спустя два месяца мы наконец раскатали его на managed-кластер под реальную нагрузку. Рассказываем что получилось.

Предыстория: почему вообще понадобился оператор

Проблема, которую мы хотели решить, была банальной и надоедливой. Prometheus 2.0 у нас работал стабильно, но каждый раз когда команда хотела добавить новый сервис в мониторинг - открывался тикет на нас. Мы правили prometheus.yml, делали curl -X POST http://prometheus:9090/-/reload, закрывали тикет. Раз в неделю - нормально. Когда команды начали активнее деплоить новые микросервисы, тикеты стали идти пачками.

Prometheus Operator решает это через ServiceMonitor - Custom Resource Definition, который описывает что мониторить. Каждая команда создаёт ServiceMonitor в своём namespace сама, оператор подхватывает изменения и перегенерирует конфиг Prometheus. Мы из цепочки выпадаем.

Что ставили и как

Устанавливали через Helm chart от CoreOS - coreos/prometheus-operator. На момент установки версия оператора 0.20.0, совместима с Kubernetes 1.10, на котором у нас стоят prod-кластеры.

После установки в кластере появляются три CRD:

  • Prometheus - сам экземпляр Prometheus: storage, retention, resources, какие ServiceMonitor-ы учитывать.
  • ServiceMonitor - описывает targets: через label selector цепляется к Kubernetes Service, указывает порт и path для scrape.
  • PrometheusRule - набор alerting-правил в формате который Prometheus понимает нативно.

Объект Prometheus мы создаём один - он под контролем инфраструктурной команды. ServiceMonitor-ы создают команды разработки в своих namespace-ах.

Манифест ServiceMonitor выглядит примерно так:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-api
  namespace: my-team
  labels:
    team: my-team
spec:
  selector:
    matchLabels:
      app: my-api
  endpoints:
  - port: metrics
    interval: 30s

Prometheus Operator смотрит на все ServiceMonitor-ы у которых label совпадает с селектором в объекте Prometheus, формирует из них конфиг scrape-конфигураций и монтирует его через ConfigMap. Reload происходит автоматически через -/reload endpoint. Команда деплоит новый сервис, добавляет ServiceMonitor - метрики в Prometheus появляются без нашего участия.

Что реально произошло при раскатке

Не обошлось без сюрпризов.

Первое - label-селекторы требуют аккуратности. Оператор по умолчанию собирает ServiceMonitor-ы из всех namespace-ов, у которых совпадают labels. Если команда создала ServiceMonitor без нужных labels - он просто молча не подхватится. Несколько раз получали вопрос «почему метрики не появились» - причина была именно в labels. Написали небольшую документацию для команд с шаблоном ServiceMonitor и обязательными полями.

Второе - port должен быть именованным. ServiceMonitor ссылается на порт по имени (port: metrics), а не по номеру. Если в Service порт не именован - оператор не знает куда идти. У нескольких старых деплойментов порты были без имён, пришлось патчить Service-манифесты. Мелочь, но неочевидная.

Третье - reload происходит не мгновенно. После изменения ServiceMonitor оператор генерирует новый конфиг и перезапускает Prometheus через reload endpoint. Это занимает несколько секунд, иногда до полуминуты на нагруженном кластере. Для нас некритично, но стоит понимать: если деплой нового сервиса и добавление ServiceMonitor идут одновременно, первые scrape-циклы Prometheus может застать сервис ещё не готовым.

Четвёртое - PrometheusRule-ы удобнее чем кажется. Мы изначально думали оставить alerting-правила в виде отдельного ConfigMap как раньше. Но попробовав PrometheusRule - пересмотрели решение. Правила теперь версионируются в том же git-репозитории что и Kubernetes-манифесты сервиса. kubectl apply раскатывает и правила вместе с кодом. Это и есть monitoring-as-code в буквальном смысле.

Где сейчас болит

Оператор работает третью неделю на production-кластере, и в целом стабильно. Но есть момент который мы пока не решили чисто.

Мультиарендность. У нас несколько клиентов на одном кластере, у каждого свой namespace. Сейчас Prometheus один на всех - так проще с ресурсами. Но тогда ServiceMonitor одной команды в теории может случайно зацепить сервисы другой, если labels совпадут. Мы пока решаем это соглашением по именованию, но это не архитектурная защита. Правильное решение - отдельный Prometheus per-tenant, но это другой разговор про ресурсы и costs.

Итого

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

Паттерн «инфраструктура управляет Prometheus, разработчики управляют ServiceMonitor-ами» работает именно так как задумывался.

Контакт

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

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