Ausbalanciertes CARP

Wie schon angekündigt, gibt es ein zweites Problem, das mit ARP in Zusammenhang steht. Hier stellt nicht das DSL-Netz das Problem dar, sondern eine Reihe von Carrier-Grade-NAT-Servern, die einen Großteil der Kunden mit öffentlicher Erreichbarkeit versorgen. Um die Last horizontal zu verteilen, d.h. einfach seitlich anbauen zu können, sollen sich alle Maschinen eine IP teilen.

Ausgangslage

Eine Gruppe von NAT-Servern soll sich eine IP-Adresse teilen. Die Clients sollen also alle das gleiche Default-Gateway benutzen und dann stabil mit definierten offiziellen IPs in die Welt kommunizieren.

Es ist notwendig, dass die Zuordnung zwischen Client und öffentlich sichtbarer IP weitestgehend konstant bleibt, denn nur so sind die folgenden Anforderungen an einen Internet-Zugang realisierbar:

  • Downloads und Streams laufen durch.
  • Sessions auf Webportalen bleiben nach initialer Anmeldung bestehen, man muss sich nicht während der Nutzung unvermittelt neu anmelden.
  • Längere Sitzungen (VPN, RDP oder SSH) laufen unterbrechungsfrei.
  • Verbindungen zum (IMAP)-Postfach bleiben bestehen.
  • Eingebundene Netzlaufwerke bleiben zur lokalen Arbeit verfügbar.
  • Online-Spiele laufen durch.

Für kürzere Aktionen, wie gelegentliches Webbrowsen oder Post-Abholen, sind die Anforderungen weit weniger streng. Dort reicht es, wenn für die Dauer der Verbindungen die öffentliche NAT Adresse stabil bleibt. Sie kann sogar pro Verbindung wechseln.

Beim Ausfall einer Maschine soll die Last auf die anderen Maschinen verteilt werden. Die vom Ausfall betroffenen Kunden sollen, höchstens eine kurze Unterbrechung bemerken.

Kanonische Umsetzung

Da sich alle Maschinen die gleiche IP teilen, muss die IP virtuell sein. Und da die ARP-Einträge in den Client ziemlich langlaufend sind, ist es notwendig für den Fall eines Ausfalls die MAC-Adresse umzuziehen. Es sind also auch noch virtuelle MAC-Adressen einzusetzen, die nach Bedarf zwischen den physischen Maschinen getauscht werden können.

Im einfachsten Fall gibt es zwei System: Ein aktives, dass die betreffende virtuelle IP und MAC Adresse bedient. Bei einem Ausfall übernimmt das passive Reserve-System die IP und die MAC. Das aktive System wird damit passiv. Dieses Szenario bietet aber keine Lastverteilung.

Um eine Lastverteilung hinzubekommen, müssen verschiedene Clients offenbar mit verschiedenen Servern reden. Da sie aber nur eine MAC Adresse per ARP lernen können und die Server sich nicht eine MAC Adresse teilen können (wer bekommt denn dann das betreffende Paket?), müssen zwangsweise mehrere virtuelle MAC Adressen zum Einsatz kommen. Jeder Client lernt dann eine andere virtuelle MAC Adresse und spricht dann ausschließlich mit der Maschine, die diese Adresse gerade bedient.

Bei einem Ausfall muss die MAC-Adresse des betroffenen Systems auf andere Server umziehen. Die Verteilung der anderen virtuellen MAC-Adressen ist von diesem Ausfall aber nicht betroffen. Deswegen ist es sinnvoll, pro MAC Adresse ein getrenntes Failover einzusetzen.

Aus der Cisco Welt sind diese Methoden bekannt:

  • HSRP stellt die Aktiv/Passiv-Umschaltung zwischen zwei Systemen bereit.
  • VRRP ist die nicht-proprietäre Alternative zu HSRP, die dieses auch auf mehrere Systeme ausweitete (was HSRP später übernahm).
  • GLBP ist ein Erweiterung von HSRP auf mehrere MAC-Adressen, die mehrere aktive Geräte – also Lastverteilung – ermöglicht.

Da alle diese Methoden patentbehaftet sind, hat die BSD Welt CARP entwickelt. Und dort sieht es sehr gut für mich aus:

The carp has limited abilities for load balancing the incoming connections between
hosts in Ethernet network.  For load balancing operation, one needs several CARP
interfaces that are configured to the same IP address, but to a different VHIDs.
Once an ARP request is received, the CARP protocol will use a hashing function
against the source IP address  in the ARP request to determine which VHID should
this request belong to.

Damit ist schon gleich verraten, wie es funktioniert:

  • Die IP Adresse des Clients entscheidet darüber, welche virtuelle MAC er zu sehen bekommt.
  • Da er sich dann immer mit dieser MAC verbindet, landet er immer auf dem gleichen Server.
  • Der Server kann nun sicher stellen, dass der Client für alle NAT-Verbindungen die gleiche öffentliche IP benutzt.
  • Er benötigt dafür nur lokale öffentliche IPs. Der NAT State muss nicht zwischen den Maschinen geteilt werden.

Dieses Konzept skaliert prima.

In Praxis sieht das dann so aus:

cloned_interfaces="${cloned_interfaces} carp0 carp1 carp2 carp3"
ifconfig_carp0="vhid 1 advskew 133 pass xxx 100.100.0.1/17"
ifconfig_carp1="vhid 2 advskew 166 pass yyy 100.100.0.1/17"
ifconfig_carp2="vhid 3 advskew 200 pass zzz 100.100.0.1/17"
ifconfig_carp3="vhid 4 advskew 100 pass aaa 100.100.0.1/17" 

