Ich betreue jetzt seit Version 2.7.1 Docker Container, die man sich bei Dockerhub herunterladen kann.

Während ich Anfangs auf alpine als Basis setzte (klein und schlank) musste ich in der Zwischenzeit auf debian schwenken.
Die Paketbetreuer bei alpine waren – für meinen Releasezyklus – etwas zu langsam und in letzter Zeit gibt es größere Probleme mit der libc Implementierung (musl-lib), die bei alpine die Basis bildet.
Immer wieder stürzte der icinga2 Prozess ab und ich konnte dem ganzen nicht mit einem Debbuger beikommen. Dazu gibt es einen entsprechenden Thread im Monitoring Portal so wie einen Bugreport bei alpine.

Aber was unterscheidet jetzt meine Container Implementierung von der anderer (zum Beispiel Christoph Wiechert)?
Ich gebe es zu, ich bediene mich ab un an bei Christoph. Sein Container ist gut, auch wenn er CentOS benutzt. ;)

(Vorsicht, jetzt könnte eine Menge Eigenwerbung folgen! ;) )

Mein Container kann aktuell in zwei verschiedenen Flavours laufen:

  1. als Icinga2 Master
  2. als Icinga2 Satellite


Die Konfiguration des Satellites erfolgt dabei quasi vollautomatisch.
Um das zu gewährleisten nutze ich sinnvollerweise die Icinga2 API und habe zusätzlich meinen Cerificate Service integriert.
(Eine erste Vorstellung hatte ich schon einmal im Blog)

Mittlerweile kann ich den Certificate Service auch hinter einem (SSL) Proxy betreiben und er prüft, ob der anfragende Service auch für die Signierung eines Zertifikates berechtigt ist. (Das hatte mal der Michael angeregt)

(Ich werde jetzt nicht jede einzelne Codezeile kommentieren, ein Grundverständniss von Icinga2 und Containern setze ich jetzt mal voraus)

Aber wie funktioniert das ganze eigentlich im Zusammenspiel in einem Container?

Aktuell speichern die Container einen lokalen Zustand. Unter anderem beinhaltet das auch – für den Master – eine eigene CA, oder – für die Satelliten – ein durch die CA signiertes ein SSL-Zertifikat zur sicheren Kommunikation.

Der Master Container erzeugt bei seinem ersten Start eine CA und speichert diese im Filesystem ab.
Zum Schluß startet der den Cerificate Service und damit wäre der Master betriebsbereit.

Ein Satellite Container muß da schon etwas mehr tun.
So bald der Container festgestellt hat, dass sein Master und der Cerificate Service verfügbar ist, versucht er beim Master ein Zertifikat zu beantragen. (Natürlich nur dann, wenn er noch keines hat!)
Das geschieht über den Aufruf von icinga2 node wizard. Da dies aber ein Wizard ist, der hier auf Eingaben reagiert, muß ich einen Umweg über expect gehen.
Dazu habe ich mir ein script zusammengestellt, welches mir alle Fragen so beantwortet, wie es erwartet wird.
Das rufe ich anschließend bei der Initialisierung auf.
Dadurch generiert mir der Master ein entsprechendes Zertifikat und der erste Schritt ist abgeschlossen.

Jetzt wird es etwas komplizierter.
Denn jetzt muß man manuell auf dem Master das Zertifikat gegenzeichnen. (Dazu empfehle ich die Dokumentation vom Icinga Projekt!)
Manuell finde ich – gerade im Container Umfeld – ziemlich doof.

Um das ganze zu umgehen habe ich mir den Cerificate Service geschrieben. Dieser bietet mir eine ReST Schnittstelle, so daß ich mein Satellite beim Master beantragen kann, dass sein Zertifikat signiert wird.
Anschließend kann der Prozess dieses herunterladen und installieren.
Natürlich ist das nicht das einzige, was der Service macht. Schließlich wird für jeden Satellite auch eine Konfiguarion benötigt.
Sobald sich der Satellite seine eigene Konfiguration hingelegt hat, startet er – über die API – den Master neu, da nur so die neue Konfiguration der Endpunkte angezogen wird. (Aus System Engineering Sicht gäbe es bestimmt eine bessere Lösung ;) )

Der Satellit wartet jetzt wieder, bis der Master verfügbar ist um sich anschließend selber beim Master zu registrieren.

Dazu nutzte ich initial einen festen Satz an Host-Variablen.
Das erwies sich als viel zu starr, so daß ich jetzt über ein Container Volume eine individuelle Konfiguration in den Container hineinreichen kann.
Den festen Satz habe ich mit der Zeit etwas erweitert und der wird jetzt nur noch benutzt, wenn es keine anderweitige Konfiguration gefunden wird.

Auch hier gilt, ein Neustart muß sein um erstellte Konfigurationen und Zertifikate zu aktivieren.

Damit haben wir einen lauffähigen Verbund von einem Icinga Master und (mindestens) einem Satelliten.

Den Certificate Service kann ich jetzt auch von jedem anderen Service anfragen.
Zum Beispiel binde ich meinen icinga-dashing Container damit automagisch an den Master an.

Jetzt gibt es aber auch „Künstler“ die anfangen eine Master Instanz komplett aus dem Netz zu schießen. Damit ist aber auch die erzeugte CA obsolete und nicht mehr verfügbar. Wenn dann der Master neu gestartet wird und der ganze Konfigurationszirkus von vorn beginnt, sind damit alle Zertifikate der Satelliten nicht mehr gültig und das Monitoring als solches nicht mehr nutzbar.
(Die Art von Künstlern, die nach dem sie alles kar0tt gemacht haben weil sie das Konzept nicht verstanden haben, lauthals herumschreien, dass Icinga2 für Monitoring völlig ungeeignet sei. Hauptsache man ist nicht selber Schuld.)

Meine (erste und aktuelle) Lösung war, dass ich die Satelliten etwas intelligenter gemacht habe und im Hintergrund ein Script über das Zertifikat der CA wacht.
Dazu erstellt es einen Hashwert über die ca.crt und fragt periodisch beim Certificate Service an, ob der Hash noch in Ordnung ist.
Wenn nicht, werden die eigenen Zertifikate gelöscht und der Container startet sich erneut.
Zwar beginnt das Spiel damit von neuem, aber das Monitoring heilt sich in dem Falle selber.

Mein aktuelles Ziel ist aber die Integration von consul als externen Speicher für die Zertifikate zu verwenden.
Damit könnte jederzeit der Master seine eigene Zertifikate wiederherstellen.
Gegenfalls müsste ich dann aber auch alle Dateien, die icinga2 unter /var/lib/icinga2 anlegt auch dort speichern.
Mal sehen, wie ich das auf Architekturseite hin bekomme. ;)