Configure o nginx para não travar se o host no upstream não for encontrado

117

Temos vários aplicativos rails sob domínio comum no Docker e usamos o nginx para direcionar solicitações a aplicativos específicos.

our_dev_server.com/foo # proxies to foo app
our_dev_server.com/bar # proxies to bar

A configuração é semelhante a esta:

upstream foo {
  server foo:3000;
}

upstream bar {
  server bar:3000;
}

# and about 10 more...

server {
  listen *:80 default_server;

  server_name our_dev_server.com;

  location /foo {
      # this is specific to asset management in rails dev
      rewrite ^/foo/assets(/.*)$ /assets/$1 break;
      rewrite ^/foo(/.*)$ /foo/$1 break;
      proxy_pass http://foo;
  }

  location /bar {
      rewrite ^/bar/assets(/.*)$ /assets/$1 break;
      rewrite ^/bar(/.*)$ /bar/$1 break;
      proxy_pass http://bar;
  }

  # and about 10 more...
}

Se um desses aplicativos não for iniciado, o nginx falhará e para:

host not found in upstream "bar:3000" in /etc/nginx/conf.d/nginx.conf:6

Não precisamos que todos estejam ativados, mas o nginx falha de outra forma. Como fazer o nginx ignorar upstreams com falha?

Morozov
fonte
1
Você está vinculando os contêineres de aplicativos aos contêineres Nginx ou os executando separadamente? Se o host dentro do upstreambloco não resolver, em tempo de execução, então o Nginx sairá com o erro acima ...
Justin
1
Se você puder usar um IP, ele inicializará bem. Usar resolver( nginx.org/en/docs/http/ngx_http_core_module.html#resolver ) funcionaria no seu caso?
Justin
@ Justin nós temos cada aplicativo em um contêiner separado, nginx também. Vincule-os ao docker
Morozov,
@Justin A ordem de inicialização está bem, o nginx inicia após outros aplicativos. Queremos apenas exibir alguns deles :)
Morozov
1
Eu tenho uma configuração semelhante (contêiner Nginx com contêiner (s) de aplicativo) . Criamos uma imagem Nginx que inclui um proxy.shscript que lê as variáveis ​​de ambiente e adiciona upstreamentradas dinamicamente para cada uma e, em seguida, inicia o Nginx. Isso funciona muito bem, pois quando executamos nosso contêiner de proxy, podemos passar os upstreams necessários em tempo de execução. Você poderia fazer algo semelhante para habilitar / desabilitar certos upstreams no lançamento (ou como minha configuração, apenas adicionar os necessários no tempo de execução)
Justin

Respostas:

90
  1. Se você pode usar um IP estático, então use-o, ele iniciará e retornará 503se não responder.

  2. Use a resolverdiretiva para apontar para algo que pode resolver o host, independentemente se ele está ativo ou não.

  3. Resolva-o no locationnível, se você não puder fazer o acima (isso permitirá que o Nginx seja iniciado / executado) :

    location /foo {
      resolver 127.0.0.1 valid=30s;
      # or some other DNS (you company/internal DNS server)
      #resolver 8.8.8.8 valid=30s;
      set $upstream_foo foo;
      proxy_pass http://$upstream_foo:80;
    }
    
    location /bar {
      resolver 127.0.0.1 valid=30s;
      # or some other DNS (you company/internal DNS server)
      #resolver 8.8.8.8 valid=30s;
      set $upstream_bar foo;
      proxy_pass http://$upstream_bar:80;
    }
    
Justin
fonte
1
sua opção 3 funciona muito bem para mim. Se eu não especificar um resolvedor, você sabe por quanto tempo o nginx armazenará em cache o IP que ele resolve?
Riley Lark de
14
Obrigado! Apenas usar uma variável parece impedir o nginx de ser inteligente sobre isso
Blanka
1
Descobri que um grupo de captura de regex me permitiu pular a variável:location ~ ^/foo/(.*)$ { proxy_pass http://foo/$1; }
Danny Kirchmeier
2
Como isso funciona para um proxy TCP? Parece que não há como tentar a opção 3 para o proxy tcp.
krish7919
1
@Charlie esse tipo de erro no nginx quase sempre está relacionado à falta de ";" assinar no final da linha :)
SteveB
18

Para mim, a opção 3 da resposta de @ Justin / @ duskwuff resolveu o problema, mas tive que alterar o IP do resolvedor para 127.0.0.11 (servidor DNS do Docker):

location /foo {
  resolver 127.0.0.11 valid=30s;
  set $upstream_foo foo;
  proxy_pass http://$upstream_foo:80;
}

location /bar {
  resolver 127.0.0.11 valid=30s;
  set $upstream_bar foo;
  proxy_pass http://$upstream_bar:80;
}

Mas, como @ Justin / @ duskwuff mencionou, você pode usar qualquer outro servidor DNS externo.

Neumann
fonte
15

A principal vantagem de usar upstreamé definir um grupo de servidores que pode escutar em portas diferentes e configurar o balanceamento de carga e failover entre eles .

No seu caso, você está definindo apenas 1 servidor primário por upstream, portanto, ele deve estar ativo .

Em vez disso, use variáveis ​​para seu proxy_pass(s) e lembre-se de lidar com os possíveis erros (404s, 503s) que você pode obter quando um servidor de destino está inativo.

danielgpm
fonte
1
> Em vez disso, use variáveis ​​para seu (s) proxy_pass (s) e lembre-se de lidar com os possíveis erros (404s, 503s) que você pode obter quando um servidor de destino está inativo. Você pode explicar como fazer isso? Se eu fizer set $variable http://fooe proxy_pass $variablemantiver o foo "upstream" (para manter as vantagens que você mencionou), ainda estou encontrando o problema mencionado pelo OP.
Tibor Vass
6
Como você pode ver em outros exemplos, será set $variable fooeproxy_pass http://$variable
danielgpm 01 de
2
@danielgpm Como você afirmou, usar a variável para proxy_pass funciona perfeitamente e resolveu meu problema. Ajudaria outras pessoas se você pudesse atualizar sua resposta e mencioná-lo como exemplo
Nitb
3
E se eu tiver mais de um e quiser ignorar aqueles que não podem ser resolvidos?
talabes
0

Tive o mesmo problema de "Host não encontrado" porque parte do meu host estava sendo mapeada usando em $urivez de $request_uri:

proxy_pass http://one-api-service.$kubernetes:8091/auth;

E quando a solicitação mudou para a sub-solicitação de autenticação, o $uriperdeu seu valor inicial. Alterar o mapeamento a ser usado em $request_urivez de $uriresolver meu problema:

map $request_uri $kubernetes {
    # ...
}
Washington Guedes
fonte
-8

Você não pode usar a --linkopção, em vez disso, você pode usar o mapeamento da porta e vincular o nginx ao endereço do host.

Exemplo: execute seu primeiro contêiner docker com -p 180:80opção, segundo contêiner com -p 280:80opção.

Execute nginx e defina estes endereços para proxy:

proxy_pass http://192.168.1.20:180/; # first container
proxy_pass http://192.168.1.20:280/; # second container
kvaps
fonte