YOTRON stand jetzt zum zweiten Mal vor der Herausforderung, in einem Projekt mit verteilten Teams Grafana als zentrale Monitoringinstanz zu betreiben. Denn Grafana ist immer noch das Maß aller Dinge um relevante aber auch visuell ansprechende und performante Dashboards für den Applikationsbetrieb zu erstellen. Was sich aber erst einmal als einfache Anforderung anhört, hat jedes mal zu einem größeren Aufwand geführt.
Hinweis: Umsetzung als OpenSource-Projekt (Update vom 12.10.2024)
Wir haben die hier im Blog postulierte Lösung auf Basis von Python weiterentwickelt und als OpenSource-Projekt in GitHub bereitgestellt (siehe Grafana-Content-Manager)
Schwachstellen von Grafana
Denn die Schwachstellen sind immer noch die gleichen wie eh un je. Da wären vor allem
- die nicht existierende Backup- und Recoveryfunktionalität,
- SQLite als zentrale Metadatenbank, die nur ganz schlecht in einem geclusterten Setup von Grafana funktioniert,
- das nutzergerechte Alertings sowie
- die fehlende Continious-Delivery Funktionalität, die eine Synchronisierung der Dashboards und Alertings zwischen z.B. einer Entwicklungs- und einer produktiven Umgebung erlaubt.
Vor allem die Punkte 1 und 3 müssen mit eigenen Entwicklungen ergänzt werden, möchte man eine sichere, transparente und komfortable Lösung anbieten. Grafana unterstützt diese Anforderungen nur in dem Maße, dass es eine JSON-basierte Repräsentation der Dashboards, Alerts usw. anbietet, die, gepaart mit einer Export- und Import-Funktion, für ein Backup sowie einen Synchronisationsprozess genutzt werden kann. Die Nutzung funktioniert aber nur mit manuellen Anpassungen. Einfach eine JSON-Repräsentation in ein anderen Grafana zu importieren reicht nur in den seltensten Fällen. Genannt seien hier vor allen die Anpassung der UIDs der einzelnen Dashboard-Komponenten wie der Datasource.
Expertise bei einer ungeliebten Aufgabe bei hohem Aufwand
Das bedeutet, dass man bei Grafana mit der Out-Of-The-Box Funktionalität nur bei einer gewissen Expertise und einem hohen manuellen Aufwand ein stabiles und sicheres Monitoring aufbauen kann. Dies bei einem Thema, dass gerade in der Softwareentwicklung als zwar notwendig akzeptierte, aber trotzdem als eher ungeliebte Aufgabe angesehen wird.
Thema Backup und Recovery
Grafana besitzt keine eigene explizite Backup- oder Recoveryfunktionalität. Implizit ist dies durch die Möglichkeit, die Dashboards in ihrer JSON-Repräsentation zu speichern und auch wieder zu importieren, gegeben. Dies bedeutet aber einen hohen manuellen Aufwand bei der Notwendigkeit einer Expertise. Verteilte Entwicklerteams darin zu schulen, wie man die eigene kontinuierliche Arbeit an Dashboards oder Alerts in Grafana sichert, wäre zwar notwendig, hat aber wegen des Zeitaufwands keine hohe Priorität oder hat nicht einmal einen bewusst geplanten Fokus.
Alternative Backup- und Recoverymöglichkeiten
Für Backup- und Recovery von Grafana gibt es aber durchaus Alternativen, vor allem wenn diese auf Storages abgelegt sind. Für Kubernetes haben wir sehr gute Erfahrungen mit Kasten K10 gemacht, einem Tool mit dem man Resourcen von Kubernetes sichern kann. Dies setzt aber zum eine Kubernetes voraus, zum anderen den Betrieb von Kasten K10 als eine separate Applikation. Diese benötigt zusätzliches KnowHow, das im optimalen Fall in einem zentralen Team organisiert ist, um Backups strukturiert zu erstellen und die Systeme nicht zu überlasten (Siehe hier auch unseren Erfahrungsbericht mit Kasten K10)
Beschreibung aus der Praxis
In einem Projekt standen wir vor der Herausforderung auf der grünen Wiese ein Monitoring für verteilte Entwicklerteams aufbauen zu müssen. Da OpenShift als Kubernetesprovider im Einsatz war, war die Installation von Grafana und Prometheus durch die OpenShift-Operator vorgegeben. Für das Alerting nutzten wir Unified Alerting von Grafana, da dieses ab Mayor Release 10 von Grafana das alte Alerting ablösen wird.
Da das Monitoring durch die Entwicklerteams nur rudimentär genutzt wurde, hatten wir halbwegs freie Hand.
Vorgehen
Auch wenn es sich erst mal seltsam anhört, aber im allerersten Schritt haben wir uns um das Backup inklusive Desaster-Recovery gekümmert,
um bei einem kompletten Verlust an Dashboards und Alerts diese wiederherzustellen zu können. Gerade bei der Nutzung der SQLite im
geclusterten Grafana sollte man immer mit dem Verlust der SQLite-Metadatenbank rechnen.
Auch wenn die Erfahrung in einem Setup fehlt, ist das der richtige Ansatz um durch “Kinderkrankheiten” verursachte Datenverluste durch ein Backup wiederherzustellen zu können.
Unser grundsätzliches Ziel bei der Implementierung des Prozesses war es, diesen so sicher und so automatisiert wie möglich zu halten und bei den Entwicklern der Dashboards keinerlei Aufwand durch die Sicherung ihrer Arbeit entstehen zu lassen. Ergänzende Ziele bei der Entwicklung einer Backup- und Recoveryfunktionalität waren daher
- die vollautomatische Prozessierung des Backups aller Grafana Komponenten wie Dashboards oder Alerts,
- keinen Aufwand bei den Entwicklern zu erzeugen,
- die Nutzung der vorhandenen Infrastruktur,
- die Transparenz des Backups,
- Etablierung einer Versionierung des Backups,
- eine Recovery per “Knopfdruck” ohne Nachbearbeitung sowie
- keine gesonderte Entwicklungssprache für die Teams.
Infrastruktur
Die relevante Infrastruktur umfasste keine besondere Applikation. Wie erwähnt lief Grafana auf einer OpenShift-Umgebung. Für die Code-Versionierung sowie der Continuous-Integration und des Continuous-Delivery (CICD) war GitLab im Einsatz.
Ansatz
Grobes Konzept
Sämtliche Prozesse wurden über die gewöhnliche Grafana-API entwickelt. Für das Backup und das Restoring wurde die JSON-Repräsentation der Grafana-Komponenten, wie Dashboards, Alerts, Contact-Points usw. genutzt, die man über die API von Grafana pullen und auch wieder nach Grafana pushen kann.
Gesetzt war die GitLab CICD für das Pullen der JSON-Repräsentation der Dashboards, Alerts usw. von Grafana, sowie für das Desaster Recovery, also das pushen der Grafana-Komponenten aus dem Backup nach Grafana.
Für die Persistierung hatten wir uns für die einfachste Möglichkeit entschieden, nämlich GitLab als Verionierungsystem. Während des Backups wurden die JSON-Repräsentationen der Grafana Komponenten einfach in einem GitLab-Folder gespeichert und als neue Version committed.
Der Ansatz war recht einfach umzusetzen. Wir konnten bei jedem Backupprozess immer alle JSONs der Dashboards und Alerts neu als Datei (über-)schreiben. Wenn keine Änderung vorlag (niemand hat an dem jeweiligen Dashboard gearbeitet) hatte sich das JSON nicht geändert und GitLab hat diese damit auch als nicht zu committen ignoriert. GitLab übernahm damit automatisch die Versionierung der Dashboards.
Zusätzlich hatten wir uns noch um den Prozess gekümmert, um gelöschte Dashboards zu identifizieren und das korrespondierende JSON-Files zu löschen.
Programmiersprache
Wir haben uns entschieden sämtliche Prozesse in Bash zu programmieren. Java und JS/TS/NodeJS waren für die Applikationen gesetzt, aber wir wollten dafür keinen neuen Buildprozess einführen, wie es für Java, Golang oder auch Python notwendig gewesen wäre. Der Prozess konnte so in GitLab ohne neuen Dockercontainer oder Build durchgeführt werden sowie ohne die Anbindung spezieller Repositories wie PyPi, Maven-Repository usw..
In Bash nutzen wir vor allem CURL für die Anfragen gegen die Grafana-API und JQ für die Analyse des JSON-Responses. Wir arbeiteten auch mit Bash relativ strukturiert und versahen alle CURL Requests mit einem Errorhandling.
Die API-Calls wurden jeweils als eigene Funktion konfiguriert, z.B.
createGrafanaAlertFromFile() {
response=$(curl -X POST -H "$authbaerertoken" -H "X-Disable-Provenance: ''" -H "Content-Type: application/json" -w ",%{http_code}" -d @$1 $GRAFANA_URL/api/v1/provisioning/alert-rules)
statusCode=${response: -3}
if [ "$statusCode" != "200" ]; then
echo "Could not create Grafana alert-rule, Request failed, StatusCode: $statusCode" >> $errorlogfolder/errors.log
exit
fi
echo "$statusCode"
}
Eine Anmerkung zum Header-Eintrag “X-Disable-Provenance”. Wenn man Resourcen in Grafana via API erstellt, können manche in der Grafana-GUI nicht mehr bearbeitet werden, wie die Notification-Policies. Das ist eine normale Funktionalität von Grafana. Mit dem Header “X-Disable-Provenance” können diese zur Bearbeitung mit der Grafana-GUI freigegeben werden.
Prozesse
Alle Prozesse für das regelmäßige Backup und das Restoren in die verschiedenen Umgebungen übernahmen GitLab-Pipelines. Der Bashcode lag zusammen mit den Dashboards im GitLab-Projekt und konnte somit einfach direkt aus der Pipeline aufgerufen werden. Der stündliche Cron-Job für das Backup wurde in Gitlab über den Pipelinescheduler organisiert. Für das Restoren wurde einfach die Pipeline des Commits ausgeführt das den zu restorenden Stand beinhaltete.
Feedback
In der Anfangszeit des Projektes hatten wir die Metadatenbank von Grafana häufiger verloren, sodass es mehrmals notwendig war, ein Desaster Recovery durchzuführen. Der Prozess hat funktioniert, wir hatten nie Rückmeldung bzgl. fehlender oder falscher Inhalte erhalten. Bei der Übergabe an das Betriebsteam haben wir noch die Rückmeldung bekommen, dass unser Code in Bash sehr strukturiert war, was bei Bash auch nicht immer selbstverständlich ist.
Erfahrungen
Grundsätzlich hat sich das Konzept bewährt. Auch bei Problemen, bestand immer die Möglichkeit auf einen älteren Commit zurückzugehen, und diesen zu recovern. Die Entwickler selbst hatten keine eigenen Aufwände in dem Prozess. Jedes neue Grafana-Dashboard war sofort Teil des Backups. Auch wenn der Prozess mal nicht durchlaufen konnte, war beim nächsten Durchlauf wieder ein korrektes und aktuelles Backup vorhanden.
Als umständlich erwies sich das Error-Handling der GrafanaAPI-Requests. Ein strukturiertes Handling mit einem TRY CATCH, das auch für einen Abbruch des Prozesses sorgt,
hätte die Entwicklung vereinfacht.
Zudem wird in Bash bei einem Funktionsaufruf eigntlich ein
Consolentext zurückgegeben und keine Objekte, Arrays oder ähnliches. Ein Errortext war von einem JSON-Text Out-Of-The-Box nicht zu unterscheiden.
Hier wäre ein Ansatz mit einer ordentlichen Skriptsprache wie Python besser gewesen.
Der Prozess läuft auch nach der Übergabe an das interne Operating Team weiterhin vollautomatisch im Hintergrund.