基础环境
版本信息
- Centos 7.1
- nginx version: openresty/1.13.6.2
nginx配置信息
代码语言:javascript复制stream {
server {
listen 53 udp;
proxy_pass close_stream_backend;
}
upstream close_stream_backend {
server 10.0.1.2:53;
server 10.0.1.3:53;
}
}
异常问题
20个线程连续压测一分钟后开始交替出现两台目标机器已经宕机(单线程访问没什么问题),出现日志如下所示:
代码语言:javascript复制[error] 7184#0: *142585778 no live upstreams while connecting to upstream, udp client: 10.0.1.2, server: 0.0.0.0:53, upstream: "dns", bytes from/to client:40/0, bytes from/to upstream:0/0
主要有两个疑惑点:首先直接访问目标机器,目标机器处于正常访问状态,而且没什么压力;另外一点如果负载均衡目标服务机器两台改为一台反而不会出现这个异常,但是服务所在机器压力非常大;所以怀疑是nginx这台机器除了问题。
分析解决
- nginx官网查询了下负载均衡策略含义,大概意思是这样的,首先nginx默认的配置为轮询,负载均衡的各个模块组成了一个链表,每次从链表到头开始往后处理,如果连接失败了,就去尝试连下一个,如果所有的都失败了,就会进行quick recovery 把每个peer的失败次数都重置为0,然后再返回一个NGX_BUSY,然后nginx就会打印一条no live upstreams ,最后又回到原始状态,接着进行下次转发。查看配置文件,如果其中一台有一次失败,nginx就会认为它已经死掉,然后就会把以后的流量全都打到另一台上面,当另外一台也有一次失败的时候,就认为两个都死掉了,就开始打印错误日志。
- 第一个想到的办法就是增大重试次数和时间,
server 10.0.1.2:53 max_fails=5 fail_timeout=60s;
把max_fails从1改成5,有一定效果,no live upstreams
出现的概率变少了很多,但却没有完全消失。另外压测QPS上不去,还不如单台机器。 - 于是使用
tcpdump -i eth2 udp port 53 -s0 -XXnn
,通过wireshark分析后,压力测试工具产生数据已经全部发送到nginx所在机器,但是这台机器没有正确处理。 - 后来想了下Nginx底层采用的IO多路复用模型epoll,其epoll又是基于linux内核实现,于是看了下
/var/log/messages
内核日志,于是发现大量如下丢包日志:
kernel: nf_conntrack: table full, dropping packet.
为了证实问题,再次进行压力数据发送,发现只有再进行压力测试时才会出现这种内核丢包日志。怀疑是服务器访问量大,内核netfilter模块conntrack相关参数配置不合理,最终导致新连接被drop掉。
- 查看netfilter参数配置
sudo sysctl -a | grep conntrack
发现65536,于是我直接提升了4倍
sudo sysctl -w net.netfilter.nf_conntrack_max=262144
suod sysctl -w net.nf_conntrack_max=262144
执行sudo sysctl -p
使其立即生效,再次执行压力测试,发现内核不在出现丢包,nginx也不会出现如上错误日志,问题解决。
总结分析
本文主要通过分析nginx网络请求异常,然后定位为内核参数设置太小导致丢包,最后通过修改内核配置解决,但是conntrack又是什么?搜索之后发现conntrack是针对状态防火墙的。如果有兴趣,请阅读Netfilter的连接跟踪系统。它还包括Netfilter基本的框架。因此conntrack是用来创建记录连接状态以检查流量并避免DDoS安全问题。
另外如上分析问题的过程中,我直接把conntrack值设置为原来的四倍,这样是否合理?碰到这类问题,又该如何设置呢?下面给出一个公式:推荐大小:CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32)
。例如,在x86_64 OS中,我有8GB内存,因此我将它设置为8*1024^3/16384/2=262144
。以上就是我分析解决问题的整个过程,如果问题,请关注公众号、加我微信,一起讨论!