ChatGPT解决这个技术问题 Extra ChatGPT

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

我们在 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 崩溃。它只是一个愚蠢的设计。任何伙伴怎么会因为另一台没有发现它的愚蠢设计而使一台服务器崩溃

h
hooknc

如果您可以使用静态 IP,则只需使用它,它将启动,如果没有响应,则返回 503。使用解析器指令指向可以解析主机的东西,无论它当前是否启动。如果您不能执行上述操作(这将允许 Nginx 启动/运行),请在位置级别解决它: location /foo { resolver 127.0.0.1 valid=30s; # 或其他一些 DNS(您公司的内部 DNS 服务器)#resolver 8.8.8.8 valid=30s;设置 $upstream_foo foo; proxy_pass http://$upstream_foo:80; } 位置 /bar { 解析器 127.0.0.1 有效=30s; # 或其他一些 DNS(您公司的内部 DNS 服务器)#resolver 8.8.8.8 valid=30s;设置 $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 中的那些错误几乎总是与缺少“;”有关在行尾签名:)
D
DJDaveMark

对我来说,@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 bar;
  proxy_pass http://$upstream_bar:80;
}

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


您是指 location /bar 下的 set $upstream_bar bar; 吗?我知道这是一个旧答案。但它适用于任何正在寻找特定于 Docker 的解决方案的人。我有点觉得这个例子令人困惑。我能想到的唯一解释是 bar 而不是 foo。
@kevinnls 是的,他做到了。我修复了上面的代码。
对于任何感兴趣的读者:需要在 nginx 配置中显式设置 resolver,否则 DNS 名称只能在启动期间解析。请参阅此处了解更多详情stackoverflow.com/a/40331256/1114532。显然 nginx 不读取 /etc/resolv.conf 并且在没有 resolver 指令的情况下使所有查找失败。
e
emyller

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

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

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

使用变量的示例:

server {
  listen 80;
  set $target "http://target-host:3005";  # Here's the secret
  location / { proxy_pass $target; }
}

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

另一个针对某人场景的快速简便的修复,我可以启动和停止而不会我的主服务器被炸毁

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

W
Washington Guedes

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

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

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

map $request_uri $kubernetes {
    # ...
}

I
Ilya Shevyryaev

根据贾斯汀的回答,最快的方法是用 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 设置子网。


G
Gerald Mücke

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

 include /etc/upstream/container_*.conf;

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

 server 127.0.0.1:10082 down; 

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

 server 127.0.0.1:10082 backup;

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

upstream my-service {
  include /etc/upstream/container_*.conf;
  server 127.0.0.1:10082 down; 
  server 127.0.0.1:10082 backup;

}

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

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

server ${ip}:${port} fail_timeout=5s max_fails=3;

k
kvaps

您可以不使用 --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

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