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.0apiVersion: 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
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