我们在 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 忽略失败的上游?
upstream
块中的主机在运行时未解析,则 Nginx 将退出并出现上述错误...
resolver
(nginx.org/en/docs/http/ngx_http_core_module.html#resolver) 会起作用吗?
proxy.sh
脚本,该脚本读取环境变量并为每个脚本动态添加 upstream
条目,然后启动 Nginx。这很有效,因为当我们运行代理容器时,我们可以在运行时传入所需的上游。您可以执行类似的操作以在启动时启用/禁用某些上游 (或者像我的设置一样,只需在运行时添加所需的上游)
如果您可以使用静态 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; }
对我来说,@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。
resolver
,否则 DNS 名称只能在启动期间解析。请参阅此处了解更多详情stackoverflow.com/a/40331256/1114532。显然 nginx 不读取 /etc/resolv.conf 并且在没有 resolver
指令的情况下使所有查找失败。
使用 upstream
的主要优点是定义一组可以侦听不同端口的服务器并在它们之间配置负载平衡和故障转移。
在您的情况下,您只为每个上游定义 1 个主服务器,因此它必须启动。
相反,请为您的 proxy_pass
(es) 使用变量,并记住处理目标服务器关闭时可能出现的错误(404、503)。
使用变量的示例:
server {
listen 80;
set $target "http://target-host:3005"; # Here's the secret
location / { proxy_pass $target; }
}
set $variable http://foo
和 proxy_pass $variable
并保持 foo “上游”(以保持您提到的优势),那么我仍然会遇到 OP 提到的问题。
set $variable foo
和 proxy_pass http://$variable
另一个针对某人场景的快速简便的修复,我可以启动和停止而不会我的主服务器被炸毁
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";
}
}
我遇到了同样的“找不到主机”问题,因为我的主机的一部分是使用 $uri
而不是 $request_uri
映射的:
proxy_pass http://one-api-service.$kubernetes:8091/auth;
并且当请求变为 auth 子请求时,$uri
失去了它的初始值。将映射更改为使用 $request_uri
而不是 $uri
解决了我的问题:
map $request_uri $kubernetes {
# ...
}
根据贾斯汀的回答,最快的方法是用 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 设置子网。
我们遇到了类似的问题,我们通过在上游容器中动态包含 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;
您可以不使用 --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
不定期副业成功案例分享
location ~ ^/foo/(.*)$ { proxy_pass http://foo/$1; }