it-swarm.com.de

HAProxy ordnungsgemäßes Neuladen ohne Paketverlust

Ich verwende einen HAProxy-Lastausgleichsserver, um die Last auf mehrere Apache-Server zu verteilen. Ich muss HAProxy jederzeit neu laden, um den Algorithmus für den Lastausgleich zu ändern.

Dies alles funktioniert einwandfrei, mit Ausnahme der Tatsache, dass ich den Server neu laden muss, ohne ein einzelnes Paket zu verlieren (im Moment bringt mir ein Neuladen durchschnittlich 99,76% Erfolg, mit 1000 Anfragen pro Sekunde für 5 Sekunden). Ich habe viele Stunden lang darüber recherchiert und den folgenden Befehl zum "ordnungsgemäßen Neuladen" des HAProxy-Servers gefunden:

haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)

Dies hat jedoch wenig oder keine Auswirkung gegenüber dem einfachen alten service haproxy reload, es fällt im Durchschnitt immer noch um 0,24%.

Gibt es eine Möglichkeit, die HAProxy-Konfigurationsdatei ohne ein einziges verworfenes Paket eines Benutzers neu zu laden?

42
Conor Taylor

Laut https://github.com/aws/opsworks-cookbooks/pull/4 und folglich http://www.mail-archive.com/[email protected]/ msg06885.html Sie können:

iptables -I INPUT -p tcp --dport $PORT --syn -j DROP
sleep 1
service haproxy restart
iptables -D INPUT -p tcp --dport $PORT --syn -j DROP

Dies hat zur Folge, dass die SYN vor einem Neustart gelöscht wird, sodass Clients diese SYN erneut senden, bis sie den neuen Prozess erreicht.

32
Mxx

Yelp teilte einen differenzierteren Ansatz, der auf sorgfältigen Tests beruhte. Der Blog-Artikel ist ein tiefer Tauchgang und es lohnt sich, ihn zu investieren, um ihn voll und ganz zu würdigen.

True Zero Downtime HAProxy Reloads

verwenden Sie Linux tc (Verkehrssteuerung) und iptables, um SYN-Pakete vorübergehend in die Warteschlange zu stellen, während HAProxy neu lädt und zwei Pids an denselben Port angeschlossen sind (SO_REUSEPORT).

Es ist mir unangenehm, den gesamten Artikel auf ServerFault erneut zu veröffentlichen. Dennoch, hier sind einige Auszüge, um Ihr Interesse zu wecken:

Durch die Verzögerung von SYN-Paketen, die in unsere HAProxy-Load-Balancer eingehen, die auf jedem Computer ausgeführt werden, können wir den Datenverkehr während des HAProxy-Neuladens minimal beeinträchtigen. Dadurch können wir Service-Backends in unserem SOA) hinzufügen, entfernen und ändern = ohne Angst vor einer signifikanten Beeinträchtigung des Benutzerverkehrs.

# plug_manipulation.sh
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --buffer
service haproxy reload
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --release-indefinite

# setup_iptables.sh
iptables -t mangle -I OUTPUT -p tcp -s 169.254.255.254 --syn -j MARK --set-mark 1

# setup_qdisc.sh
## Set up the queuing discipline
tc qdisc add dev lo root handle 1: prio bands 4
tc qdisc add dev lo parent 1:1 handle 10: pfifo limit 1000
tc qdisc add dev lo parent 1:2 handle 20: pfifo limit 1000
tc qdisc add dev lo parent 1:3 handle 30: pfifo limit 1000

## Create a plug qdisc with 1 meg of buffer
nl-qdisc-add --dev=lo --parent=1:4 --id=40: plug --limit 1048576
## Release the plug
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --release-indefinite

## Set up the filter, any packet marked with “1” will be
## directed to the plug
tc filter add dev lo protocol ip parent 1:0 prio 1 handle 1 fw classid 1:4

Inhalt: https://Gist.github.com/jolynch/97e3505a1e92e35de2c

Ein Hoch auf Yelp für den Austausch dieser erstaunlichen Erkenntnisse.

27
Steve Jansen

Es gibt eine andere, viel einfachere Möglichkeit, Haproxy ohne Ausfallzeit neu zu laden - es heißt iptables flipping (der Artikel ist eigentlich Unbounce-Antwort auf Yelp-Lösung). Es ist sauberer als die akzeptierte Antwort, da keine Pakete verworfen werden müssen, die Probleme beim langen Nachladen verursachen können.

Kurz gesagt besteht die Lösung aus den folgenden Schritten:

  1. Lassen Sie uns zwei Haproxy-Instanzen haben - die erste aktive, die einen Datenverkehr empfängt, und die zweite im Standby-Modus, die keinen Datenverkehr empfängt.
  2. Sie können die Standby-Instanz jederzeit neu konfigurieren (neu laden).
  3. Sobald der Standby-Modus mit der neuen Konfiguration fertig ist, leiten Sie alle NEUEN Verbindungen zum Standby-Knoten um, der neu aktiv wird . Unbounce bietet Bash-Skript, das den Flip mit wenigen einfachen iptable -Befehlen ausführt .
  4. Für einen Moment haben Sie zwei aktive Instanzen. Sie müssen warten, bis die geöffneten Verbindungen zu alten aktiven beendet werden. Die Zeit hängt von Ihrem Serviceverhalten und den Keep-Alive-Einstellungen ab.
  5. Der Verkehr zu alten aktiven Stopps wird zu neuem Standby - Sie sind wieder da Schritt 1.

Darüber hinaus kann die Lösung auf jede Art von Dienst (Nginx, Apache usw.) angewendet werden und ist fehlertoleranter, da Sie die Standby-Konfiguration testen können, bevor sie online geht.

