Loadbalancing mit Keepalived für Postfix und Dovecot

Abstract

Da es nur wenige Artikel zu Loadbalancing mit Keepalived in Deutsch gibt, habe ich mich entschlossen, diesen Artikel zu schreiben.

Da es nur wenige Artikel zu Loadbalancing` mit Keepalived in Deutsch gibt, habe ich mich entschlossen, diesen Artikel zu schreiben. Ein ähnliches Setup habe ich seit Jahren produktiv für Postfix auf Ubuntu Lucid im Einsatz.

Note

Dieser Artikel ist keine Anleitung für ein produktives Setup. Er soll einen kleinen Einblick in Keepalived geben. Ich empfehle Interessierten einen Test in einer virtuellen Umgebung.

Loadbalancing allgemein bietet Lastverteilung und bis zu einem gewissen Punkt auch Ausfallsicherheit. Es ist z.B. relativ einfach, nachgelagerte Server hinzuzufügen, abzuschalten oder zu rebooten, ohne dass es zu deutlich spürbaren Verbindungsabrissen für den Nutzer kommt. Freilich muss man solche Szenarien je nach Protokoll und Anwendungsfall sorgfältig planen.

SSL/TLS basierende Protokolle oder Verbindungen sollten dabei besonders beachtet werden. Um die Ausfallsicherheit auch überall zu gewährleisten, beschreibe ich hier ein Szenario, bei dem bei Ausfall des Master Loadbalancers der Standby Backup Loadbalancer die Lastverteilung automatisch übernimmt – und umgekehrt. Beide Loadbalancer sind über ein gekreuztes Kabel auf einer zusätzlichen Netzwerkkarte eth1 verbunden und kontrollieren ihre Existenz laufend über eine Art Heartbeat.

Master Loadbalancer

/etc/keepalived/keepalived.conf

global_defs {
lvs_id LVS_1
}
vrrp_instance VI_1 {
       interface eth1
       state MASTER
       virtual_router_id 51
       priority 101                    # 101 on master, 100 on backup
       advert_int 1
       smtp_alert
authentication {
auth_type PASS
auth_pass yourpass

}

       virtual_ipaddress {
           1.2.3.210/24 brd 1.2.3.255 dev eth0
}

 virtual_server 1.2.3.210 25 {
   delay_loop 60
   lb_algo rr
   lb_kind DR
   protocol TCP
   persistence_timeout 360

   real_server 1.2.3.205 25 {
   weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 25
       nb_get_retry 3
       delay_before_retry 3
       }
   }
   real_server 1.2.3.206 25 {
       weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 25
       nb_get_retry 3
       delay_before_retry 3
       }
   }
}

Backup Loadbalancer

/etc/keepalived/keepalived.conf

global_defs {
lvs_id LVS_1
}

vrrp_instance VI_1 {
       interface eth1
       state BACKUP
       virtual_router_id 51
       priority 100                    # 101 on master, 100 on backup
       advert_int 1
       smtp_alert

authentication {
auth_type PASS
auth_pass yourpass
}
       virtual_ipaddress {
           1.2.3.210/24 brd 1.2.3.255 dev eth0
}

virtual_server 1.2.3.210 25 {
   delay_loop 60
   lb_algo rr
   lb_kind DR
   protocol TCP
   persistence_timeout 360

   real_server 1.2.3.205 25 {
   weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 25
       nb_get_retry 3
       delay_before_retry 3
       }
   }
   real_server 1.2.3.206 25 {
       weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 25
       nb_get_retry 3
       delay_before_retry 3
       }
   }
}

Ich hab mich hier auf Port 25 für Postfix beschränkt, weitere Ports (z.B. Submission) können jederzeit hinzugefügt werden.

Note

Es muss natürlich eine gemeinsame Datenhaltung für die Mail User geben. Dies kann man ebenfalls für Postfix mit Datenbanken oder LDAP realisieren und dabei deren eigene ausfallsicheren Setups konfigurieren, z.B. Datenbank Master/Master oder Master/Slave. Die Verwendung von zentralen Syslog-Servern ist zusätzlich empfohlen.

Parameter und Funktionsweise

Keepalived hat sehr viele Konfigurationsmöglichkeiten, sehr häufig werden auch Setups mit Servern hinter NAT beschrieben oder auch mit Tunnel Interfaces zu den hinter den Loadbalancern gehosteten Servern.

Außerdem kann man ein Keepalived-Setup auch mit Haproxy kombinieren oder Dovecot Directoren usw. Eine Kombination mit HA Diensten und einem NFS-Server habe ich schon einmal erfolgreich getestet.

Das hier beschriebene Keepalived-Setup bezieht sich auch eine Direct Routing (DR) Load Balancing Method Konfiguration, das heißt, die nachgelagerten Server haben eine offizielle IP-Adresse und erhalten zusätzlich ein Setup einer virtuellen Loadbalancer IP-Adresse, auf die Keepalived gemäß seiner Einstellungen den Traffic delegiert. Zusätzlich wird mittels Telnet Check der geloadbalancete Port in periodischen Abständen auf Antwort bzw. Existenz überprüft. Einer der Vorteile von Direct Routing ist, dass dieses Verfahren relativ wenige Änderungen an einer bereits existierenden Netzwerkstruktur benötigt.

Wegen der Unzahl von Konfigurationsmöglichkeiten von Keepalived kann ich an diese Stelle nicht auf alle Parameter eingehen. Wichtig ist: sie müssen sinnvoll kombiniert werden, z.B. ergibt die Kombination von Round Robin und Persistenz evtl. unerwartete Resultate, denn duch die Verbindungsperstistenz, die man allgemein vor allem bei SSL Ports möchte, wird die gewollte Verteilungsgewichtung der Verbindungen zumindest teilweise scheinbar aufgehoben.

Weitere notwendige Einstellungen

Auf den Loadbalancern sollten in /etc/sysctl.conf noch folgende Einträge vorgenommen werden. Auf IPv6 bin ich hier der Einfachheit halber nicht eingegegangen. Wenn keine IPv6-Infrastuktur zur Verfügung steht, sollte man die Verwendung auch am besten gleich überall ausschalten.

net.ipv4.tcp_syncookies=1
net.ipv4.ip_forward=1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.eth0.send_redirects = 0
net.ipv6.conf.all.disable_ipv6 = 1

Ein sysctl -p würde diese sofort in Betrieb nehmen.

Note

Da Loadbalancer im weitesten Sinne auch Router sind, sollte man auf ihnen auch gleich mit iptables sinnvolle Firewall-Regeln konfigurieren.

Auf den nachgelagerten Postfix-Servern erstellt man nun die Loadbalancer-IP-Adressen bzw. Interfaces zusätzlich zu den realen IP-Adressen.

/etc/network/interfaces

auto lo:210
iface lo:210 inet static
address 1.2.3.210
netmask 255.255.255.255
broadcast 1.2.3.255
pre-up echo 1 >/proc/sys/net/ipv4/conf/all/arp_ignore; echo 2 >/proc/sys/net/ipv4/conf/all/arp_announce

Zusätzlich evtl. auch hier Einträge in /etc/sysctl.conf

net.ipv6.conf.all.disable_ipv6 = 1

Note

Die Einträge in sysctl.conf bzw. net-Anweisungen können je nach Linux-Distribution oder Kernel-Version anders lauten. Ich beziehe mich hier auf Ubuntu Lucid.

Mögliches Postfix-Setup

Ich beschreibe ein mögliches Setup, doch auch hier gibt es zahlreiche Konfigurationsvarianten. Zu erwähnen bleibt, dass es allein bei Mail üblich ist, schon im DNS Gewichtungen über den MX Record zu verwenden. Dies ist also bereits eine Art DNS-Loadbalancing. Standardmäßig wird jeder einliefernde Mailserver mindestens bei drei verschiedenen MX-Records versuchen Mails einzuliefern, denn MX-Records mit gleicher Gewichtung sind nicht verboten.

Im Grunde braucht es zur Auslieferung von Mail auch nicht zwingend einen MX-Eintrag, sondern nur einen A-Eintrag, dies ist aber ein anderes Thema. In dem hier gezeigten Beispiel wird auf zwei Postfix-Server verteilt, dazu existieren also zwei reale IP-Adressen und eine virtuelle Loadbalancer-IP-Adresse.

Daraus ergibt sich, dass es am einfachsten ist, SMTP-Verbindungen auf die virtuelle Loadbalancer-IP-Adresse zu delegieren. Dies ist für Mailclients z.B. mit automx sehr elegant möglich. Allerdings wird man hier den SMTP Submission Port wählen. Die Delegation für Mailserver ergibt sich aus den MX-Prioritäten im DNS.

Ein möglicher MX-DNS-Eintrag könnte also wie folgt aussehen:

test.test.         86400   IN      MX      10 mail.tester.tester.
test.test.         86400   IN      MX      50 mail01.tester.tester.
test.test.         86400   IN      MX      50 mail02.tester.tester.

mail.tester.tester.   6214    IN      A       1.2.3.210
mail01.tester.tester. 9053    IN      A       1.2.3.205
mail02.tester.tester. 6958    IN      A       1.2.3.206

Daraus ableitend könnte man also auf den Postfix-Servern in main.cf folgendermaßen vorgehen:

inet_interfaces = 127.0.0.1, 1.2.3.205
smtp_bind_address = 1.2.3.205
inet_protocols = ipv4
smtp_address_preference = ipv4

-----------------------------------------------------------

inet_interfaces = 127.0.0.1, 1.2.3.206
smtp_bind_address = 1.2.3.206
inet_protocols = ipv4
smtp_address_preference = ipv4

Zusätzlich pflegt man die virtuelle Loadbalancing-IP-Adresse in /etc/postfix/master.cf ein:

1.2.3.210:smtp inet  n       -       n       -       -       smtpd
    -o myhostname=mail.tester.tester
    -o smtp_helo_name=mail.tester.tester

Dieses Postfix-Setup macht SMTP-Verbindungen auf allen genutzten IP-Adressen möglich. Die Mailserver selbst liefern aber nur über ihre realen IP-Adressen aus. Diese Lösung hat den Charme, dass man leicht auf den realen IP-Adressen Postscreen zur Spambot-Abwehr nutzen kann, was auf der virtuellen Loadbalancer-IP-Adresse nicht störungsfrei funktionieren würde, da es immer noch Mail-Clients gibt, die wie andere Mailserver den SMTP-Port benutzen, um Mail einzuliefern.

Außerdem ist ein häufig genutztes Spambot-Angriffsverfahren das Einliefern von Spam-Mails in der umgekehrten Reihenfolge der MX-Gewichtung – in der Hoffnung die Mailserver mit höherer MX-Priorität seien nicht so gut gesichert.

Dovecot-Setup

Ein Eintrag in Keepalived für IMAP würde z.B. so aussehen:

   virtual_server 1.2.3.210 143 {
   delay_loop 60
   lb_algo rr
   lb_kind DR
   protocol TCP
   persistence_timeout 360

   real_server 1.2.3.205 143 {
   weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 143
       nb_get_retry 3
       delay_before_retry 3
       }
   }
   real_server 1.2.3.206 143 {
       weight 1
       TCP_CHECK {
               connect_timeout 3
       connect_port 143
       nb_get_retry 3
       delay_before_retry 3
       }
   }
}

Darüber hinaus ist zu Dovecot an dieser Stelle nicht so viel zu sagen. Dovecot-Standard ist, sich an alle verfügbaren IP-Adressen zu binden; man kann diese aber auch einzeln konfigurieren.

Mehrere Dovecot-Server hinter Loadbalancing ergeben natürlich nur wirklich Sinn, wenn diese auf einen gemeinsamen Mailbox-Storage zugreifen können. Ich möchte hier nur zwei Möglichkeiten dazu erwähnen: zu einen ist das ein NFS-Storage, und zum andern ein Master/Master DRBD mit dem Cluster Filesystem Ocfs2.

Für beide Möglichkeiten lauten die vom Dovecot-Wiki zusätzlich empfohlenen Parameter im Wesentlichen:

mail_fsync = always
mail_nfs_storage = yes
mail_nfs_index = yes
mmap_disable = yes

Für NFS ist die Verwendung von Dovecot Directors zwingend erforderlich, für Drbd Master/Master mit Ocfs2 und Maildir ist dies nach meinen Recherchen und Langzeittests nicht unbedingt erforderlich. Es ist aber als grundsätzlich besseres Design anzusehen, Dovecot Directors auf den Loadbalancern zu verwenden. Es sollte immer zu besserer Gesamtperformance auch bei Cluster-Filesystemen führen.

Es gibt zahlreiche Anbieter von Loadbalancing-Software und -Hardware. Diese sind aber teilweise sehr teuer – ein gutes Argument, sich mit Keepalived zu beschäftigen. Es gibt aber auch einige Fallstricke zu beachten, z.B. bei Verwendung von weiteren internen Interfaces auf den nachgeordneten Servern. Für Cluster-Filesysteme muss die verwendete Software sich auch sicher an diese binden lassen, sonst kann es evtl. zu einem MAC-Adressen-Wirrwarr auf den Switches kommen. Deren Konfiguration muss also ebenfalls sorgfältig geplant sein.

Außerdem sind evtl. auch statische Host-Einträge nötig. Auch die DNS-Einträge sollten vor allem bei der Verwendung mit Mailservern wohl überdacht sein. Firewalls sind natürlich ein Thema – sie sollten Keepalived nicht behindern. Darüber hinaus ist Keepalived aber robust und relativ einfach zu handhaben.

Man sollte jedoch nicht verschweigen, dass es auch noch andere Methoden und freie Software-Projekte gibt, um Loadbalancing und Ausfallsicherheit herzustellen. Dazu werden sich meine Kollegen an dieser Stelle noch äußern.

Robert Schetterer, 10. June 2013