如果未找到上游主机,则设置 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://foo
和 proxy_pass $variable
并保持 foo “上游”(以保持您提到的优势),那么我仍然遇到 OP 提到的问题。
正如您在其他示例中看到的,它将是set $variable foo
和proxy_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 不会崩溃的主要内容,如果未能解决你的问题,请参考以下文章