Service Discoveries sind für mich Fluch und Segen zugleich.
Auf der einen Seite stellen sie einem Monitoring ggf. alle nötigen Informationen zur Verfügung.
Auf der anderen Seite verleiten sie aber dazu sich nicht mehr mit dem System oder der Software - die
man überwachen soll - zu beschäftigen.
Was darin mündet, dass man wichtige Dinge übersieht und damit in ein offenes Messer läuft.
Es ist wie mit der Automatisierung: Sie nimmt einen die stupide Arbeit ab.
Aber man setzt sich nicht mehr mit der Konfiguration auseinander. Man setzt darauf, dass die
Standardkonfiguration für den eigenen Einsatzweck ausreichend ist (Was erfahrungsgemäß selten der
Fall sein dürfte).
Man verlernt es, einen Service manuell zu konfigurieren und bekommt damit im Ernstfall Probleme
eine effektive Fehleranalyse durchzuführen.
In meinem speziellen Fall nimmt mir diese eine Service Discovery eine Menge wiederkehrender Schreibarbeit ab.
Aber auch erst dann, nachdem ich sie mir programmiert habe! :)
Hintergrund
In vielen meiner Projekten bei Tallence arbeite ich an
Continuous Integration / Continuous Delivery. Also der Automatisierung von Build- und Deploymentumgebungen,
dem Deployment von unterschiedlichen Services sowie deren Monitoring. Unter anderem (aber nicht nur) für
CoreMedia Systeme.
Über die Jahre hinweg habe ich mir dafür ein ziemlich umfangreiches Toolset aufgebaut, welches ich dafür
wiederverwerten kann.
Dazu gehören Basiskonfigurationen von virtuellen Instanzen, Ansible Rollen für die Installation sowie
Konfigurationen von
dockerd,
prometheus,
grafana
und so weiter.
Beim Monitoring nutze ich die bereits existierenden und funktionierenden Regeln für den Prometheus und
tausche nur noch den individuellen Part der jeweiligen Umgebung aus.
Das beschert mir jedesmal lockere 300 zusätzliche Zeilen in der Ansible yaml Wüste.
Denn jeder neue Service muss manuell hinzugefügt werden um anschießend über das Deployment ausgerollt zu werden.
Das funktioniert … normalerweise.
Bis auf die wenigen Male, bei denen ich gegen einen der berüchtigten copy-paste Fehler laufe.
Aber ich bin damit nicht zufrieden. Für mich wäre es einfacher, wenn ich hier einen Automatismus hätte.
Eine Prometheus Regel, fertig.
Dafür einen Standard, wie die Services detektiert werden, wie deren Prometheus Tags definiert sind.
Zudem können damit sofort alle Grafana Dashboards weiterverwenden werden.
Win-Win-Win sozusagen …
Schon seit langem trug ich die Idee mit mir herum um mir für diesen ganzen Mist einen eigenen Service zu schreiben. Der mich dabei unterstützt diese riesige statische yaml Tapete einzudampfen und um einen Standard zu definieren um “hintenrum” weniger stupide Arbeit zu haben.
Lösungsansatz
Letztendlich brauchte ich nur ein ReST Service, der ein Stück Code aufruft, der im Hintergrund Informationen
vom dockerd
besorgt, aufbereitet und das Ergebniss zurückgibt.
Da ich mir schon einmal einen Container Watchdog erstellt hab (dieser überwacht Container und startet diese neu, wenn der Daemon meint sie wären tot), konnte ich auf bereits funktionierenden Code zurückgreifen, was die ersten Schritte deutlich beschleunigte.
Der schnelle Wurf wurde daher ein Deamon in python.
Der Code sieht schlimm aus und um nicht das System vollzumüllen habe ich mir bequemerweise einen Container
gebaut, der mit dem socket vom dockerd kommuniziert.
Aber einen Container bauen, der mit dem dafür nötigen Service kommuniziert?
Ist auch etwas hirnrissig.
Der nächste - richtige - Ansatz war es den bestehenden Code zu generalisieren und nach golang zu
konvertieren.
In der Vergangenheit hatte ich schon einmal ein paar Berührungspunkte mit golang und dachte daher,
dass mir das konvertieren locker von der Hand gehen würde.
Weit gefehlt!
Seit damals gab es doch einige Veränderungen und so musste ich quasi wieder von vorn beginnen.
Ideen
Nicht jeder Container, den ich ausrollen muß, muß auch über die Service Discovery dem Prometheus
bekannt gemacht werden.
Schließlich gibt es noch einige alte Services, die keinen Metrics Endpunkt anbieten.
Auf der anderen Seite gibt es aber auch Container, die 2 (oder mehrere) verschiedene Endpunkte besitzen, die ich dem Monitoring zur Verfügung stellen möchte. Oder aber deren Metrics Pfad abweichend vom üblichen Standard ist.
Viele Dinge die beachtet werden müssen um möglichst flexibel zu sein.
Lastenheft, ich komme! ;)
Die erste Idee war, ich werde der Service Discovery eine Konfiguration zur Seite stellen und darin alle Ports samt Metrics Pfade definieren. Als ich mir eine Liste der möglichen Kombinationen von Ports und Metrics Pfade zusammenstellte, ist mir aufgefallen, dass dies die ungünstigste Art sei damit umzugehen. Nach etwas herumprobieren und hinterfragen der Ergebnisse habe ich mich dazu entschlossen, dass ich eine andere Lösung dafür benötige.
Letztendlich kann ich an jeden Container entsprechende Labels anbringen, mit denen ich sowohl Port, als auch Metrics Pfad definieren kann.
1
2
3
labels:
service-discover.port.8199: "/metrics"
service-discover.port.8081: "/actuator/prometheus"
Damit bleibt die Service Discovery für mich maximal flexibel.
Umsetzung
Die erste Herausforderung für mich bei golang war schon der initiale Aufbau eines entsprechenden Projektes.
Nachdem ich das mit go.mod
verstanden habe, musste ich mich nur noch um den Code kümmern hust …
Aus anderen Sprachen war ich es gewohnt, dass man mit globalen Klassen arbeiten kann, mit includes und
anderen schönen Dingen .. die golang anders handhabt.
Irgendwie habe ich es geschafft, alle Probleme so zu umschiffen, dass das Ergebniss so wie erwartet funktioniert. Mit Sicherheit sind einge meiner Problemlösungen sehr unschön gelöst, aber sie funktionieren. (Wer möchte, kann mir gern Tipps geben, wie man das besser und/oder effizienter macht!)
Fazit
Ich habe jetzt eine funktionierende Service Discovery, die mir genau die Informationen zur Verfügung stellt, die ein Prometheus benötigt.
Die ~300 Zeilen yaml Mist konnte ich auf 4 Zeilen eindampfen.
Das ganze läuft jetzt seit November 2022 stabil und sehr zufriedenstellend in einem meiner aktuellen Projekte.
Egal in welchem der alten oder neuen Projekte ich den Service benutze, ich bekomme immer das gleiche Ergebniss.
1
2
3
4
5
6
7
8
9
10
11
12
[
{
"targets": [
"localhost:9090"
],
"labels": {
"__metrics_path__": "/metrics",
"application": "prometheus",
"container": "prometheus"
}
}
]
Die entsprechenden Grafana Dashboards kann ich also ebenso über alle gleichartigen Projekte weiterverwenden.
Was ich als sehr schade empfand war die Tatsache, dass beim Arbeitgeber scheinbar wenig Menschen sitzen,
die sich mit golang als Programiersprache befassen. Können oder wollen.
Ich habe mir diese Service Discovery (wie viele andere Ansible Rollen) in meiner Freizeit zusammengebaut.
Weil ich lernen wollte.
Und weil ich innerhalb der Projekte keine Zeit für solche Dinge bekomme.
Wobei die Vorteile - für mich - auf der Hand liegen.
Ich hätte mir gewünscht, das mir mein Arbeitgeber hier etwas entgegen gekommen wäre. Mir die Zeit und Unterstützung gegeben hätte dieses Thema umzusetzen. Denn schließlich profitiert nicht nur ich, sondern auch er und die Projekte - und damit letztendlich auch die Kunden - davon.
Okay, profitieren tut er ja jetzt schon … gute 3 Personentage Arbeit, die ich - mal wieder - in meiner Freizeit geleistet habe, steht ihm jetzt für Umme zur Verfügung.
Auch weil ich meine Lösung jetzt nach Möglichkeit in jedes kommende Projekt integrieren werde.