公司新上了两台 Web 服务器,配置完 Nginx 负载均衡后,发现其中一台始终显示 unhealthy 或干脆不出现——后台日志里也看不到请求打进来。不是防火墙没关,也不是端口没开,更不是服务没起,就是卡在“注册不上”这一步。
先看健康检查是不是真通了
很多负载均衡器(比如阿里云 SLB、AWS ALB、Nginx upstream 的 health_check)默认会发一个 HTTP HEAD 或 GET 请求到后端的 /health 或 / 路径。但你的应用可能根本没暴露这个接口,或者返回了 404、502,甚至返回 200 但 body 是空的——某些 LB 对空响应体也会判为失败。
手动模拟一下:
curl -I http://192.168.1.100:8080/health
注意看状态码和 Content-Length。如果返回 302 跳转到登录页,或返回 HTML 页面但 LB 只认 JSON 格式,照样挂。
源端口被限制,连接直接被 reset
有些后端机器启用了严格的 outbound 规则,比如 iptables 默认 drop 了非 established 的响应包。LB 发起健康检查时走的是随机源端口,后端回包若被拦截,就会表现为“超时”或“连接拒绝”。用 tcpdump 抓包最直接:
tcpdump -i eth0 port 8080 -nn
看到请求进来但没回包?十有八九是本地防火墙或 SELinux 拦住了。
主机名解析搞错了
如果你在负载均衡配置里填的是 backend-01.internal,而后端服务器 /etc/hosts 里没配这个别名,或者 DNS 解析慢、失败,会导致注册阶段握手失败。更隐蔽的是:某些 LB 会用 hostname 做证书校验或路由标记,而你的服务启动时读取的 hostname 是 localhost 或容器 ID,跟注册名对不上。
查一下:
hostname
cat /etc/hosts
容器环境里的常见坑
Docker 或 Kubernetes 下,后端服务监听的是 127.0.0.1:8080,但 LB 从外部访问需要绑定 0.0.0.0:8080。Java 应用加了 --bind-address=127.0.0.1,Node.js 用 server.listen(8080, '127.0.0.1'),都会导致 LB 连不上。
改法很简单:把监听地址换成 0.0.0.0 或留空(如 Express 中 app.listen(8080))。
时间不同步也会拖后腿
用 TLS 健康检查时,如果后端服务器时间比 LB 早或晚超过 5 分钟,证书会被认为过期或未生效。尤其在没有 NTP 同步的测试机上很常见。跑一句就能确认:
date -R
对比 LB 控制台显示的时间,差得离谱就赶紧 ntpdate pool.ntp.org 同步一把。