nginx reverse proxy - tente upstream A, depois B, depois A novamente

21

Estou tentando configurar o nginx como um proxy reverso, com um grande número de servidores de back-end. Gostaria de iniciar os back-ends sob demanda (na primeira solicitação que entra), portanto, tenho um processo de controle (controlado por solicitações HTTP) que inicia o back-end dependendo da solicitação recebida.

Meu problema é configurar o nginx para fazer isso. Aqui está o que eu tenho até agora:

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

Isso não funciona - o nginx parece ignorar qualquer código de status retornado do servidor de controle. Nenhuma das diretivas error_page na localização @handle_502 funciona e o código 451 é enviado como está para o cliente.

Eu desisti de tentar usar o redirecionamento nginx interno para isso e tentei modificar o servidor de controle para emitir um redirecionamento 307 para o mesmo local (para que o cliente tentasse novamente a mesma solicitação, mas agora com o servidor de back-end iniciado) . No entanto, agora nginx é estupidamente sobrescrevendo o código de status com o que obteve da tentativa de solicitação de backend (502), apesar de o servidor de controle estar enviando um cabeçalho "Location". Eu finalmente consegui "trabalhar" alterando a linha error_page para error_page 502 =307 @handle_502; , forçando assim que todas as respostas do servidor de controle fossem enviadas de volta ao cliente com um código 307. Isso é muito hacky e indesejável, porque 1) não há controle sobre o que o nginx deve fazer a seguir dependendo da resposta do servidor de controle (idealmente, queremos apenas repetir o backend apenas se o servidor de controle relatar sucesso) e 2) nem todos os HTTP os clientes suportam redirecionamentos HTTP (por exemplo, usuários curl e aplicativos que usam libcurl precisam ativar os seguintes redirecionamentos explicitamente).

Qual é a maneira correta de obter o nginx para tentar proxy para o servidor upstream A, então B, depois A novamente (idealmente, somente quando B retorna um código de status específico)?

    
por Vladimir Panteleev 08.09.2013 / 13:59

2 respostas

17

Pontos principais:

  • Não se preocupe com upstream blocos para failover, se pingar um servidor trará outro para cima - não é possível dizer ao nginx (pelo menos, não à versão do FOSS) que o primeiro servidor está ativo novamente. O nginx tentará os servidores na primeira solicitação, mas não nas solicitações de acompanhamento, apesar das configurações backup , weight ou fail_timeout .
  • Você deve ativar recursive_error_pages ao implementar o failover usando error_page e locais nomeados.
  • Ative o proxy_intercept_errors para lidar com códigos de erro enviados pelo servidor upstream.
  • A sintaxe = (por exemplo, error_page 502 = @handle_502; ) é necessária para manipular corretamente os códigos de erro no local nomeado. Se = não for usado, o nginx usará o código de erro do bloco anterior.

Resposta original / registro de pesquisa:

Aqui está uma solução melhor que encontrei, o que é uma melhoria, pois não exige um redirecionamento de cliente:

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

Então, é só pegar o servidor de controle para retornar 502 em "sucesso" e esperar que o código nunca seja retornado pelos backends.

Atualização: o nginx continua marcando a primeira entrada no bloco upstream como inativo, por isso não tenta os servidores em ordem em solicitações sucessivas. Eu tentei adicionar weight=1000000000 fail_timeout=1 à primeira entrada sem efeito. Até agora não encontrei nenhuma solução que não envolva um redirecionamento de cliente.

Edit: Mais uma coisa que eu gostaria de saber - para obter o status de erro do manipulador error_page , use esta sintaxe: error_page 502 = @handle_502; - o sinal de igual fará com que o nginx obtenha o status de erro do manipulador. p>

Edit: E eu tenho que trabalhar! Além da correção error_page acima, tudo o que era necessário era ativar recursive_error_pages !

    
por 08.09.2013 / 14:17
1

Você pode tentar algo como o seguinte

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

}
    
por 08.09.2013 / 14:22