8
gertas

Bearbeiten: Bei meiner Antwort wird davon ausgegangen, dass der Kernel nur Datenverkehr an den zuletzt mit SO_REUSEPORT zu öffnenden Port sendet, während er tatsächlich Datenverkehr an alle Prozesse sendet, wie in einem der Kommentare beschrieben. Mit anderen Worten, der Iptables-Tanz ist weiterhin erforderlich. : (

Wenn Sie sich auf einem Kernel befinden, der SO_REUSEPORT unterstützt, sollte dieses Problem nicht auftreten.

Der Prozess, den Haproxy beim Neustart ausführt, ist:

1) Versuchen Sie, SO_REUSEPORT beim Öffnen des Ports festzulegen ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798 )

2) Versuchen Sie, den Port zu öffnen (wird mit SO_REUSEPORT erfolgreich sein)

3) Wenn dies nicht erfolgreich war, signalisieren Sie dem alten Prozess, seinen Port zu schließen, warten Sie 10 ms und versuchen Sie alles erneut. ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/haproxy.c#L1554-L1577 )

Es wurde zuerst im Linux 3.9-Kernel unterstützt, aber einige Distributionen haben es zurückportiert. Zum Beispiel unterstützen EL6-Kernel von 2.6.32-417.el6 dies.

4
Jason Stubbs

Ich werde mein Setup erklären und wie ich die anmutigen Nachladungen gelöst habe:

Ich habe ein typisches Setup mit 2 Knoten, auf denen HAproxy ausgeführt und Keepalived ausgeführt wird. Keepalived verfolgt die Schnittstelle dummy0, sodass ich einen "ifconfig dummy0 down" ausführen kann, um das Umschalten zu erzwingen.

Das eigentliche Problem ist, dass, ich weiß nicht warum, ein "Haproxy-Reload" immer noch alle ESTABLISHED-Verbindungen trennt :( Ich habe das von gertas vorgeschlagene "iptables flipping" versucht, aber ich habe einige Probleme gefunden, weil es ein NAT auf der Ziel-IP-Adresse, was in einigen Szenarien keine geeignete Lösung ist.

Stattdessen habe ich beschlossen, einen CONNMARK-Dirty-Hack zu verwenden, um Pakete zu markieren, die zu NEUEN Verbindungen gehören, und diese markierten Pakete dann an den anderen Knoten umzuleiten.

Hier ist der Regelsatz von iptables:

iptables -t mangle -A PREROUTING -i eth1 -d 123.123.123.123/32 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP

Die ersten beiden Regeln markieren die Pakete, die zu den neuen Flows gehören (123.123.123.123 ist das Keepalived VIP, das auf dem Haproxy zum Binden der Frontends verwendet wird).

Die dritte und vierte Regel markieren Pakete mit FIN/RST-Paketen. (Ich weiß nicht warum, TEE-Ziel "ignoriert" FIN/RST-Pakete).

Die fünfte Regel sendet ein Duplikat aller markierten Pakete an den anderen HAproxy (192.168.0.2).

Die sechste Regel verwirft Pakete, die zu neuen Flows gehören, um zu verhindern, dass ihr ursprüngliches Ziel erreicht wird.

Denken Sie daran, rp_filter auf Schnittstellen zu deaktivieren, da sonst der Kernel diese Mars-Pakete verwirft.

Und zu guter Letzt beachten Sie die zurückgesandten Pakete! In meinem Fall gibt es ein asymmetrisches Routing (Anfragen kommen an Client -> Haprox11 -> Haprox22 -> Webserver und Antworten gehen von Webserver -> Haprox11 -> Client), aber es hat keine Auswirkungen. Es funktioniert gut.

Ich weiß, dass die eleganteste Lösung darin besteht, iproute2 für die Umleitung zu verwenden, aber es hat nur für das erste SYN-Paket funktioniert. Als es das ACK (3. Paket des 3-Wege-Handshakes) erhielt, wurde es nicht markiert :( Ich konnte nicht viel Zeit für Nachforschungen aufwenden, sobald ich sah, dass es mit dem TEE-Ziel funktioniert, ließ es es dort. Probieren Sie es natürlich mit iproute2 aus.

Grundsätzlich funktioniert das "anmutige Nachladen" folgendermaßen:

  1. Ich aktiviere den iptables-Regelsatz und sehe sofort die neuen Verbindungen zum anderen HAproxy.
  2. Ich behalte "netstat -an | grep ESTABLISHED | wc -l" im Auge, um den "Entleerungsprozess" zu überwachen.
  3. Sobald nur noch wenige (oder keine) Verbindungen vorhanden sind, "ifconfig dummy0 down", um das Failover von Keepalived zu erzwingen, sodass der gesamte Datenverkehr an den anderen HAproxy weitergeleitet wird.
  4. Ich entferne den Regelsatz iptables
  5. (Nur für "non-preempting" keepalive config) "ifconfig dummy0 up".

Der IPtables-Regelsatz kann einfach in ein Start/Stopp-Skript integriert werden:

#!/bin/sh

case $1 in
start)
        echo Redirection for new sessions is enabled

#       echo 0 > /proc/sys/net/ipv4/tcp_fwmark_accept
        for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
        iptables -t mangle -A PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
        iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
        iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
        iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
        iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
        iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
        ;;
stop)
        iptables -t mangle -D PREROUTING -i eth1 -m mark --mark 1 -j DROP
        iptables -t mangle -D PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
        iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
        iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
        iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark
        iptables -t mangle -D PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1

        echo Redirection for new sessions is disabled
        ;;
esac
2
Vins Vilaplana