Es gibt also vier CARP Interfaces, die sich die gleiche IP teilen. Der CARP Algorithmus sorgt dafür, dass jeder Client einer anderen VHID zugeordnet wird und damit stabil auf einem der Server landet. Es ist immer der Server aktiv, der den kleinsten advskew Wert vorweisen kann. Und die unterscheiden sich pro Maschine.

Kommt eine neue Maschine hinzu, kommt überall ein weiteres CARP Interface hinzu.

Und wir macht man das mit dem statischen NAT? Man nimmt wieder die Client IP zu Hilfe:

nat 1 config ip a.b.c.d same_ports
nat 2 config ip a.b.c.e same_ports
...
add 2001 nat   1 ipv4 from 100.64.0.0:255.192.0.63 to any
add 2002 nat   2 ipv4 from 100.64.0.1:255.192.0.63 to any
...

Das war's.

Updates

Mittlerweile sind die neueren Systeme auf 10.2-STABLE: Dort fehlt in der Man-Page der betreffende Absatz zu ARP Balance.

Die Architektur der CARP Implementierung hat sich geändert. CARP ist nun kein Pseudointerface mehr, sondern hängt direkt am physischen Interface.

Und das hat fatale Konsequenzen:

  • Es ist nicht mehr möglich, mehrere CARP Instanzen pro IP zu haben.
  • Der Code, der die virtuelle MAC anhand der Client IP klassifizierte, existiert nicht mehr.

In der Praxis sieht das dann so aus:

# ifconfig vlan500 inet 192.0.2.1/24
# ifconfig vlan500 | fgrep 192
        inet 192.0.2.1 netmask 0xffffff00 broadcast 192.0.2.255

# ifconfig vlan500 inet 192.0.2.10/32 vhid 10 alias
# ifconfig vlan500 | fgrep 192
        inet 192.0.2.1 netmask 0xffffff00 broadcast 192.0.2.255
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 10

# ifconfig vlan500 inet 192.0.2.10/32 vhid 11 alias
# ifconfig vlan500 | fgrep 192
        inet 192.0.2.1 netmask 0xffffff00 broadcast 192.0.2.255
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 11

Es wurde also keine zweite CARP-Instanz zur gleichen IP hinzugefügt, sondern die Parameter der IP geändert.

Der Grund für dieses Verhalten liegt im Adressmanagement von Interfaces. Es ist nicht möglich an einem Interface zweimal die gleiche IP zu haben!

Beim alten Ansatz mit separaten Pseudointerfaces trat das Problem nicht auf: Jedes Interface hat seinen eigenen IP-Raum.

Mit verschiedenen IPs ist es allerdings kein Problem, mehrere CARP Instanzen am Interface zu halten.

# ifconfig vlan500 inet 192.0.2.10/32 vhid 10 alias
# ifconfig vlan500 inet 192.0.2.11/32 vhid 11 alias
# ifconfig vlan500 | fgrep 192
        inet 192.0.2.1 netmask 0xffffff00 broadcast 192.0.2.255
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 10
        inet 192.0.2.11 netmask 0xffffffff broadcast 192.0.2.11 vhid 11

Ein Blick in den Sourcecode offenbart, dass der neue CARP-Code jedes Instanz als alleinstehend betrachtet.

Das bedeutet, dass die folgende Fehlkonfiguration zu unerwartetem Verhalten führt:

# ifconfig vlan500 inet 192.0.2.10/32 vhid 11 alias
# ifconfig vlan500 | fgrep 192
        inet 192.0.2.1 netmask 0xffffff00 broadcast 192.0.2.255
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 11
        inet 192.0.2.11 netmask 0xffffffff broadcast 192.0.2.11 vhid 11

Der betreffende Server könnte dann mehrfach Announcements für die VHID 11 herausgeben. Diese Announcements könnten sich widersprechen und zu einem permanenten Flappen der Interfaces führen.

Tatsächlich passiert dies aber nicht, wie ein Blick in den Source und in die Ausgabe von ifconfig lehrt.

# ifconfig vlan500 | fgrep vhid
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 11
        inet 192.0.2.11 netmask 0xffffffff broadcast 192.0.2.11 vhid 11
        carp: MASTER vhid 11 advbase 1 advskew 100

# ifconfig vlan500 inet 192.0.2.10/32 vhid 10 advskew 10 alias
# ifconfig vlan500 | fgrep vhid
        inet 192.0.2.10 netmask 0xffffffff broadcast 192.0.2.10 vhid 10
        inet 192.0.2.11 netmask 0xffffffff broadcast 192.0.2.11 vhid 11
        carp: MASTER vhid 11 advbase 1 advskew 100
        carp: MASTER vhid 10 advbase 1 advskew 10

Es gibt also pro VHID eine CARP Instanz am Interface, das ein oder mehrere verschiedene IPs tragen kann.

Ratlos

Was nun? Der bisherige Ansatz ist nicht mehr möglich. Alte und neue System kooperieren nicht mehr, sie können nicht mehr im gleichen Netz betrieben werden.

Was ist eigentlich der korrekte Stand der Dinge?

  • Es gibt mehrere CARP-Instanzen, d.h. mehrere virtuelle MAC Adressen.
  • Der Automatismus der ARP-Verteilung anhand der Client-IP ist kaputt.

Wieder steht die Frage: Wie kann man korrekte ARP Antworten schicken?

Post a comment

Related content