一、问题背景

客户内网环境不允许应用服务器直接访问外网,必须通过跳板机访问外网,所在使用Nginx代理访问外网,部署架构图可参考Nginx反向代理HTTP header自定义参数丢失问题
在部署完成后,测试代理接口都正常,但是一段时间过后,就出现了外网接口访问超时的问题。

二、问题排查

Nginx代理的外网接口都是通过域名进行访问,发现请求超时后,直接在服务器上ping请求地址中的域名,发现域名是可以ping通的,后面继续观察请求超时的情况,发现每次出现请求超时时,在服务器都是能直接ping通请求中的域名,但是服务器每次ping命令解析后的IP与上一次不同,在服务器则无法ping通上次解析后的IP,所以怀疑是不是Nginx缓存了DNS解析。通过查阅文档,发现Nginx在启动时会检查域名是否能够解析,在第一次请求时会缓存DNS解析记录,并且忽略了DNS中的TTL值永久缓存。

注意:在使用Nginx进行反向代理时,如果配置的是域名,突然出现了404问题时,可以查看域名绑定的IP是否发生了变更,如果这种变更不频繁,可以重启Nginx解决,重启Nginx后域名会重新进行解析。

三、问题分析与解决

外网接口中的域名是动态解析的,不同时间访问时域名解析后的IP是动态变化的,客户内网环境只能访问域名实时解析后的IP地址(外网环境验证过没有这个问题),上次解析后的IP会有无法访问的情况。刚开始发现请求超时后,重启Nginx即可解决超时问题,但这只能作为临时的解决方法。

通过查阅文档得知,nginx提供了resolver配置项,可以设置域名解析服务器,并设置缓存的有效期:

Nginx文档地址:http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver

Syntax:	resolver address ... [valid=time] [ipv6=on|off] [status_zone=zone];
Default:	—
Context:	http, server, location
Configures name servers used to resolve names of upstream servers into addresses, for example:

resolver 127.0.0.1 [::1]:5353;
The address can be specified as a domain name or IP address, with an optional port (1.3.1, 1.2.2). If port is not specified, the port 53 is used. Name servers are queried in a round-robin fashion.

Before version 1.1.7, only a single name server could be configured. Specifying name servers using IPv6 addresses is supported starting from versions 1.3.1 and 1.2.2.
By default, nginx will look up both IPv4 and IPv6 addresses while resolving. If looking up of IPv6 addresses is not desired, the ipv6=off parameter can be specified.

Resolving of names into IPv6 addresses is supported starting from version 1.5.8.
By default, nginx caches answers using the TTL value of a response. An optional valid parameter allows overriding it:

resolver 127.0.0.1 [::1]:5353 valid=30s;
Before version 1.1.9, tuning of caching time was not possible, and nginx always cached answers for the duration of 5 minutes.
To prevent DNS spoofing, it is recommended configuring DNS servers in a properly secured trusted local network.
The optional status_zone parameter (1.17.1) enables collection of DNS server statistics of requests and responses in the specified zone. The parameter is available as part of our commercial subscription.

修改Nginx配置文件如下:

server {
    listen       9000;
    server_name  localhost;
    resolver 114.114.114.114 valid=60s;

    location /ncs/mobile/phone-verify {
        set $mobile www.cmpassport.com;
        proxy_pass https://$mobile/openapi/rs/tokenValidate;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_redirect off;
    
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

注意:需要将代理的域名设置为变量,否则域名无法动态解析

四、验证

上述配置中我们设置DNS解析缓存时间为60s,通过tcpdump抓取请求114.114.114.114 DNS服务器的包,在请求过程中,发现每间隔一分钟Nginx就会去重新请求DNS服务器,问题解决。

DNS解析抓包