Nginx Reverse Proxy - Versuchen Sie Upstream A, dann B, dann erneut A

Nginx Reverse Proxy - Versuchen Sie Upstream A, dann B, dann erneut A

Ich versuche, nginx als Reverse-Proxy mit einer großen Anzahl von Backend-Servern einzurichten. Ich möchte die Backends bei Bedarf starten (bei der ersten eingehenden Anfrage), daher habe ich einen Steuerungsprozess (gesteuert durch HTTP-Anfragen), der das Backend je nach eingehender Anfrage startet.

Mein Problem ist, nginx so zu konfigurieren, dass es funktioniert. Das habe ich bisher erreicht:

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;
    }
}

Das funktioniert nicht – nginx scheint alle vom Kontrollserver zurückgegebenen Statuscodes zu ignorieren. Keine der error_pageAnweisungen am @handle_502Standort funktioniert und der Code 451 wird unverändert an den Client gesendet.

Ich habe den Versuch aufgegeben, hierfür eine interne Nginx-Umleitung zu verwenden, und habe versucht, den Kontrollserver so zu ändern, dass er eine 307-Umleitung an denselben Speicherort ausgibt (damit der Client dieselbe Anfrage erneut versucht, jetzt aber mit gestartetem Backend-Server). Jetzt überschreibt Nginx jedoch dummerweise den Statuscode mit dem, den es vom Backend-Anfrageversuch erhalten hat (502), obwohl der Kontrollserver einen „Location“-Header sendet. Ich habe es schließlich „funktionieren“ lassen, indem ich die Zeile error_page in geändert habe error_page 502 =307 @handle_502;, wodurch alle Antworten des Kontrollservers gezwungen werden, mit einem 307-Code an den Client zurückgesendet zu werden. Das ist sehr hackig und unerwünscht, weil 1) es keine Kontrolle darüber gibt, was Nginx als Nächstes tun soll, abhängig von der Antwort des Kontrollservers (idealerweise möchten wir das Backend nur dann erneut versuchen, wenn der Kontrollserver Erfolg meldet) und 2) nicht alle HTTP-Clients HTTP-Umleitungen unterstützen (z. B. müssen Curl-Benutzer und libcurl-verwendende Anwendungen folgende Umleitungen explizit aktivieren).

Wie kann man Nginx dazu bringen, zuerst den Upstream-Server A, dann B und dann wieder A als Proxy zu verwenden (idealerweise nur, wenn B einen bestimmten Statuscode zurückgibt)?

Antwort1

Wichtige Punkte:

  • Kümmern Sie sich nicht um upstreamBlockierungen für ein Failover, wenn durch das Pingen eines Servers ein anderer hochfährt – es gibt keine Möglichkeit, nginx mitzuteilen (zumindest nicht der FOSS-Version), dass der erste Server wieder hochgefahren ist. nginx probiert die Server bei der ersten Anfrage der Reihe nach aus, nicht jedoch bei Folgeanfragen, ungeachtet aller - backup, weight- oder fail_timeout-Einstellungen.
  • DumussAktivieren Sie diese Option recursive_error_pages, wenn Sie ein Failover mithilfe error_pagebenannter Standorte implementieren.
  • Aktivieren Sie diese Option proxy_intercept_errors, um vom Upstream-Server gesendete Fehlercodes zu verarbeiten.
  • Die =Syntax (z. B. error_page 502 = @handle_502;) ist erforderlich, um Fehlercodes am benannten Ort korrekt zu verarbeiten. Wenn =sie nicht verwendet wird, verwendet nginx den Fehlercode aus dem vorherigen Block.

Hier ist eine Zusammenfassung:

server {
    listen ...;
    server_name $DOMAINS;

    recursive_error_pages on;

    # First, try "Upstream A"
    location / {
        error_page 418 = @backend;
        return 418;
    }

    # Define "Upstream A"
    location @backend {
        proxy_pass http://$IP:81;
        proxy_set_header  X-Real-IP     $remote_addr;
        # Add your proxy_* options here
    }

    # On error, go to "Upstream B"
    error_page 502 @handle_502;

    # Fallback static error page, in case "Upstream B" fails
    root /home/nginx/www;
    location = /_static_error.html {
        internal;
    }

    # Define "Upstream B"
    location @handle_502 { # What to do when the backend server is not up
        proxy_pass ...;
        # Add your proxy_* options here
        proxy_intercept_errors on;          # Look at the error codes returned from "Upstream B"
        error_page 502 /_static_error.html; # Fallback to error page if "Upstream B" is down
        error_page 451 = @backend;          # Try "Upstream A" again
    }
}

Ursprüngliche Antwort/Forschungsprotokoll folgt:


Ich habe folgenden besseren Workaround gefunden, der eine Verbesserung darstellt, da er keine Client-Umleitung erfordert:

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;
}

Bringen Sie den Kontrollserver dann einfach dazu, bei „Erfolg“ 502 zurückzugeben, und hoffen Sie, dass dieser Code nie von den Backends zurückgegeben wird.


Update: nginx markiert den ersten Eintrag im upstreamBlock immer als inaktiv, sodass es bei aufeinanderfolgenden Anfragen die Server nicht der Reihe nach durchprobiert. Ich habe versucht, weight=1000000000 fail_timeout=1den ersten Eintrag zu ergänzen, aber ohne Erfolg. Bisher habe ich keine Lösung gefunden, die keine Client-Umleitung erfordert.


Bearbeiten: Eine weitere Sache, die ich gerne wüsste – um den Fehlerstatus vom error_pageHandler abzurufen, verwenden Sie diese Syntax: error_page 502 = @handle_502;– dieses Gleichheitszeichen veranlasst nginx, den Fehlerstatus vom Handler abzurufen.


Bearbeiten: Und ich habe es zum Laufen gebracht! Zusätzlich zum error_pageobigen Fix musste ich nur noch aktivieren recursive_error_pages!

Antwort2

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;
    }

}

verwandte Informationen