July 15, 2023

Grafana-operator, oncall и превозмогание

Привет. Давно ничего не писал.

В этой заметке - несколько часов боли. Пострадал за вас, чтоб вам не пришлось!

Где боль еще не начинается

Grafana-operator - это оператор для кубера, который добавляет кастомные ресурсы и позволяет хранить "мух отдельно от котлет". Тут подробно расписывать не буду, документация в помощь.

В общем, мне понравилась идея конфигурировать графану не огромной портянкой values для хельмчарта, а отдельными файликами типа

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: prometheus
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  datasource:
    name: prom1
    type: prometheus
    access: proxy
    url: http://prometheus-service:9090
    isDefault: true
    jsonData:
      'tlsSkipVerify': true
      'timeInterval': "5s"
    editable: true

Круто? Круто.

Но, как в том самом анекдоте, есть нюанс... Для ленивых - листайте до tl;dr.

Gitlab Auth. Где боль еще не начинается, но уже вот-вот почти.

Окей, давайте посмотрим на манифест для графаны.

apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: grafana
  labels:
    dashboards: "grafana"
spec:
  config:
    log:
      mode: "console"
    auth:
      disable_login_form: "false"
    security:
      admin_user: root
      admin_password: secret


Окей, все понятно, в spec.config мы задаем конфиг в ямл-формате. Существующая инсталяция у меня скофигурирована в ini (да, вот такой я бракованный девопс, получается), перегоним.

...
# Authentication
[auth]
disable_login_form = False
oauth_auto_login = False
disable_signout_menu = False
[auth.gitlab]
enabled = True
allow_sign_up = True
scopes = api
...

...
  config:
    auth:
      gitlab:
        enabled: "true"
        allow_sign_up: "true"
        scopes: "api"
...

И вот, брюки легким движением руки превращаются... Превращаются...
В невалидную хуйню.

The Grafana "grafana" is invalid: spec.config.auth.gitlab: Invalid value: "object": spec.config.auth.gitlab in body must be of type string: "object"

Окей-окей, это я дебил.
В доке есть такой пример:

...
  config:
    auth:
    ...
    auth.basic:
      enabled: "True"
...

Переписываем, применяем, работает:

...
  config:
    auth:
    ...
    auth.gitlab:
      enabled: "true"
      allow_sign_up: "true"
      scopes: "api"
...

Плагины боли

Оператор умеет в установку плагинов. Но тут вылазит нюанс: они объявляются только в датасорсах

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: example-grafanadatasource
spec:
  datasource:
    access: proxy
    type: prometheus
    jsonData:
      timeInterval: 5s
      tlsSkipVerify: true
    name: Prometheus
    url: http://prometheus-service:9090
  instanceSelector:
    matchLabels:
      dashboards: grafana
  plugins:
    - name: grafana-clock-panel
      version: 1.3.0

или в дашбордах

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
  name: keycloak-dashboard
spec:
  instanceSelector:
    matchLabels:
      dashboards: grafana
  plugins:
    - name: grafana-piechart-panel
      version: 1.3.9
  json: >
    {
    ...
    }

У самой графаны есть 3 типа плагинов: панели, датасорцы и приложения (applications).
И если с панелями и датасорцами все это вполне валидно, то с applications все не так очевидно.

Чтоб не тянуть кота за яйца, сразу скажу - мне нужен был плагин grafana-oncall.

Ну окей, давайте впилим его, хз, в манифест нашего датасорца alertmanager:

---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: alertmanager 
spec:
  instanceSelector:
    matchLabels:
      dashboards: grafana
  datasource:
    name: Alertmanager
    access: proxy
    type: alertmanager
    url: http://prometheus-alertmanager.monitoring.svc.cluster.local:9093
    jsonData:
      # Valid options for implementation include mimir, cortex and prometheus
      implementation: prometheus
      # Whether or not Grafana should send alert instances to this Alertmanager
      handleGrafanaManagedAlerts: false
    basicAuth: false
    editable: true
  plugins:
    - name: grafana-oncall-app
      version: v1.3.12

Тут главное еще не обосраться с версионированием, ведь примерно все версионируют свои плагины просто цифрами (например, 1.3.1), но не авторы oncall.


Лирическое отступление: Из-за этого некоторые дописывают в ансиблы vv1.3.1 , потому что роль имеет свойство тримать v в начале версии. Где-то на просторах графана-форума есть тред об этом.

Окей, поднимаем, получаем ошибку "несовместимая версия" графаны. А все почему? А потому что оператор раскатывает версию 9.1 из своего репозитория.
Лады, переопределим:

---
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: grafana
  labels:
    dashboards: "grafana"
  annotations:
    cert-manager.io/issuer: "letsencrypt-prod"
spec:
  deployment:
    spec:
      template:
        spec:
          containers:
            - name: grafana
              image: grafana/grafana:latest
...

Ладно, ладно, чтобы сэкономить вам время - это не сработает. Причем никаких ошибок не будет, у вас просто не будет установленного плагина. Кек.

Ок, давайте сделаем свой кастомный образ:

FROM grafana/grafana:latest

RUN grafana cli plugins install grafana-oncall-app

Запушим, укажем в манифесте, поднимем...
И получим кислый хер, плагина нет. Почему? А прост оператор по дефолту монитрует в /var/lib/grafana вольюм emptyDir

Сука))00)0)0)0)

tl;dr


Окей, вернемся к нашему докерфайлу и перепишем его, опираясь на depricated Dockerfile. Возможно, где-то есть и не depricated, но было 3 часа ночи и мне было лень искать.

FROM grafana/grafana:latest

USER root #Потому что будем двигать нашу директорию с плагинами в новое место

RUN grafana cli plugins install grafana-worldmap-panel \ #и что угодно, даже небо, даже Аллаха
    && grafana cli plugins install grafana-clickhouse-datasource \
    && grafana cli plugins install grafana-sentry-datasource \
    && grafana cli plugins install redis-datasource \
    && grafana cli plugins install grafana-polystat-panel \
	&& grafana cli plugins install grafana-oncall-app \
	&& mv /var/lib/grafana/plugins /usr/share/grafana/public #перемещаем в новое место, которое не монтируется оператором

USER grafana #вертаем взад юзера
ENV GF_PATHS_PLUGINS=/usr/share/grafana/public/plugins #определяем новую директорию для плагинов по-умолчанию


Билдим, пушим, кайфуем.

---
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: grafana
  labels:
    dashboards: "grafana"
  annotations:
    cert-manager.io/issuer: "letsencrypt-prod"
spec:
  deployment:
    spec:
      template:
        spec:
          containers:
            - name: grafana
              image: drf4ust/plugined-grafana:latest
              imagePullPolicy: Always


Во така хуйня, малята.