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

Grafana 5.1 provisioning: дашборды в Git, автозагрузка при деплое

Grafana 5.1 умеет загружать дашборды и datasources из файлов при старте - решена проблема потери дашбордов при пересоздании контейнера.

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

Grafana 5.1 - provisioning дашбордов и datasources через конфигурационные файлы при старте контейнера

Несколько недель назад мы писали про Prometheus Operator и monitoring-as-code в Kubernetes. Там оставалась одна незакрытая деталь: Prometheus складно управляет своими конфигами через CRD, а Grafana по-прежнему хранила дашборды в SQLite внутри контейнера. Grafana 5.1 закрывает этот пробел - теперь есть официальный provisioning API.

Проблема, которую мы хотели решить

С Grafana в Docker у нас был один стойкий раздражитель. Кто-нибудь из команды строит дашборд вручную через UI - красиво, с панелями, thresholds, annotations. Потом в рамках планового обновления или какого-нибудь инцидента контейнер пересоздаётся. Если не был смонтирован persistent volume под /var/lib/grafana - дашборд исчез. Был смонтирован - надо помнить об этом явно и не потерять volume при переезде между нодами.

В Kubernetes это болит особенно заметно: StatefulSet для Grafana ради одного SQLite с дашбордами выглядит как избыточная конструкция. Мы какое-то время экспортировали дашборды вручную через «Export JSON» и коммитили в git - но это ручной процесс, про него забывали, и репозиторий регулярно отставал от реальности.

Provisioning в Grafana 5.1 меняет механику: дашборды и datasources описываются файлами, которые Grafana читает при старте. Файлы живут в git. Контейнер stateless.

Как устроен provisioning

Grafana ищет конфигурационные файлы в директориях, которые указаны в основном grafana.ini:

[paths]
provisioning = /etc/grafana/provisioning

В этой директории два поддиректория: datasources/ и dashboards/. Каждый содержит YAML-файлы с конфигурацией.

Datasource выглядит так:

apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false

Поле editable: false - важное. Если его не выставить, кто-нибудь поправит datasource через UI, а при следующем рестарте provisioning перезапишет изменения файлом. Явный запрет снимает эту неоднозначность.

Для дашбордов нужен двухуровневый конфиг. Сначала dashboards/default.yaml - описывает откуда брать JSON-файлы дашбордов:

apiVersion: 1
providers:
  - name: default
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    options:
      path: /etc/grafana/dashboards

Потом в /etc/grafana/dashboards/ лежат сами JSON-файлы дашбордов. Их можно экспортировать из Grafana через UI один раз, или написать с нуля - формат документированный.

Как мы это раскатали

На managed-кластерах у нас Grafana работает в Kubernetes как Deployment с одной репликой. Добавили ConfigMap с provisioning-конфигами и volume с JSON-дашбордами, смонтировали в нужные пути. Dockerfile при этом не меняется - Grafana читает всё из примонтированных файлов.

Структура в git-репозитории получилась такая:

monitoring/
  grafana/
    provisioning/
      datasources/
        prometheus.yaml
      dashboards/
        default.yaml
    dashboards/
      kubernetes-nodes.json
      kubernetes-pods.json
      postgresql.json

Всё это упаковывается в два ConfigMap: один под provisioning-конфиги, второй под JSON-файлы дашбордов. При деплое - kubectl apply, Grafana поднимается с актуальными дашбордами автоматически.

Что поймали в процессе

Первое - uid в JSON-файлах дашборда. Grafana 5.x добавила UID для дашбордов - уникальный идентификатор, не зависящий от числового id. Если экспортировать дашборд через UI и сохранить JSON с UID, при следующем импорте через provisioning Grafana опознает его как тот же дашборд и обновит, а не создаст дубликат. Без UID при каждом рестарте Grafana создавала новую копию - в итоге накапливались дубли. Лечится добавлением "uid": "some-stable-string" в JSON вручную или через экспорт в версии 5.x.

Второе - disableDeletion: false с осторожностью. С таким флагом Grafana при рестарте удаляет дашборды, которых нет в файлах, но которые кто-то создал через UI. Для нас это желаемое поведение - git является source of truth. Но важно объяснить команде: если создал дашборд в UI и не закоммитил JSON - при следующем деплое он исчезнет.

Третье - переменные в datasource через env. Иногда URL Prometheus отличается между окружениями. Жёстко кодить его в YAML неудобно. Grafana 5.1 поддерживает подстановку переменных окружения в provisioning-файлах через синтаксис ${VAR_NAME}. Для нас это удобно: один и тот же YAML-файл, разные значения URL через переменные окружения в Kubernetes Deployment.

Что пока не закрыто

Provisioning работает на старте контейнера - это одновременно и достоинство, и ограничение. Если нужно добавить новый дашборд без рестарта Grafana, механика не предусмотрена. Grafana следит за директорией с файлами и подхватывает изменения без рестарта только для дашбордов (не для datasources) - но это работает когда файловая система меняется «живьём», что в случае ConfigMap в Kubernetes немного отличается от простого обновления файла на диске. Мы это место пока не проверяли на production и не делаем на него ставку.

Отдельный вопрос - alerting-правила Grafana. Алерты, созданные через UI, никак не участвуют в provisioning-механике 5.1. Они по-прежнему живут в SQLite. Для нашего стека critical alerting идёт через Prometheus Alertmanager, поэтому мы не завязаны на Grafana-алерты - но если у кого-то они настроены, это нужно учитывать.

Итого

Переход занял один рабочий день - большую часть времени съело приведение дашбордов в порядок: добавление UID, удаление дублей, чистка устаревших панелей которые накопились за время ручного управления. Теперь Grafana stateless, дашборды в git, новый контейнер поднимается с нужным состоянием без ручного вмешательства. Именно то, чего хотелось.

Контакт

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

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