Nginx将无法启动,在上游找不到主机

rt4zxlrg  于 2023-06-21  发布在  Nginx
关注(0)|答案(7)|浏览(262)

我使用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。到目前为止,我还没有找到任何其他实际工作的替代品。

brccelvz

brccelvz1#

nginx的早期版本(1.1.4之前),已经为全球访问量最大的网站提供了大量的支持(如果服务器头是可信的,有些甚至现在仍然如此),甚至不支持upstream端的keepalive,因为在数据中心设置中这样做几乎没有好处,除非你在各种主机之间有一个不小的延迟;参见https://serverfault.com/a/883019/110020以获得一些解释。
基本上,除非你知道你特别需要上游和前端之间的keepalive,否则它很可能只会使你的架构弹性降低,情况更糟。
(Note您当前的解决方案也是错误的,因为IP地址的更改同样不会被检测到,因为您只在配置重新加载时进行主机名解析;所以,即使nginx启动了,一旦上游服务器的IP地址发生变化,它基本上就会停止工作。
潜在的解决方案,选择一个:

  • 最好的解决方案似乎只是摆脱upstreamkeepalive,因为在数据中心环境中可能不必要,并使用proxy_pass变量为每个请求提供最新的DNS解析(nginx仍然足够智能,仍然可以缓存此类解析)
  • 另一种选择是通过商业订阅获得nginx的付费版本,该订阅在upstream上下文中具有server指令的resolve参数。
  • 最后,另一种尝试是使用set variable和/或map来指定upstream中的服务器;这一点既没有得到证实,也没有被否认;例如它可能工作也可能不工作。
svujldwt

svujldwt2#

您的场景与使用aws ELB作为upstream时非常相似,其中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

bbuxkriu

bbuxkriu3#

一种可能的解决方案是涉及本地DNS缓存。它可以是本地DNS服务器,如Bind或Dnsmasq(通过一些巧妙的配置,请注意nginx也可以使用specified dns server代替系统默认值),或者只是在hosts文件中维护缓存。
看起来使用hosts文件和一些脚本是非常简单的方法。主机文件应分为静态和动态部分(即cat hosts.static hosts.dynamic > hosts),动态部分应该由脚本自动生成(和更新)。
也许不时检查主机名以更改IP是有意义的,并在更改时更新主机文件并在nginx中重新加载配置。如果某些主机名无法解析,则应使用旧IP或某些默认IP(如www.example.com)。127.0.1.9) should be used.
如果你不需要nginx配置文件中的主机名(即,IP就足够了),可以通过脚本生成带有IP(解析的主机名)的upstream部分,并将其包含到nginx配置中-在这种情况下,不需要触摸hosts文件。

70gysomp

70gysomp4#

我把resolve参数放在server上,你需要在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;
}
yizd12fk

yizd12fk5#

我的问题与集装箱有关。我正在使用docker compose创建nginx容器和app容器。在docker-compose.yml的app container配置中设置network_mode: host时,nginx找不到上游的app container。删除此选项解决了问题。

0yycz8jy

0yycz8jy6#

我们可以暂时解决

cd /etc
sudo vim resolv.conf
i
nameserver 8.8.8.8 
:wq

然后做sudo nginx -t重新启动nginx它将工作的时刻

8ulbf1ek

8ulbf1ek7#

另一种方法是编写一个只做我想要的事情的新服务。下面的代码替换了nginx,用于使用Node.js代理https连接

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

相关问题