如果找不到上游主机,则设置nginx不会崩溃

Mor*_*zov 94 nginx url-rewriting proxypass

我们在Docker的常见域下有几个rails应用程序,我们使用nginx将请求定向到特定应用程序.

our_dev_server.com/foo # proxies to foo app
our_dev_server.com/bar # proxies to bar
Run Code Online (Sandbox Code Playgroud)

Config看起来像这样:

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...
}
Run Code Online (Sandbox Code Playgroud)

如果其中一个应用程序未启动,则nginx将失败并停止:

host not found in upstream "bar:3000" in /etc/nginx/conf.d/nginx.conf:6
Run Code Online (Sandbox Code Playgroud)

我们并不需要它们全部启动,但nginx失败了.如何让nginx忽略失败的上游?

Jus*_*tin 76

  1. 如果您可以使用静态IP,那么只需使用它,它就会启动,503如果它没有响应则返回.

  2. 使用该resolver指令指向可以解析主机的内容,无论它是否正在运行.

  3. location如果您不能执行上述操作,则在该级别解析它(这将允许Nginx启动/运行):

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 谢谢!仅使用变量似乎可以防止nginx对它的聪明 (12认同)
  • 这对TCP代理如何起作用?似乎没有办法尝试tcp代理的选项3。 (2认同)
  • @Charlie nginx 中的那些错误几乎总是与 **missing ";" 有关 在行尾签名** :) (2认同)

dan*_*gpm 13

使用的主要优点upstream是定义一组服务器,而不是可以在不同端口上侦听,在它们之间配置负载平衡和故障转移.

在您的情况下,您只为每个上游定义1个主服务器,因此必须启动它.

相反,为您的proxy_pass(es)使用变量并记住处理目标服务器关闭时可能出现的错误(404s,503s).

  • 如果我有多个问题,并且我想忽略那些无法解决的问题怎么办? (6认同)
  • 正如你在其他例子中看到的那样,它将是`set $ variable foo`和`proxy_pass http:// $ variable` (5认同)
  • > 相反,为您的 proxy_pass(es) 使用变量并记住处理目标服务器关闭时可能出现的错误(404s、503s)。你能详细说明如何做到这一点吗?如果我执行 `set $variable http://foo` 和 `proxy_pass $variable` 并保持 foo “上游”(以保持您提到的优势),那么我仍然会遇到 OP 提到的问题。 (2认同)
  • @danielgpm如您所述,将变量用于proxy_pass可以完美地解决我的问题。如果您可以更新答案并将其作为示例,它将对其他人有所帮助 (2认同)

neu*_*ann 13

对我来说,@ Justin / @ duskwuff的答案的选项3解决了这个问题,但是我不得不将解析器IP更改为127.0.0.11(Docker的DNS服务器):

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;
}
Run Code Online (Sandbox Code Playgroud)

但是正如@ Justin / @ duskwuff所述,您可以使用任何其他外部DNS服务器。

  • 您的意思是“location /bar”下的“set $upstream_bar bar;”吗?我知道这是一个旧答案。但这对于任何正在寻找特定于 Docker 的解决方案的人来说都是相关的。我觉得这个例子有点令人困惑。我能想到的唯一解释是 bar 而不是 foo。 (3认同)
  • 对于任何感兴趣的读者:需要在 nginx 配置中显式设置 `resolver`,否则 DNS 名称只能在启动期间解析。有关更多详细信息,请参阅此处 /sf/answers/2823187951/。显然,nginx 不会读取 /etc/resolv.conf,并且在没有 `resolver` 指令的情况下,所有查找都会失败。 (2认同)

Ger*_*cke 8

我们遇到了类似的问题,我们通过在上游容器中动态包含conf文件来解决它,这些文件是由side-car容器生成的,该容器对docker.sock上的事件做出反应,并在上游配置中使用通配符包含文件:

 include /etc/upstream/container_*.conf;
Run Code Online (Sandbox Code Playgroud)

如果列表为空,我们添加了一个永久关闭的服务器条目 - 因此有效服务器列表不为空。该服务器条目永远不会收到任何请求

 server 127.0.0.1:10082 down; 
Run Code Online (Sandbox Code Playgroud)

最后一个条目指向 nginx 中托管错误页面的(内部)服务器(例如 503)

 server 127.0.0.1:10082 backup;
Run Code Online (Sandbox Code Playgroud)

所以最终的上游配置如下所示:

upstream my-service {
  include /etc/upstream/container_*.conf;
  server 127.0.0.1:10082 down; 
  server 127.0.0.1:10082 backup;
Run Code Online (Sandbox Code Playgroud)

}

在 nginx 配置中,我们添加了一个监听错误端口的服务器:

server {
    listen 10082;

    location / {
        return 503;
        add_header Content-Type text/plain;
    }

    error_page 503 @maintenance;
    location @maintenance {
       internal;
       rewrite ^(.*)$ /503.html break;
       root error_pages/;
    }
}
Run Code Online (Sandbox Code Playgroud)

如前所述,每个上游容器的配置文件是由一个脚本(bash、curl、jq)生成的,该脚本使用curl与docker.socket交互,它的rest api获取所需的信息(ip、端口)并使用此模板生成文件。

server ${ip}:${port} fail_timeout=5s max_fails=3;
Run Code Online (Sandbox Code Playgroud)