Bei meinem aktuellen Arbeitgeber nutzen wir seit meinem Einzug vor vier Jahren dort ein Icinga2 (damals noch parallel zum bestehenden Zabbix).
Mittlerweile ist Zabbix verschwunden und Icinga2 ist das Hauptmonitoring Tool. In den letzten Wochen haben wir aus der einzelnen Icinga2 Instanz nun einen HA Cluster gebastelt, das im Grunde auf diesem Konzept (meinen Dank an Carsten) beruht. Jeder der in dieser Thematik steckt, kommt früher oder später zum gleichen oder ähnlichen Ansatz.
Bei uns gibt es allerdings eine Besonderheit: Consul.
Consul in knappen Worten: Es ist ein verteilter Mesh Dienst, der in erster Linie Dienste bereitstellt und ein Key-Value Speicher ist.
Auf jedem Server(Node) der Dienste beherbergt, die durch unseren HAProxy Balancer laufen, bekommen einen Consul Agent. Dieser Agent hat zwei Aufgaben:
- Node im Katalog bereitstellen
- Verfügbarkeit prüfen
Damit taucht also der jeweilige Server im Consul Netzwerk auf und kann anschließend mit einem oder mehreren Diensten (Service) bestückt werden. In unserem Fall war das Icingaweb2. Warum so kompliziert ? Nun, für Consul gibt es einen kleinen Daemon der sich “consul-template” nennt. Dieser schaut permanent nach Änderungen im konfigurierten kv/ Bereich (Key-Value) vom Consul und generiert daraus eine passende haproxy.cfg Datei. Damit kann ich zum Beispiel Änderungen am HAProxy durchführen, ohne die Datei selbst anfassen zu müssen. Besonders angenehmen ist die Möglichkeit einen Server – der in die Wartung soll (Updates / Neustarts etc.) – einfach aus dem Balancer nehmen zu können, indem z.B. weight auf 0, oder der Consul Agent auf Maintenance gesetzt wird.
Nur für Icingaweb2 lohnt sich dieser Aufwand natürlich nicht wirklich, doch wenn das gleiche Konzept auch auch MariaDB angewendet wird, gibt es bereits zwei Dienste, die von Consul profitieren (können). Es ist meist nur eine Frage der Zeit, bis sich weitere Dienste hinzugesellen.
Ich habe mal ein abgewandeltes Diagramm von unserem aktuellen Layout erstellt:
Dort sind nun natürlich viel mehr Komponenten abgebildet, als ich bisher angesprochen habe, aber der Reihe nach.
Konzept
Icinga2 Konfiguration
Unsere Icinga2 Konfiguration wird aus zwei Quellen zusammengesetzt:
- Icingaweb2 - Director Modul
- Templates - Nahezu alle Host und Service Templates werden dort verwaltet
- Notifications - Benachrichtigungsregeln
- User - Alle Benutzer
- PuppetDB - Neue Hosts werden aus der PuppetDB gezogen via Director PuppetDB Module
- zones - Puppet
- global-templates/
- master/
- dc-1/
In den von Puppet gepflegten Zonen sind bei uns aktuell sämtliche Hosts enthalten, die über keinen Puppet Agent verfügen. Das sind vor allem Switches, Router und Firewalls. Der Grund ist einfach der, dass wir mittels Dictionary Ports/Interfaces etc. definieren, welches über das Director Modul so nicht unterstützt wird.
Das Director Modul von Icingaweb ist der Kleber, der alles zusammenhält. Jeder Host mit einem Puppet Agent, bekommt über ein Profil mit auf dem Weg, welches den Icinga2 Agent passend konfiguriert und einbindet. Dabei werden bei jedem Lauf gesammelte Fakten (facts) in eine PostgreSQL Datenbank abgelegt (pro Rechenzentrum!). Diese Daten werden vom Director PuppetDB abgerufen und in die eigene DB überführt (synchronisiert). Während der Synchronisation wird z.B. die Icinga2 Zone hinzugefügt, als Beispiel host.zone = dc-1
. In einem zweiten Schritt werden aus den synchronisierten Fakten Icinga2 Hosts erstellt, angereichert mit weiteren Host Variablen. Dies können sein:
- vars.is_virtual
- vars.blockdevices []
- vars.puppet_modules
- vars.zones
- vars.role
- …
Ein besonders wichtiger Punkt ist der, dass ich bestimmten Hosts auch Templates mitgeben kann, sodass immer das Template “Host Squid” eingebunden wird, wenn der certname=*proxy*
` ist.
Datenbank
Damit Icinga2 und Icingaweb2 funktionieren, benötigt es natürlich eine Datenbank, die klassischerweise eine MySQL bzw. MariaDB ist. Damit beim Ausfall einer Datenbank Icingaweb2 nicht gleich blind (und damit der Benutzer) ist (Icinga2 selbst würde aber dennoch weiter funktionieren), wird die Datenbank als Cluster konfiguriert. Bei MariaDB ist das ein Galera Cluster. Damit kann ich einen n-Master aufbauen, sodass auf alle DB Server schreibend und lesend zugegriffen werden kann.
HAProxy / Keepalived
Nun, was nutzt mir ein hochverfügbarer DB Cluster oder Webserver, wenn beim Ausfall die IP Adresse nicht auf einen funktionierenden Host geschwenkt wird ? Nun an dieser Stelle kommt das klassische Paar HAProxy und Keepalived zum Einsatz. HAProxy stellt sicher, dass die als Backend konfigurierten Server und deren Dienste funktionieren, um Anfragen an sie weiterzuleiten. Antwortet ein Dienst nicht so, wie er sollte, werden alle Anfragen an die verbliebenen geleitet.
Keepalived stellt die virtuelle IP Adresse bereit, über die alle Anfragen eingehen. Sollte der Server auf dem Keepalived läuft ein Problem haben, werden alle virtuellen IP Adressen auf den zweiten Server hochgefahren.
Consul / consul-template
Während bisher noch keine wesentlichen Neuerungen gegenüber den vielen Anleitungen im Netz auftauchten, kommt jetzt eine Komponente, die erst einmal Arbeit produziert, aber sich im Unternehmen dann doch irgendwann bezahlt macht: Consul
Consul ist ein Dienst, der ein eigenständiges Netz (MESH) aufspannt, sowie Daten und einen Service Katalog bereitstellt.
Ohne jetzt allzutief in die Consul Thematiken vorzudringen, gibt es zwei Anwendungszwecke in diesem Setup:
- Backend/Frontend Konfiguration für HAProxy
- Service Funktion prüfen und bereitstellen
Ich kann dann über eine Weboberfläche, API oder CLI die HA Proxy Konfiguration verändern oder eine Node aus dem Balancer nehmen. Im optimalen Fall nimmt sich ein Backend selbst aus der Konfiguration, wenn der Server zum Beispiel neugestartet wird, und fügt sich anschließend wieder hinzu. Gleiches gilt natürlich, wenn einfach nur der Dienst (z.B: Apache2) kaputt ist.
Für den HAproxy Teil wird consul-template verwendet, der den Consul ausfragt und die neue Konfiguration erstellt. Den Rest erledigt Consul selbst.
Hat man das Konzept erst einmal verstanden, dauert es nicht mehr lang, bis weitere Service Dienste ihren Weg in Consul finden.
Fazit
Im Grunde ist das Konzept krisenfest, da es keinen SPOF gibt. Es gab allerdings zwei Dinge, die uns ein wenig Probleme bereiteten:
- Kein ProxySQL: ProxySQL versteht SQL und dient als Vermittler zwischen dem Client (Applikation) und den tatsächlichen Datenbanken. Es können da so Geschichten wie READ/WRITE Splitting erledigt werden und ProxySQL schwenkt auf eine funktionierende Datenbank um, wenn die aktiv benutzte eben nicht mehr aktiv ist.
Leider klappt das nicht mit Icinga2 IDO und Icingaweb2, da ProxySQL einige SQL Kommandos “frisst”, bzw. nicht richtig weiterleitet, weshalb wir mit einer kaputten IDO da standen und einer kaputten Icinga2 Konfiguration. - SELECT * from foo; != OK: Dank BINlogs lief bei uns sehr zügig die Platte voll, weshalb die aktive MariaDB keine Schreiboperationen mehr durchführen konnte, aber lesen ging dafür immer noch(!). Dies hatte zur Konsequenz, dass Icinga2 die IDO nicht befüllen konnte, aber HAProxy/Consul die DB nicht aus dem Balancer nahm, weil der SELECT Test klappte ja noch. Wäre die MariaDB tot gewesen … hätte Consul die HAProxy Konfiguration neu erstellt und ich hätte keine Daten verloren.
Ich werde die nächsten Tage und Wochen mal dieses Konzept im Detail aufschreiben, sodass jeder es später nachvollziehen kann :-)