设置nginx在上游找不到主机时不会崩溃

pxq42qpu  于 2023-01-16  发布在  Nginx
关注(0)|答案(9)|浏览(195)

我们在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

我们不需要它们都是up的,但是nginx会失败。如何让nginx忽略失败的上游?

ctzwtxfj

ctzwtxfj1#

1.如果你可以使用一个静态IP,那么就使用它,它会启动,如果它没有响应,就返回503
1.使用resolver指令指向可以解析主机的内容,而不管主机当前是否已启动。
1.解决它在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;
 }
w8ntj3qf

w8ntj3qf2#

对我来说,@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服务器。

brjng4g3

brjng4g33#

使用upstream的主要优点是定义一组服务器,它们可以监听不同的端口并在它们之间配置负载平衡和故障转移***。
在您的情况下,您
仅为每个上游定义了1个主服务器,因此 * 它必须启动
**。
相反,对proxy_pass使用变量,并记住处理目标服务器关闭时可能出现的错误(404s、503s)。
使用变量的示例:

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

zbq4xfa04#

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

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和它的rest api与docker.socket交互,以获取所需的信息(ip、port),并使用此模板生成文件。

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

lh80um4z5#

另一个快速和简单的修复某人的情况下,我可以开始和停止没有我的主服务器爆炸了

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

hfsqlsce6#

我遇到了同样的“Host not found”问题,因为我的主机的一部分是使用$uri而不是$request_uriMap的:

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

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

map $request_uri $kubernetes {
    # ...
}
2eafrhcq

2eafrhcq7#

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

cx6n0qe3

cx6n0qe38#

https://stackoverflow.com/a/32846603/11780117
我不能添加评论,所以我在这里添加。
如果原始反向代理是这样编写的:

location ^~ /api {                                                
    proxy_set_header Host $http_host;                             
    proxy_set_header X-Real-IP $remote_addr;                      
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    
    proxy_pass http://other-host:8000/api;                     
}

当用户访问https://you-domain/api/test?query=name URL时,后端服务器收到的PATH是/api/test?query=name,它正在工作。

location ^~ /api {                                                
    proxy_set_header Host $http_host;                             
    proxy_set_header X-Real-IP $remote_addr;                      
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    resolver 127.0.0.11 valid=30s;
    set $backend other-host;
    proxy_pass http://$backend:8000/api;
}

请注意,这里当您请求https://you-domain/api/test?query=name URL时,后端服务器实际接收到的PATH是/api它丢失了许多参数。

因此,当您使用变量时,正确的配置应该是:

location ^~ /api {                                                
    proxy_set_header Host $http_host;                             
    proxy_set_header X-Real-IP $remote_addr;                      
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    resolver 127.0.0.11 valid=30s;
    set $backend other-host;
    proxy_pass http://$backend:8000;
}

如果你想访问根目录的代理访问后端,那么你需要这个:

rewrite /api/(.*) /$1 break;
proxy_pass http://$backend:8000;

然后您请求https://you-domain/api/test?query=name,后端服务器实际收到的PATH是/test?query=name

vcudknz3

vcudknz39#

您可以不使用--link选项,而是使用端口Map并将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

相关问题