有一天,接到用户电话,请求帮忙排除一个问题。
用户的服务是部署在k8s集群上的,通过nodePort向集群外暴露服务,前端使用了nginx做负载均衡,nginx转发到集群中三台主机上的nodePort。
问题的现象是,nginx连后端nodePort偶尔会发生connection refused,因为连接后端失败后,nginx会临时停止向该后端服务器发送请求,假如有多个后端服务器同时发生次情况时,甚至会造成服务中断。
nginx日志中的报错如下:
2020/06/01 16:30:45 [error] 2956#2956: *2309354 connect() failed (111: Connection refused) while connecting to upstream, client: x.x.x.x, server: xxxxxxx.xxxxx.xxxx, request: "GET /xxx/xxx HTTP/1.1", upstream: "http://x.x.x.x:300xx/xxx/xxx", host: "x.x.x.x:8080"
tcpdump抓包显示,后端服务器收到nginx发送的SYN报文之后,直接回复了RST。
一开始听用户说是通过nodePort暴露服务时,就有点怀疑是因为SNAT问题导致的内核丢包。于是查看nf_conntrack。
conntrack -L | grep 300xx
发现该300xx端口确实是已经有一个连接在使用:
ipv4 2 udp 17 1 src=x.x.x.101 dst=x.x.x.102 sport=300xx dport=8472 [UNREPLIED] src=x.x.x.102 dst=x.x.x.101 sport=8472 dport=300xx mark=0 zone=0 use=2
因为客户的集群下远多于三台主机,并且nodePort在集群中所有工作阶段上都可以正常转发,于是建议客户把nginx后端服务器的实例数加大,果然,变更实施后,这个问题不再出现,性能反而有明显的提升。
但是,其实nodePort使用的SNAT机制总是有一定的概率会丢包,建议不要在生产环境中使用nodePort,而应该通过ingress暴露服务。