Frage nginx reverse proxy - versuchen Sie upstream A, dann B, dann wieder A


Ich versuche, Nginx als Reverse-Proxy mit einer großen Anzahl von Back-End-Servern einzurichten. Ich möchte die Back-Ends on-demand starten (auf der ersten Anfrage, die hereinkommt), also habe ich einen Kontrollprozess (gesteuert durch HTTP-Anfragen), der das Backend startet, abhängig von der Anfrage, die es empfängt.

Mein Problem ist nginx zu konfigurieren, um es zu tun. Folgendes habe ich bisher:

server {
    listen 80;
    server_name $DOMAINS;

    location / {
        # redirect to named location
        #error_page 418 = @backend;
        #return 418; # doesn't work - error_page doesn't work after redirect

        try_files /nonexisting-file @backend;
    }

    location @backend {
        proxy_pass http://$BACKEND-IP;
        error_page 502 @handle_502; # Backend server down? Try to start it
    }

    location @handle_502 { # What to do when the backend server is not up
        # Ping our control server to start the backend
        proxy_pass http://127.0.0.1:82;
        # Look at the status codes returned from control server
        proxy_intercept_errors on;
        # Fallback to error page if control server is down
        error_page 502 /fatal_error.html;
        # Fallback to error page if control server ran into an error
        error_page 503 /fatal_error.html;
        # Control server started backend successfully, retry the backend
        # Let's use HTTP 451 to communicate a successful backend startup
        error_page 451 @backend;
    }

    location = /fatal_error.html {
        # Error page shown when control server is down too
        root /home/nginx/www;
        internal;
    }
}

Dies funktioniert nicht - nginx scheint alle Statuscodes zu ignorieren, die vom Kontrollserver zurückgegeben werden. Keiner dieser error_page Richtlinien in der @handle_502 Standortarbeit, und der 451-Code wird unverändert an den Client gesendet.

Ich habe es aufgegeben, die interne nginx-Umleitung dafür zu verwenden, und habe versucht, den Steuerungsserver so zu modifizieren, dass er eine 307-Umleitung an den gleichen Ort sendet (so dass der Client dieselbe Anfrage wiederholt, aber jetzt mit dem Backend-Server gestartet wird). Nun überschreibt nginx jedoch den Statuscode dummerweise mit dem, den er vom Backend-Anforderungsversuch erhalten hat (502), obwohl der Kontrollserver einen "Standort" -Header sendet. Ich habe es schließlich "funktioniert", indem ich die Zeile error_page zu ändern error_page 502 =307 @handle_502;Dadurch werden alle Antworten des Steuerservers mit einem 307-Code zurück an den Client gesendet. Dies ist sehr hacky und unerwünscht, weil 1) es keine Kontrolle darüber gibt, was nginx als nächstes tun sollte, abhängig von der Antwort des Kontrollservers (idealerweise wollen wir das Backend nur wiederholen, wenn der Kontrollserver Erfolg meldet) und 2) nicht alle HTTP Clients unterstützen HTTP-Weiterleitungen (zB müssen curl-Benutzer und libcurl-verwendende Anwendungen die folgenden Weiterleitungen explizit aktivieren).

Was ist der richtige Weg, um Nginx zu versuchen, Proxy Server A, dann B, dann wieder A (im Idealfall nur, wenn B einen bestimmten Statuscode zurückgibt)?


21
2017-09-08 11:59


Ursprung




Antworten:


Schlüsselpunkte:

  • Mach dir keine Sorgen mit upstream Blöcke für Failover, wenn das Pingen eines Servers einen anderen bringt - es gibt keine Möglichkeit, nginx (zumindest nicht die FOSS-Version) mitzuteilen, dass der erste Server wieder aktiv ist. Nginx wird die Server in der Reihenfolge der ersten Anfrage versuchen, aber keine Folgeanfragen, trotz irgendwelcher backup, weight oder fail_timeout die Einstellungen.
  • Sie Muss aktivieren recursive_error_pages bei der Implementierung von Failover mit error_page und benannte Orte.
  • Aktivieren proxy_intercept_errors um Fehlercodes zu verarbeiten, die vom Upstream-Server gesendet wurden.
  • Das = Syntax (z.B. error_page 502 = @handle_502;) wird benötigt, um Fehlercodes am angegebenen Ort korrekt zu behandeln. Ob = Wird nicht verwendet, verwendet nginx den Fehlercode des vorherigen Blocks.

