线上突然告警,zk集群不可用。随机看了zk节点挂了前的的机器的CPU与内存,内存100%,如下图所示。
首先我看了日志,由于线上配置的问题,所有的日志全部扔掉了。基于日志没有发现有价值的内容,接着又看了节点的网络,TCP连接数,磁盘IO,线程数,这些都是在正常范围内。
接着想着尝试启动一个节点,启动成功。但是通过top命令查看zk线程CPU利用率在30%左右,在正常情况下zk一般不耗内存(除非异常了)。
下面是最耗CPU的堆栈信息:
"NIOServerCxn.Factory:0.0.0.0/0.0.0.0:22288" daemon prio=10 tid=0x00007ff25c22e000 nid=0x9b24b runnable [0x00007ff17dab6000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
- locked <0x0000000601b704e8> (a sun.nio.ch.Util$2)
- locked <0x0000000601b71370> (a java.util.Collections$UnmodifiableSet)
- locked <0x0000000601b70de8> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
at org.apache.zookeeper.server.NIOServerCnxnFactory.run(NIOServerCnxnFactory.java:178)
at java.lang.Thread.run(Thread.java:662)
通过netstat命令查看连接状态,发现有许多TIME_WAIT
[root@host-x-x-x-x bin]# netstat -pano | grep 22288
tcp 0 0 ::ffff:127.0.0.1:15298 ::ffff:127.0.0.1:22288 TIME_WAIT - timewait (15.34/0/0)
tcp 0 0 ::ffff:x.x.x.x:22288 ::ffff:x.x.x.x:39904 TIME_WAIT - timewait (30.02/0/0)
查看zk的启动状态,结果如下(当然,集群要全部启动后才能看到是master/follower):
当然过了几分钟,该节点也挂了。只好继续查找原因,当节点启动后,虽然集群不可用,但是还是在尝试建立TCP连接,至于为啥是TCP四次挥手中的TIME_WAIT状态,我猜测是由于在zk集群节点挂后,我马上建立重启,但集群未生效,节点通过TIME_WAIT允许老的重复分节在网络中消逝 (端口号还是保持不变)。
该节点启动不一会儿(有正常的端口号),端口又挂了。查看内存,发现内存使用率陡增(集群不可用,为啥内存还陡增了?虽然zk集群不可用,但是客户端与该ip在尝试建立socket连接,不断分配端口文件描述符……期望有更好的理由[吃瓜])。首先想到的是调整内存。
确实调整内存后很有效果 ,不再出现内存陡增情况。
接着继续看zk配置的节点列表,总共有11个docker,但有6个docker不可用。这就很明显了,过半的节点不可用,当然集群不可用了。因此将server保留最新的5个节点,启动zk节点,集群可用。
虽然问题解决了,但遗留了一个问题:在集群不可用时,启动其中一个zk节点,为啥短时间内内存使用率很高?
没有请求到zk,不代表tcp请求没建立。这套zk客户端ip很多,虽然请求被拒絕了,但是还是在试着建立socket连接,尝试进行TCP连接,这也将消耗大量的资源(但还是不足以说明内存使用率过高)
后续思考了一下,由于节点启动后,会进行数据同步操作,对内存的消耗很大。由于数据量很大,节点启动后进行数据同步,对内存的消耗很大,而zk配置的是默认的内存,可能会不断的对内存进行重新分配,导致堆内存接近docker内存,如下图,实际docker的最大内存为8G 。继续进行数据恢复则会导致内存溢出。