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

Posted

技术标签:

【中文标题】如果未找到上游主机,则设置 nginx 不会崩溃【英文标题】:Setup nginx not to crash if host in upstream is not found 【发布时间】:2015-12-27 00:49:05 【问题描述】:

我们在 Docker 中的公共域下有几个 Rails 应用程序,我们使用 nginx 将请求定向到特定应用程序。

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

配置如下:

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...

如果其中一个应用没有启动,那么 nginx 会失败并停止:

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

我们不需要它们都启动,否则 nginx 会失败。 如何让 nginx 忽略失败的上游?

【问题讨论】:

您是将应用容器与 Nginx 容器链接起来,还是将它们分开运行?如果upstream 块中的主机在运行时没有解析,那么 Nginx 将退出并出现上述错误... 如果您可以使用IP,那么它会启动正常。在您的情况下使用resolver (nginx.org/en/docs/http/ngx_http_core_module.html#resolver) 会起作用吗? @Justin 我们将每个应用程序都放在单独的容器中,nginx 也是如此。用 docker 链接它们 我有一个类似的设置(带有应用容器的 Nginx 容器)。我们创建了一个 Nginx 映像,其中包含一个 proxy.sh 脚​​本,该脚本读取环境变量并为每个动态添加 upstream 条目,然后启动 Nginx。这很有效,因为当我们运行代理容器时,我们可以在运行时传入所需的上游。您可以执行类似的操作以在启动时启用/禁用某些上游 (或者像我的设置一样,只需在运行时添加所需的上游) 我只是讨厌 nginx 崩溃。它只是一个愚蠢的设计。任何朋友怎么会因为另一台没有发现它的愚蠢设计而导致一台服务器崩溃 【参考方案1】:

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

    使用resolver 指令指向可以解析主机的东西,无论它当前是否启动。

    location级别解决它,如果你不能做到上述(这将允许Nginx启动/运行)

     location /foo 
       resolver 127.0.0.1 valid=30s;
       # or some other DNS (your company's 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 (your company's internal DNS server)
       #resolver 8.8.8.8 valid=30s;
       set $upstream_bar foo;
       proxy_pass http://$upstream_bar:80;
     
    

【讨论】:

您的选项 3 非常适合我。如果我不指定解析器,你知道nginx会缓存它解析的IP多久吗? 谢谢!仅仅使用一个变量似乎让 nginx 不聪明 我发现一个正则表达式捕获组允许我跳过变量:location ~ ^/foo/(.*)$ proxy_pass http://foo/$1; 这对 TCP 代理有什么作用?似乎没有办法为 tcp 代理尝试选项 3。 @Charlie nginx 中的那些错误几乎总是与 missing ";" 有关在行尾签名 :)【参考方案2】:

对我来说,@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;

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

【讨论】:

您的意思是set $upstream_bar bar;location /bar 下吗?我知道这是一个旧答案。但它适用于任何正在寻找特定于 Docker 的解决方案的人。我有点觉得这个例子令人困惑。我能想到的唯一解释是 bar 而不是 foo。【参考方案3】:

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

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

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

【讨论】:

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

另一个快速简单的解决方案,我可以启动和停止,而我的主服务器不会被炸毁

    extra_hosts:
      - "dockerhost:172.20.0.1" # <-- static ipv4 gateway of the network ip here thats the only sorta downside but it works for me, you can ifconfig inside a container with the network to find yours, kinda a noob answer but it helped me
    networks:
      - my_network
server 
  listen 80;
  server_name servername;

  location / 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;

    proxy_pass https://dockerhost:12345;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  

【讨论】:

【参考方案5】:

根据贾斯汀的回答,最快的方法是用 IP 地址替换最终主机。您需要使用--ip 172.18.0.XXX 参数为每​​个容器分配一个静态IP 地址。 NGINX 在启动时不会崩溃,如果主机不可用,只会简单地响应 502 错误。

使用静态 IP 运行容器:

docker run --ip 172.18.0.XXX something

Nginx 配置:

location /foo 
    proxy_pass http://172.18.0.XXX:80;

请参阅this 发布如何使用 Docker 设置子网。

【讨论】:

【参考方案6】:

我遇到了同样的“找不到主机”问题,因为我的部分主机是使用 $uri 而不是 $request_uri 映射的:

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

当请求变为 auth 子请求时,$uri 失去了初始值。将映射更改为使用 $request_uri 而不是 $uri 解决了我的问题:

map $request_uri $kubernetes 
    # ...

【讨论】:

【参考方案7】:

你可以不使用--link选项,而是可以使用端口映射并将nginx绑定到主机地址。

示例:使用 -p 180:80 选项运行您的第一个 docker 容器,使用 -p 280:80 选项运行第二个容器。

运行 nginx 并为代理设置这些地址:

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

【讨论】:

以上是关于如果未找到上游主机,则设置 nginx 不会崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Nginx 不会以在上游找不到的主机启动

Nginx 如何处理上游响应的数据

如果上游启动,Nginx 绕过缓存,如果关闭则使用缓存

其他未知域名绑定你网站,Nginx服务器如何设置禁止

解析注册推送通知 - 如果用户未登录则崩溃

Docker 网络 - nginx:在上游找不到 [emerg] 主机