文章轉載自:https://segmentfault.com/a/1190000022365954
1 問題現象
我們使用 Nginx 的時候,經常會用到 Proxy 功能,為了方便管理,后端站點或者服務一般用域名來表示。
在運維過程中,有一次后端需要切換,按理說,只需要更改 DNS 解析到新的 IP 就能完成切換,然后發現更改 DNS 解析后,走 Nginx 怎么也訪問不了后端,而在 Nginx 機器上直接 curl 后端是沒有問題的。
問題找了半天發現是 Nginx 會緩存 DNS 解析,需要重載 Nginx 才會刷新。
2 問題重現
在測試機(centos6.5)上安裝 Nginx(1.10.2),寫入測試用的 Nginx Server 配置,配置非常簡單,就是 www.test.com 轉給 proxy.test.com,最后會返回 200 OK
server {
listen 80;
server_name www.test.com;
location / {
proxy_set_header Host proxy.test.com;
proxy_pass http://proxy.test.com;
}
}
server {
listen 80;
server_name proxy.test.com;
location / {
return 200 'OK';
}
}
然后在 /etc/hosts 文件下寫入本地解析,www.test.com 解析到本機,proxy.test.com 解析到一個不存在的地址
127.0.0.1 www.test.com
10.10.10.114 proxy.test.com
重啟 Nginx
/usr/sbin/nginx -s reload
執行測試,手動 curl www.test.com,訪問不存在的 IP 會被 hang 住,最后超時並打印了一條錯誤日志
2020/04/14 10:10:09 [error] 21634#0: *3 upstream timed out (110: Connection timed out) while connecting to upstream, client: 127.0.0.1, server: www.test.com, request: "GET / HTTP/1.1", upstream: "http://10.10.10.114:80/", host: "www.test.com"
這個時候,我們再調整 hosts 文件,改為正確的 IP
127.0.0.1 www.test.com
127.0.0.1 proxy.test.com
然后再手動 curl www.test.com,發現還是被 hang 住,查看日志,發現超時日志里面寫的還是舊 IP
2020/04/14 10:17:30 [error] 21634#0: *5 upstream timed out (110: Connection timed out) while connecting to upstream, client: 127.0.0.1, server: www.test.com, request: "GET / HTTP/1.1", upstream: "http://10.10.10.114:80/", host: "www.test.com"
使用 /usr/sbin/nginx -s reload 命令重載 Nginx,刷新緩存,再次請求 curl www.test.com,就沒有問題了
3 問題探索
我也沒看過 Nginx 代碼,對於 DNS 緩存問題還是只能手動探索
3.1 什么時候緩存的
常用 Nginx 的同學都知道,如果 Nginx proxy_pass 里面的域名不能解析的話,是無法啟動 Nginx 的,我看網友說啟動的時候僅僅是檢測是否能解析,只有在第一次請求的時候才會緩存,那么還是手動測試一下。
先設置為錯誤 IP,並重啟 Nginx
[root@chengqm ~]# grep 'proxy.test.com' /etc/hosts
10.10.10.114 proxy.test.com
[root@chengqm ~]# /etc/init.d/nginx restart
Stopping nginx: [ OK ]
Starting nginx: [ OK ]
在沒有請求前,把解析改為正確 IP
[root@chengqm ~]# sed -i 's#10.10.10.114 proxy.test.com#127.0.0.1 proxy.test.com#g' /etc/hosts
[root@chengqm ~]# grep 'proxy.test.com' /etc/hosts
127.0.0.1 proxy.test.com
首次請求
[root@chengqm ~]# curl www.test.com
還是 hang 住並打印一條日志
2020/04/14 10:32:55 [error] 23405#0: *1 upstream timed out (110: Connection timed out) while connecting to upstream, client: 127.0.0.1, server: www.test.com, request: "GET / HTTP/1.1", upstream: "http://10.10.10.114:80/", host: "www.test.com"
結論: 在 1.10.2 版本 Nginx 中,啟動 Nginx 的時候就會緩存 DNS 解析,其他版本還沒有試。
3.2 DNS輪詢情況下,會不會只緩存一個
既然 Nginx 會做 DNS 緩存,那么使用 DNS 輪詢的情況下,只緩存第一個解析的 IP 還是所有,我們來測試一下。
在本機搭建 Named 服務,並加上兩個域名的解析,其中,proxy.test.com 使用了 DNS 輪詢,一個是錯誤 IP,一個是正確IP
www IN A 127.0.0.1
proxy IN A 127.0.0.1
proxy IN A 10.10.10.114
本地 DNS 地址指向本機
[root@chengqm ~]# cat /etc/resolv.conf
nameserver 127.0.0.1
重啟 Nginx
[root@chengqm ~]# /etc/init.d/nginx restart
Stopping nginx: [ OK ]
Starting nginx: [ OK ]
手動請求 www.test.com,可以看到,第一次請求成功,第二次卡了一段時間后返回成功
查看第二次請求的日志,發現有一個失敗的和成功的,意味着 Nginx 確實去請求了錯誤的 IP,當不通的時候就會換下一個 IP 進行請求
結論: Nginx 會緩存所有 DNS 解析
4 緩存問題的解決方案
4.1 每次更改 DNS 解析都重載 Nginx
重載 Nginx 一定會刷新緩存,這是最保險也是最麻煩的一種方案,如果體量小還可以接受,如果 Nginx 實例比較多就有些困難,除非有批量運維工具幫忙。
4.2 使用 Nginx 的 resolver
我們在使用 Nginx 過程中,有時需要根據 Url 傳值動態選擇 host 進行代理轉發,這種模式下,一開始是不會去進行 DNS 解析的,只有請求的時候才會進行 DNS 解析,並且要設置 resolver 指定 DNS 服務器 IP。
這個時候,我們就可以使用 resolver 語法來解決 DNS 緩存的問題,比如說,我在原來的 Nginx 配置里指定 DNS IP,並設置緩存 60 秒。
server {
listen 80;
server_name www.test.com;
resolver 127.0.0.1 valid=60s;
resolver_timeout 3s;
set $proxy_url "proxy.test.com";
location / {
proxy_set_header Host proxy.test.com;
proxy_pass http://$proxy_url;
}
}
4.3 使用模塊nginx-upstream-dynamic-servers
模塊地址: nginx-upstream-dynamic-servers
該模塊在第一次啟動的時候會進行一次解析,解析完后,在 DNS 服務器設定的 TTL 過期時間內不會再次更新,過期后會再次發起解析請求
使用方法
http {
resolver 8.8.8.8;
upstream example {
server example.com resolve;
}
}
使用這種方法的時候,DNS TTL 時間需要設置短一些。
4.4 使用模塊 ngx_upstream_jdomain
文檔對應地址: domain_resolve
ngx_upstream_jdomain 模塊是一個依賴 DNS 解析實現的 upstream 負載均衡,該模式下,允許使用域名來寫 upstream 后端。該模塊默認情況下,會每秒做一次 DNS 解析,使用方法如下
http {
resolver 8.8.8.8;
resolver_timeout 10s;
upstream backend {
jdomain www.baidu.com port=80 interval=5; # 每 5 秒解析一次
}
server {
listen 8080;
location / {
proxy_pass http://backend;
}
}
}
4.5 使用其他版本 Nginx
Tengine Tengine 的 ngx_http_upstream_dynamic 模塊可以提供動態的 DNS 解析
NGINX Plus Plus版本提供了動態解析的語法