proxy inverso nginx: prova a monte A, quindi B, quindi nuovamente A


22

Sto cercando di configurare nginx come proxy inverso, con un gran numero di server back-end. Vorrei avviare i back-end su richiesta (sulla prima richiesta che arriva), quindi ho un processo di controllo (controllato da richieste HTTP) che avvia il back-end a seconda della richiesta che riceve.

Il mio problema è configurare nginx per farlo. Ecco cosa ho finora:

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

Questo non funziona: sembra che nginx ignori qualsiasi codice di stato restituito dal server di controllo. Nessuna delle error_pagedirettive nella @handle_502posizione funziona e il codice 451 viene inviato così com'è al client.

Ho rinunciato a provare a utilizzare il reindirizzamento interno nginx per questo, e ho provato a modificare il server di controllo per emettere un reindirizzamento 307 nella stessa posizione (in modo che il client potesse ritentare la stessa richiesta, ma ora con il server back-end avviato). Tuttavia, ora nginx sta sovrascrivendo stupidamente il codice di stato con quello ottenuto dal tentativo di richiesta back-end (502), nonostante il server di controllo stia inviando un'intestazione "Posizione". Finalmente ho ottenuto "funzionante" modificando la riga error_page inerror_page 502 =307 @handle_502;, costringendo così tutte le risposte del server di controllo a essere rinviate al client con un codice 307. Questo è molto caotico e indesiderabile, perché 1) non c'è controllo su ciò che nginx dovrebbe fare in seguito alla risposta del server di controllo (idealmente vogliamo riprovare il back-end solo se il server di controllo riporta il successo), e 2) non tutto HTTP i client supportano i reindirizzamenti HTTP (ad esempio, gli utenti arricciati e le applicazioni che usano libcurl devono abilitare esplicitamente i seguenti reindirizzamenti).

Qual è il modo corretto per ottenere nginx per tentare di eseguire il proxy al server upstream A, quindi B, quindi di nuovo A (idealmente, solo quando B restituisce un codice di stato specifico)?

Risposte:


20

Punti chiave:

  • Non preoccuparti dei upstreamblocchi per il failover, se il ping di un server ne farà apparire un altro - non c'è modo di dire a nginx (almeno, non alla versione FOSS) che il primo server è di nuovo attivo. nginx cercherà i server in ordine alla prima richiesta, ma non il follow-up richieste, nonostante le backup, weighto fail_timeoutimpostazioni.
  • È necessario abilitare recursive_error_pagesquando si implementa il failover utilizzando error_pageposizioni denominate.
  • Abilitare proxy_intercept_errorsper gestire i codici di errore inviati dal server upstream.
  • La =sintassi (ad es. error_page 502 = @handle_502;) È necessaria per gestire correttamente i codici di errore nella posizione indicata. Se =non viene utilizzato, nginx utilizzerà il codice di errore del blocco precedente.

Segue risposta originale / diario di ricerca:


Ecco una migliore soluzione che ho trovato, che è un miglioramento dal momento che non richiede un reindirizzamento client:

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

Quindi, basta ottenere il server di controllo per restituire 502 in "successo" e sperare che il codice non venga mai restituito dai backend.


Aggiornamento: nginx continua a contrassegnare la prima voce nel upstreamblocco come inattiva, quindi non prova i server in ordine su richieste successive. Ho provato ad aggiungere weight=1000000000 fail_timeout=1alla prima voce senza alcun effetto. Finora non ho trovato alcuna soluzione che non implichi un reindirizzamento del client.


Modifica: Un'altra cosa che vorrei sapere - per ottenere lo stato di errore dal error_pagegestore, utilizzare questa sintassi: error_page 502 = @handle_502;- che segno di uguale farà sì che nginx ottenga lo stato di errore dal gestore.


Modifica: E l'ho fatto funzionare! Oltre alla error_pagecorrezione di cui sopra, tutto ciò che serviva era abilitare recursive_error_pages!


1
Per me ha proxy_next_upstreamfatto il trucco (beh, il mio scenario non era complesso come il tuo), volevo solo nginx per provare il server successivo in caso di errore, quindi ho dovuto aggiungere proxy_next_upstream error timeout invalid_header non_idempotent;( non_idempotent, perché voglio inoltrare principalmente le POSTrichieste).
Philipp,

1

Potresti provare qualcosa di simile al seguente

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

}

nginx non riproverà a.example.netdopo aver fallito una volta sulla stessa richiesta. Invierà al client l'errore riscontrato durante il tentativo di connessione b.example.net, che non sarà quello che si aspettavano a meno che non implementassi anche il proxy nel server di controllo.
Vladimir Panteleev,

E quale sarebbe la tua configurazione nella prossima situazione: la richiesta al ritorno A a monte fallisce, il ritorno B a monte fallisce, quindi proviamo di nuovo a monte A e anche falliamo (502)?
ALex_hha,

A monte B è il server di controllo. Il suo scopo è assicurarsi che la prossima richiesta a monte A avrà esito positivo. L'obiettivo è provare a monte A, se fallisce provare a monte B, se "ha avuto successo" (usando la nostra convenzione interna di "successo"), riprovare a monte A. Se la mia domanda non era abbastanza chiara, fammi sapere come posso migliorarla.
Vladimir Panteleev,

Supponiamo che l'upstream A sia inattivo, ad esempio qualche problema hardware. Cosa renderà a monte B? È in grado di restituire la risposta alla richiesta dal client?
ALex_hha,

Questo problema non riguarda il failover per guasti hardware. Questo problema riguarda l'avvio di back-end a monte su richiesta. Se il server di controllo (upstream B) non è in grado di riattivare il back-end (upstream A), idealmente l'utente dovrebbe ricevere un messaggio di errore appropriato, ma non è il problema che sto cercando di risolvere: il problema è che nginx riprova A dopo B di nuovo, nell'ambito della stessa richiesta.
Vladimir Panteleev,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.