Wie ein Backend-Neustart für Ihre Besucher unsichtbar wird
Zwei Webserver zeigen noch Fehler, wenn einer neu startet. Die Einstellungen in HAProxy, Varnish und keepalived, die einen Neustart unsichtbar machen, und wie Sie es testen.

Zwei Webserver hinter einem Load Balancer zu haben bedeutet nicht, dass ein Neustart unsichtbar ist. Es bedeutet, dass ein Neustart einen kurzen Schwall von Fehlern erzeugt statt eines Ausfalls. Der Unterschied zwischen «kurz» und «keine» liegt in ein paar Einstellungen am Edge, und darin, sie mit echtem Traffic zu testen, bevor Sie sich darauf verlassen. So schließen wir diese Lücke.
Warum Redundanz allein noch Fehler zeigt
Wenn PHP-FPM oder nginx auf einem Web-Knoten neu startet, passieren in der einen oder den zwei Sekunden, die das dauert, drei Dinge. Anfragen, die auf diesem Knoten bereits in Bearbeitung sind, werden abgeschnitten. Neue Anfragen treffen weiter ein, weil der Load Balancer noch nicht bemerkt hat, dass der Knoten weg ist. Und wenn der Knoten zurückkommt, bekommt er möglicherweise seinen vollen Anteil am Traffic, bevor seine Caches und sein Opcode-Cache warm sind.
Ein Load Balancer mit Standardeinstellungen behandelt keinen dieser Fälle elegant. Sein Health Check läuft alle paar Sekunden und braucht mehrere Fehlschläge, bevor er einen Knoten als down markiert, also schickt er in diesem Fenster bewusst Besucher an einen Server, der nicht antwortet. Jede dieser Anfragen wird zu einem 502 oder 503 auf jemandes Bildschirm.
Retry und Redispatch am Edge
Die erste Korrektur besteht darin, HAProxy es woanders noch einmal versuchen zu lassen, wenn eine Verbindung fehlschlägt:
defaults
mode http
retries 3
option redispatch 1
retry-on conn-failure empty-response response-timeout
timeout connect 3s
retries erlaubt HAProxy, eine Anfrage zu wiederholen, deren Verbindung fehlgeschlagen ist. option redispatch 1 lässt die Wiederholung an einen anderen Server gehen, statt auf den einzuhämmern, der gerade abgelehnt hat. retry-on erweitert Wiederholungen über abgelehnte Verbindungen hinaus auf leere Antworten und Timeouts, also genau das, was ein Prozess mitten im Neustart oft erzeugt.
Wiederholungen sind sicher für Anfragen, die nicht verarbeitet wurden. Bei POST-Anfragen, die die Anwendung erreicht haben könnten, ist HAProxy standardmäßig konservativ, und das wollen Sie auch: Ein Checkout darf nie zweimal abgeschickt werden.
Health Checks, die die Anwendung sehen, nicht den Port
Ein TCP-Check besteht, solange nginx lauscht, selbst wenn jeder PHP-Worker festhängt. Prüfen Sie etwas, das den Stack beansprucht:
backend be_web
option httpchk GET /healthz
http-check expect status 200
default-server inter 2s fall 3 rise 2 slowstart 20s
server web1 <web1>:80 check
server web2 <web2>:80 check
Der Endpoint /healthz sollte ein winziges PHP-Skript sein, das berührt, was zählt (eine Datenbankverbindung öffnet, einen Cache-Schlüssel liest) und schnell zurückkehrt. fall 3 bedeutet, dass ein einzelner langsamer Check während einer Log-Rotation oder eines Reloads keinen gesunden Knoten hinauswirft. rise 2 hält einen Knoten draußen, bis er zweimal geantwortet hat. slowstart fährt einen zurückkehrenden Server schrittweise hoch, damit ein Knoten mit kalten Caches nicht auf einen Schlag den vollen Anteil am Traffic bekommt.
Leeren, bevor Sie neu starten
Der sauberste Neustart ist einer, von dem der Load Balancer vorher weiß. Das Runtime-API von HAProxy erlaubt es, einen Knoten aus der Rotation zu nehmen, ohne bestehende Anfragen fallen zu lassen:
echo "set server be_web/web1 state drain" | socat stdio /run/haproxy/admin.sock
# wait for current sessions to finish
echo "show servers state be_web" | socat stdio /run/haproxy/admin.sock
systemctl restart php-fpm # on web1
echo "set server be_web/web1 state ready" | socat stdio /run/haproxy/admin.sock
Packen Sie das in ein Skript, und jeder geplante Neustart (ein PHP-Sicherheitsupdate, eine Konfigurationsänderung, ein Kernel-Reboot) wird zu Leeren, Warten, Neustarten, Verifizieren, Zurückholen, ein Knoten nach dem anderen.
Den Cache die Lücke abdecken lassen
Ein Full-Page-Cache tut mehr, als anonymen Traffic zu beschleunigen. Mit konfigurierter Grace liefert Varnish weiter die letzte gute Kopie einer Seite aus, wenn er keine frische holen kann:
import std;
sub vcl_recv {
if (std.healthy(req.backend_hint)) {
set req.grace = 10s;
}
}
sub vcl_backend_response {
set beresp.ttl = 5m;
set beresp.grace = 24h;
}
Solange die Backends gesund sind, werden Objekte innerhalb von Sekunden nach Ablauf aufgefrischt. Wenn jedes Backend ungesund ist, darf Varnish Objekte bis zu einen Tag über ihre TTL hinaus ausliefern. Leser bekommen während einer Datenbankwartung oder eines festhängenden PHP-Pools weiter Seiten, und die Pfade für angemeldete Benutzer und den Checkout, die den Cache absichtlich umgehen, sind die einzigen, die davon abhängen, dass die Backends laufen.
Das Edge selbst braucht einen Partner
Zwei Edge-Knoten mit keepalived teilen sich Floating-Adressen, und ein Tracking-Skript stuft ein Edge herab, dessen HAProxy gestoppt ist:
vrrp_script chk_haproxy {
script "/usr/sbin/pidof haproxy"
interval 2
fall 2
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
track_script { chk_haproxy }
virtual_ipaddress { <floating address>/<prefix> }
}
Wir bauen Edges über Kreuz: Jeder Knoten ist Primary für eine öffentliche Adresse und Standby für die andere, und der DNS-Eintrag trägt beide. Beide Edges führen die ganze Zeit Traffic, und ein Ausfall verschiebt eine Adresse innerhalb von Sekunden auf den Überlebenden.
Mit Traffic beweisen, nicht mit einem Gefühl
Ein Neustart ist nur dann unsichtbar, wenn Sie ihm dabei zugesehen haben, unsichtbar zu sein. Der Test, den wir vor der Übergabe fahren, ist einfach:
- Legen Sie von außen gleichmäßige Last auf die Site, mit einer Mischung aus gecachten und ungecachten URLs. Ein kleiner Lastgenerator oder eine curl-Schleife reicht.
- Zählen Sie die Antworten nach Statuscode am Client, mit Zeitstempel.
- Starten Sie PHP-FPM auf einem Web-Knoten neu. Dann auf dem anderen. Dann lassen Sie ein Edge ausfallen.
- Vergleichen Sie die Zählung auf Client-Seite mit den Origin-Logs.
while true; do
printf '%s ' "$(date +%T.%N)"
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://www.example.com/
sleep 0.2
done | tee restart-test.log
grep -vc ' 200 ' restart-test.log
Der Origin protokolliert bei jedem Neustart üblicherweise eine Handvoll Fehler: Das ist die Lücke, die das Edge auffangen soll. Was zählt, ist die Zahl auf Client-Seite. Wenn Retry, Redispatch und Grace funktionieren, bleibt die Besucherzählung bei null, während der Origin seine kurze Neustartlücke zeigt. Falls doch Fehler den Client erreichen, sagen Ihnen die Zeitstempel, welche Ebene sie durchgelassen hat.
Ihre Checkliste
- HAProxy wiederholt mit Redispatch, und
retry-ondeckt leere Antworten und Timeouts ab. - Ein HTTP-Health-Check, der PHP und die Datenbank berührt, mit
fallundrisegegen Flapping abgestimmt. - Geplante Neustarts, die zuerst leeren, über das Runtime-API.
- Varnish-Grace lang genug, um Ihre längste Backend-Wartung abzudecken.
- Zwei Edges mit Floating-Adressen und einem Tracking-Skript, beide mit Traffic.
- Ein Neustart-Test unter Last, mit notierter Fehlerzahl auf Client-Seite.
Wenn Sie Hilfe möchten, einen Stack so abzustimmen, dass seine Neustarts verschwinden: Wir machen das jede Woche.
CTA
Related
Verwandt: Hochverfügbare Web-Tiers, N+1 auf jeder Ebene · Object Caching mit automatischem Failover · Traffic-Steuerung am Edge für Crawler und Scraper
Veröffentlicht am 27. August 2026.
Alle Notizen aus der Praxis