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-ами» работает именно так как задумывался.