Nginx反代cloudflare站点间歇性502故障分析与解决:no live upstreams while connecting to upstream

最近反代cloudflare的站点出现间歇性502故障,开始以为是 config 少設定了一些參數,後來才發現是 域名動態IP 的問題。

Nginx 在啟動時會向 DNS server 查詢 Domain 對應的 IP,之後就會 cache IP,直到下次重新載入 config 才會再更新,所以當我們服務的 IP 改變之後,Nginx 就會開始出現 Connect failed, Connection timeout 等 error.

Nginx错误日志分析:

2025/01/1 01:02:22 [error] 123#0: *123 no live upstreams while connecting to upstream, client: 124.xxx, server: abc, request: "GET /1.jpg HTTP/2.0", upstream: "https://xxx/1.jpg", host: "abc", referrer: "https://abc/1.html"

反向代理服务器出现no live upstreams while connecting to upstream, 也就说Nginx检测不到任何存活的上游服务器。

调查错误

1、上游服务器状态正常
2、反代服务器负载状态正常
3、反代理服务器内其他站点状态正常,反代站点间歇出现502错误

源服务器确认正常,反代服务器只有反代站点间歇性502错误。所以判断问题出现在CloudflareCDN,CloudflareCDN部分IP节点可能因为过载原因响应502。

反代流程:

客户端<>反代服务器<>CloudflareCDN<>源服务器

解决方案:

1、Nginx >=1.27.4 社区版,允许在 upstream 中调用 resolve 指令解决(以往此功能是Nginx Plus版本专属).

2、Nginx <1.27.4 社区版,不使用 upstream,改用 set 將 url 設為变量,再传递給 proxy_pass.

3、Hosts指定cloudflare CDN IP

方案1 - Nginx | upstream配置:

#nginx.conf
http {
resolver 1.1.1.1 valid=2s ipv6=off;
#valid=2s 每 2 秒解析一次域名,确保解析到最新 IP。

upstream backend {
zone upstream_dynamic 128k;# 共享内存,用于动态管理 upstream
server abc1.com:443 fail_timeout=10s max_fails=5 resolve;
#resolve: 确保 Nginx 动态解析 DNS。resolve 指令告诉 Nginx 在每次请求时解析域名,这对于动态域名(例如,DNS 解析变化频繁的情况)
server abc2.com:443 backup fail_timeout=10s max_fails=5 resolve;
server abc3.com:443 fail_timeout=10s max_fails=5 resolve;
server abc4.com:443 fail_timeout=10s max_fails=5 resolve;
keepalive 1024;# 允许长连接,减少TCP建立开销
}

#fail_timeout:设置服务器失败后多长时间重新尝试。
#max_fails:在 fail_timeout 时间内允许的最大失败次数。
...

keepalive_timeout 1000 #客户端与服务器之间空闲连接的超时时间

proxy_connect_timeout 300;
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_buffer_size 512k;
proxy_buffers 16 256k;
proxy_busy_buffers_size 512k;
proxy_temp_file_write_size 8m;
proxy_next_upstream error timeout invalid_header http_500 http_503 http_502;
}

resolver 是設定 DNS server, valid 是指 DNS server 回傳的 IP 會被 chache 的時間,在這個時間內不會再向 DNS server 詢問 IP.

設定完之後,resolver 就會按照 DNS server 的 TTL 來做更新。

注意:upstream内使用resolve指令需要将Nginx社区版更新至>=1.27.4版本才能使用或者购买Nginx商业版本

https://nginx.org/en/docs/http/ngx_http_upstream_module.html

方案2 - Nginx | set配置

將 url 設為变量,再傳給 proxy_pass:

http
    {
resolver 1.1.1.1 valid=2s ipv6=off;
...

server {
    set $backend https://abc.com;
...
#PROXY-START/
location ^~ / {
    proxy_pass $backend;
    ....
    }
  }
}

方案3 - hosts配置

修改本地hosts文件或nginx配置文件,指定cloudflare CDN的IP

https://www.cloudflare.com/zh-tw/ips/

#/etc/etc/hosts

1.1.1.1 abc.com(反代域名)

刷新 DNS缓存:

systemctl restart systemd-resolved
systemctl restart NetworkManager

验证 /etc/hosts 是否生效:

ping abc.com

此方案一般不被推荐

参考资料:

https://nginx.org/en/docs/http/ngx_http_upstream_module.html

https://wshs0713.github.io/posts/4c986e0c/

dark
sans