Nginx不会以上游

时间:2018-05-09 08:11:45

标签: nginx http-proxy

我使用nginx代理并为我保留远程服务器的持久连接。

我已经配置了大约15个与此示例类似的块:

upstream rinu-test {
    server test.rinu.test:443;
    keepalive 20;
}
server {
    listen 80;
    server_name test.rinu.test;
    location / {
        proxy_pass https://rinu-test;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $http_host;
    }
}

问题是如果无法在一个或多个upstream块中解析主机名,则nginx将不会(重新)启动。我也不能使用静态IP,其中一些主机明确表示不这样做,因为IP会发生变化。我在此错误消息中看到的所有其他解决方案都说要摆脱upstream并执行location块中的所有操作。这不可能,因为keepalive仅在upstream下可用。

我可以暂时承担丢失一台服务器但不是全部15台服务器的费用。

编辑: 原来nginx不适合这个用例。应使用备用后端(上游)keepalive代理。自定义Node.js替代方案位于my answer。到目前为止,我还没有找到任何其他替代方案。

6 个答案:

答案 0 :(得分:5)

早期版本的nginx(1.1.4之前版本)已经为全球访问量最大的网站提供了大量数据(有些人甚至现在都做了,如果要相信服务器标题),甚至都没有在keepalive方面支持upstream,因为在数据中心设置中这样做几乎没有什么好处,除非你的各个主机之间有一个非平凡的延迟;有关解释,请参阅https://serverfault.com/a/883019/110020

基本上,除非您知道在上游和前端之间特别需要保持活跃,否则很可能只是让您的架构不那么有弹性而且更糟糕。

(请注意,您当前的解决方案也是错误的,因为IP地址的更改同样不会被检测到,因为您只在配置重新加载时执行主机名解析;因此,即使nginx确实启动了,它也是'一旦上游服务器的IP地址发生变化,ll基本上就会停止工作。)

潜在的解决方案,选择一个:

  • 最好的解决方案似乎只是摆脱upstream keepalive在数据中心环境中可能不必要,并使用proxy_pass的变量来获取最新的DNS每个请求的分辨率(nginx仍然足够聪明 - 仍然可以缓存此类分辨率)

  • 另一个选择是通过商业订阅获得nginx的付费版本,该订阅在server上下文中具有upstream指令的resolve参数。

  • 最后,要尝试的另一件事可能是使用set variable和/或map来指定upstream内的服务器;这既未得到证实也未被否认已经实施;例如,它可能有效也可能无效。

答案 1 :(得分:2)

一种可能的解决方案是涉及本地DNS缓存。它可以是本地DNS服务器,如Bind或Dnsmasq(有一些狡猾的配置,请注意nginx也可以使用specified dns server代替系统默认值),或者只是在hosts文件中维护缓存。 / p>

似乎使用hosts文件和一些脚本是非常简单的方法。 hosts文件应该插入静态和动态部分(即cat hosts.static hosts.dynamic > hosts),动态部分应该由脚本自动生成(和更新)。

或许有必要不时检查更改IP的主机名,并更新主机文件并在更改时重新加载nginx中的配置。如果某些主机名无法解析,则应使用旧IP或某些默认IP(如127.0.1.9)。

如果您不需要nginx配置文件中的主机名(即IP足够),可以通过脚本included生成带有IP(已解析主机名)的upstream部分nginx config - 在这种情况下无需触摸hosts文件。

答案 2 :(得分:2)

另一种方法是编写一个只能满足我想要的新服务。以下内容使用Node.js

替换代理https连接的nginx
const http = require('http');
const https = require('https');

const httpsKeepAliveAgent = new https.Agent({ keepAlive: true });

http.createServer(onRequest).listen(3000);

function onRequest(client_req, client_res) {
    https.pipe(
        protocol.request({
            host: client_req.headers.host,
            port: 443,
            path: client_req.url,
            method: client_req.method,
            headers: client_req.headers,
            agent: httpsKeepAliveAgent
        }, (res) => {
            res.pipe(client_res);
        }).on('error', (e) => {
            client_res.end();
        })
    );
}

用法示例: curl http://localhost:3000/request_uri -H "Host: test.rinu.test" 这相当于: curl https://test.rinu.test/request_uri

答案 3 :(得分:1)

我将resolve参数放在服务器上,您需要在nginx.conf中设置Nginx Resolver,如下所示:

/etc/nginx/nginx.conf:

http {
    resolver 192.168.0.2 ipv6=off valid=40s;  # The DNS IP server
} 

Site.conf:

upstream rinu-test {
    server test.rinu.test:443;
    keepalive 20;
}

答案 4 :(得分:0)

我的问题与容器有关。我正在使用 docker compose 创建 nginx 容器以及应用程序容器。在 network_mode: host 的应用容器配置中设置 docker-compose.yml 时,nginx 无法找到上游应用容器。删除它解决了问题。

答案 5 :(得分:-1)

您的场景非常类似于使用aws ELB作为uptreams的场景,其中resolve对于定义域的正确IP至关重要。

您需要做的第一件事就是确保您使用的DNS服务器可以解析到您的域,然后您可以像这样创建配置:

resolver 10.0.0.2 valid=300s;
resolver_timeout 10s;

location /foo {
    set $foo_backend_servers foo_backends.example.com;
    proxy_pass http://$foo_backend_servers;
 }

location /bar {
    set $bar_backend_servers bar_backends.example.com;
    proxy_pass http://$bar_backend_servers;
 }

请注意,resolver 10.0.0.2它应该是可以运行的DNS服务器的IP并回答您的查询,具体取决于您的设置,这可能是unbound等本地缓存服务。然后只使用resolve 127.0.0.1

现在,使用变量从文档中指定域名非常重要:

  

当您使用变量在proxy_pass指令中指定域名时,NGINX会在其TTL到期时重新解析域名。

您可以使用dig等工具检查解析器,例如:

$ dig +short stackoverflow.com

如果必须在上游使用keepalive,如果不是使用Nginx +的选项,那么你可以尝试openresty balancer,你需要使用/实现lua-resty-dns