Original Antwort / Forschungsprotokoll folgen:


Hier ist ein besser Workaround, den ich gefunden habe, was eine Verbesserung darstellt, da keine Clientumleitung erforderlich ist:

upstream aba {
    server $BACKEND-IP;
    server 127.0.0.1:82 backup;
    server $BACKEND-IP  backup;
}

...

location / {
    proxy_pass http://aba;
    proxy_next_upstream error http_502;
}

Dann bringen Sie den Kontrollserver 502 auf "Erfolg" zurück und hoffen, dass der Code von den Backends nie zurückgegeben wird.


Update: nginx markiert weiterhin den ersten Eintrag in der upstream Block als "down", so dass die Server bei aufeinanderfolgenden Anfragen nicht nacheinander abgefragt werden. Ich habe versucht, hinzuzufügen weight=1000000000 fail_timeout=1 zum ersten Eintrag ohne Wirkung. Bisher habe ich keine Lösung gefunden, die keine Client-Weiterleitung beinhaltet.


Edit: Eine weitere Sache, die ich wünschte, ich wüsste - um den Fehlerstatus von der error_page Handler, benutze diese Syntax: error_page 502 = @handle_502; - Dieses Gleichheitszeichen bewirkt, dass nginx den Fehlerstatus vom Handler erhält.


Edit: Und ich habe es funktioniert! In Ergänzung zu error_page Fix oben, alles, was benötigt wurde, war es zu ermöglichen recursive_error_pages!


16
2017-09-08 12:17



Für mich ist das proxy_next_upstream Hat den Trick gemacht (mein Szenario war nicht so komplex wie deines), ich wollte nur, dass nginx den nächsten Server ausprobiert, wenn ein Fehler auftrat, also musste ich hinzufügen proxy_next_upstream error timeout invalid_header non_idempotent; (non_idempotent, weil ich hauptsächlich weiterleiten möchte POST Anfragen). - Philipp


Sie könnten etwas wie das folgende versuchen

upstream backend {
    server a.example.net;
    server b.example.net backup;
}

server {
    listen   80;
    server_name www.example.net;

    proxy_next_upstream error timeout http_502;

    location / {
        proxy_pass http://backend;
        proxy_redirect      off;
        proxy_set_header    Host              $host;
        proxy_set_header    X-Real-IP         $remote_addr;
        proxy_set_header    X-Forwarded-for   $remote_addr;
    }

}

1
2017-09-08 12:22



Nginx wird nicht erneut versuchen a.example.net nachdem es einmal auf dieselbe Anfrage gescheitert ist. Er sendet den Fehler an den Client, der beim Herstellen einer Verbindung auftritt b.example.net, was nicht das ist, was sie erwartet haben, es sei denn, ich würde das Proxying auch auf dem Kontrollserver implementieren. - Vladimir Panteleev
Und was wäre mit Ihrer Konfiguration in der nächsten Situation: Anfrage an die Upstream Ein Return Fail, Upstream B Return Fail, dann versuchen wir wieder upstream A und auch fail (502)? - ALex_hha
Upstream B ist der Kontrollserver. Sein Zweck besteht darin, sicherzustellen, dass die nächste Anfrage an Upstream A erfolgreich sein wird. Das Ziel ist es, Upstream A zu versuchen, wenn es versagt, versuchen Upstream B, wenn es "erfolgreich" (unter Verwendung unserer internen Konvention von "Erfolg"), versuchen Sie upstream A erneut. Wenn meine Frage nicht klar genug war, lass mich wissen, wie ich es verbessern kann. - Vladimir Panteleev
Hmm, nehmen wir an, Upstream A ist down, für z.B. etwas Hardware-Problem. Was wird Upstream B machen? Ist es in der Lage, eine Antwort auf die Anfrage des Kunden zurückzugeben? - ALex_hha
Bei diesem Problem handelt es sich nicht um Failover für Hardwarefehler. Dieses Problem besteht darin, Upstream-Back-Ends auf Anforderung zu starten. Wenn der Steuerungsserver (vor B) das Backend nicht aktivieren kann (vor A), sollte der Benutzer idealerweise eine entsprechende Fehlermeldung erhalten, aber das ist nicht das Problem, das ich lösen möchte - das Problem ist, dass nginx versucht, es erneut zu versuchen A nach B nochmal, in derselben Anfrage. - Vladimir Panteleev