slan's blog

Face loneliness, or seek comfort in temporary escapes?

docker在高流量情况下出现丢包,网络超时

2016-03-27 10:22:00 +0000 UTC

docker在高流量情况下出现丢包,网络超时

概要

docker在高流量情况下(频繁的进行tcp连接)可能导致网络丢包,超时等网络问题。原因是docker的NAT机制,启用了iptables进行网络中转,可能触发 nf_conntrack table爆掉的问题,导致网络丢包,超时。这本质来说不是docker问题,但是在应用docker时不得不注意这一机制。

现象

某次对一个web应用进行压测,nginx+php-fpm,典型的web环境,不过是跑在docker中。压测一开始很正常,逐步加量,到某个点出现瓶颈,无法访问504,502。系统负载正常,cpu内存占用都很低,网络带宽占用也不高。

分析过程

既然系统没到瓶颈,那么应该是某个配置问题,导致负载上不去,先看fpm的error日志,其中出现:

php_network_getaddresses: getaddrinfo failed: Name or service not known dsn:mysql:host=****;port=3306;dbname=****;charset=utf8

域名解析失败,于是写死host,再次压测,还是一样的情况,不过error中换了日志:

PHP Fatal error:  Uncaught PDOException: SQLSTATE[HY000] [2002] Connection timed

连接数据库出现超时

由此,基本怀疑是系统的网络出现问题,导致dns服务不正常,tcp连接也出现超时,查看netstat,有大量的time_wait,是不是频繁连接数据库,导致端口资源分配完了呢?

为了验证,调整了net.ipv4.tcp_tw_recycle,net.ipv4.tcp_tw_reuse等参数,time_wait是下去了,但网络还是一样不正常。

一时找不到原因,用strace跟踪了下,发现fpm中,同样的请求数,一分钟前是正常,无压力,一分钟左右后,就突然全部卡住,strace日志:

04:33:25.883957 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 6 <0.000045>

04:33:25.884065 fcntl(6, F_GETFL)       = 0x2 (flags O_RDWR) <0.000027>

04:33:25.884202 fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000035>

04:33:25.884312 connect(6, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("121.199.***.***")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000048>

04:33:25.884428 poll([{fd=6, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 0 (Timeout) <60.047186>

connect数据库出现60秒的超时,不能访问的原因于是就基本确定了,网络异常导致fpm连接数据库超时,堵塞了所有的fpm进程,导致后续访问完全timeout.这样cpu负载确实是不高的。出现这种情况,调整fpm的进程数是没用的,有多少会堵多少。

继续查看为什么网络出现原因,翻系统日志(/var/log/syslog),发现:

Mar 26 13:02:45 iZ23cf3dp1nZ kernel: [ 7548.212230] nf_conntrack: table full, dropping packet

Mar 26 13:02:45 iZ23cf3dp1nZ kernel: [ 7548.652083] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.276076] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340070] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340081] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340089] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340094] nf_conntrack: table full, dropping packet

Mar 26 13:02:47 iZ23cf3dp1nZ kernel: [ 7550.364029] net_ratelimit: 26 callbacks suppressed 

google下,发现是iptables的nf_conntrack table爆掉了,而这个一般是系统有做NAT中转服务才会用到,NAT???为什么会用到NAT???查看iptables -l恍然大悟,有host机对docker container的网络转发规则,于是定位到了原因,修改net.ipv4.netfilter.ip_conntrack_max为原来四倍,再压测一切正常,继续加量,真正的达到了系统瓶颈,cpu跑满,负载飙升。

解决方案

我是直接调整了nf_conntrack table size完事,因为我们的服务瓶颈不在网络,然而有时候不一定有效,有可能你们的流量更大,tcp连接更频繁,调整size也有极限,可能会带来内存占用很大的问题,需要权衡。

推荐google下nf_conntrack: table full, dropping packet,原因以及应对方案分析很多,不再重复。