So gut sich das im ersten Dovecot Director-Artikel auch anhört, gibt es dennoch auch ein paar Dinge, die der Director nicht so gut beherrscht. Als Erstes verteilt er zwar wunderbar die Verbindungen, macht aber keinerlei Monitoring, ob das Backend überhaupt verfügbar ist. Dafür gibt es wiederum zwei weitere kleine Dienste, die diesen Job übernehmen können. Dovemon (Dovecot Pro) und Poolmon (https://github.com/HeinleinSupport/poolmon [1]). Diese prüfen regelmäßig die Verfügbarkeit der Backends, deaktivieren sie im Fehlerfall im Director und lassen die User umziehen. Der Director würde die Verbindungen nämlich weiter an das fehlerhafte Backend schicken.
Des Weiteren lassen sich ohne einen Reload von Dovecot keine weiteren Direktoren mit in den Ring nehmen. Was bei 2-3 Direktoren und virtuellen Maschinen noch kein Problem ist, wird bei automatischer Skalierung via Kubernetes & Co. aber ein großer Nachteil. In typischen Container-Konzepten hieße das nämlich, alle Direktoren-Container neu auszurollen.
Und der Director lädt beim Start zwar seine Config von der Platte, danach werden Änderungen aber im Speicher gehalten und mit den anderen Direktoren über das Ring-Protokoll synchronisiert. Hier kann es zu Split-Brain Situationen kommen, wenn mehrere Direktoren unterschiedliche Laufzeitdaten haben.
In Dovecot 3.0 entfernt Dovecot genau nur den Director Service, also die Möglichkeit, die User automatisch auf die entsprechenden Backend Systeme zu verteilen bzw. deren Verteilung zu managen.
Alle anderen Komponenten inklusive des Proxy-Features bleiben uns erhalten. Wenn es also einen Weg gibt, die Host-Variable (z.B. host=10.0.0.1) wie beim Director nach unseren Vorgaben und Wünschen zu setzen, können wir auf den Director auch verzichten. Die Frage ist, welche Möglichkeiten uns Dovecot hierfür bietet.
Wenn wir also von je zwei Backends mit Replikation ausgehen, die für einen User in Frage kommen, wäre ein sehr einfacher Ansatz, random eine der 2 IP-Adressen auszuwählen (echo 10.0.0.$((1 + $RANDOM % 2))). Das könnte in einem Skript oder auch über datenbank-interne Funktion umgesetzt werden. Bei diesem Ansatz würde natürlich die Backend-Persistenz fehlen, also das Backend jedes Mal neu ausgewürfelt werden. Also müssen wir uns das ausgewürfelte Backend auch für eine Weile merken. Ein Skript bräuchte also noch eine Datenbank. Der initiale datenbank-interne Ansatz müsste neben dem Würfeln das Ergebnis auch speichern. In einer SQL-Datenbank könnte das mit einer Stored Procedure umgesetzt werden. Ein ähnlicher Ansatz wie im SQL wäre mit Lua Scripting in einer Redis-Datenbank möglich.
Wenn wir uns die Backend-Zuordnung in einer Datenbank merken, kann hier auch gleich das Monitoring der Backends implementiert werden. Sobald ein Backend nicht verfügbar ist, wird dieses in der Datenbank inaktiv gesetzt und wird bei der Auswahl des passenden Hosts nicht mehr berücksichtigt.
Realistisch gesehen werden die Stored-Procedures im SQL oder das Lua-Scripting im Redis zu unflexibel für alle Anforderungen sein. Uns bleibt dann noch der Aufruf eines Skriptes oder eines Dienstes via eines extra Dovecot Service oder vielleicht via CheckPassword passdb/userdb. Die interessantere Alternative scheint die Lua-Scripting Fähigkeit direkt in Dovecot zu sein. Diese scheint auch seitens Dovecot an verschiedenen Stellen anstelle anderer alter Funktionen favorisiert zu werden.
Lua Skripte lassen sich als passdb bzw. userdb Abfragen einrichten, sind spätestens mit entsprechenden Erweiterungen sehr mächtig und machen die Abfrage und Aufbereitung der User-Daten bzw. der Authentifizierung sehr flexibel.
In einem Lua Script ist es nun möglich mit einer HTTP API abzufragen oder mit Datenbanken wie SQL oder Redis zu kommunizieren. Man könnte also mittels eines externen HTTP Service oder vielleicht auch direkt in einem Dovecot-internen Lua-Script sowohl eines der verfügbaren Backends auswürfeln, sich die Auswahl für die Persistenz in einer Datenbank merken und vielleicht sogar ein gewisses Monitoring gleich mit integrieren.
Dieser Ansatz wäre wie der des Dovecot 3.0 Cluster mit zentraler Cassandra Datenbank unabhängig von einzelnen Proxy-Instanzen und könnte problemlos auch mit Kubernets-Pods skaliert werden.
Komplett ersetzt haben wir den Dovecot Director damit natürlich noch nicht. Um im produktiven Einsatz den gleichen oder sogar noch besseren Funktionsumfang zu bieten, brauchen wir auch noch einen Ersatz für Tools wie doveadm director kick oder flush. Aber auch dafür werden wir Ansätze finden, die der Arbeitsweise des Directors entsprechen. Das sollte mit der Dovecot HTTP API relativ einfach handhabbar sein.
Der Wegfall des Dovecot Directors ist schade, aber wenn wir uns die Probleme in bestimmten Situationen ansehen, nachvollziehbar. Eine Lösung ohne das bisherige Ring Protokoll bringt uns in containerisierten oder großen Umgebungen wirkliche Vorteile oder machen diese im Blick auf Auto-Skalierung überhaupt erst möglich. Die Funktionen werden wir mit den zahlreichen Möglichkeiten im Dovecot auch mit anderen Mitteln umsetzen können. Auch wenn der Dovecot 3.0 Cluster nur Teil der Pro Edition werden wird. Dovecot ist und bleibt der derzeit beste Ansatz für Mailbox-Systeme.
Wir werden uns Dovecot 3.0 in Ruhe anschauen und ein bisschen damit spielen. Sobald wir das Gefühl haben, dass wir es produktiv nutzen möchten, werden wir auch eine Lösung implementiert haben, die es uns erlaubt, die Funktionalität des Director weiterzuführen und vielleicht sogar ein wenig besser zu sein.
[1] In unserem Poolmon Fork haben wir ein paar Patches anderer Entwickler zusammen getragen und ein paar neue Funktionen eingebaut.
Autor: Carsten Rosenberg, Mailserver-Consultant, Heinlein Support
Zurück zu Teil 1 unseres Dovecot 3.0 Artikels.
Kommentare