1 问题背景
2 探索
2.1 各节点端口的连通性统计
2.2 未重启节点状态检查
2.3 通过日志文件寻找线索失败
2.4 线上ZK节点添加端口探测监控
2.5 柳暗花明
3 集群恢复方案制定
3.1 测试环境还原线上集群
3.2 测试Codis集群和demo服务连接到测试ZK集群
3.3 关键要素测试
3.4 方案选择
4 方案落地
4.1 日志滚动调整配置文件
4.2 准备3节点小集群
4.3 老节点工作路径配置调整与滚动操作
5 总结
我司缓存Codis依赖的ZooKeeper服务,在探索异地机房数据库高可用方案时,期望通过添加observer的方式实现异地容灾,但在方案实施阶段出现了添加observer节点失败,并发现选举端口不通的日志记录,复现状态如下:
我
:???,先telnet一下leader的3888端口试试
我
:3888 真的不通了?!!新节点不能加入集群!!!但是老集群读写状态是正常的,这情况太离谱了......
我
:重启老节点,集群应该会重新发现节点,顺便恢复选举端口
......
我
:完了...老节点端口通了但是也回不到集群了??!
捋清操作时间线,同步ZK集群现状,向各位同事大佬求助,尝试将集群恢复到正常状态。
为了填平赌上职业生涯重启ZK节点的坑,开启了本篇的探索之旅。
close_wait的来源都是安全扫描的IP地址但是根本没有成功创建连接
因为使用的默认配置启动服务,ZK日志没有有效的切割和生命周期管理,仅使用 ">" 清空处理过,当前最大的日志已经堆积了20G之多,遗憾的是最后通过排查也没有找到有效信息。
监控添加后大约1个小时左右,重启的新节点3888选举端口又不通了!!
通过ITCP架构群发出issue,得到了去哪儿网小伙伴的积极回应,对比两方的jstack,发现我们的ZK缺少了QuorumCnxManager$Listener线程!!!
这个线程是负责监听3888选举端口,并accept选举请求的。我们未重启节点里这个线程都没了!!!
由此一切都解释通了,多数节点的选举功能失效,当有节点想加入集群时,当然不可能通过。
具体的排查验证过程可查阅往期精品文章 ↓↓↓
至于这个issue问题的修复也在 ZooKeeper-3.4.7版本完成,使用高版本的读者朋友们可以放心了~
测试环境使用线上snapshot恢复搭建 ZooKeeper-3.4.6 版本5节点集群,myid 和 角色完全与线上一致,然后通过telnet -1 将所有节点3888选举端口打挂掉,重启测试集群6号/8号两节点形成以下状态。
确认Codis-Proxy在测试ZK集群内注册了临时节点;
确认服务在使用不同版本客户端时,可以从测试ZK集群获取到Codis-Proxy地址信息创建连接且读写操作正常。
ZooKeeper-3.4.6以后版本,使用版本号3.4.13,是否完美向下兼容,镜像恢复是否可行,操作流程顺序是什么?
在ZK集群和ZK节点服务均正常情况下,Codis-Proxy连接到ZK集群会向 /jodis/{CodisProductName}/ 写入 proxy-{proxyToken} 临时节点信息,ZK集群恢复的判断条件之一必须是Codis-Proxy临时节点注册没有异常。
服务启动后客户端会缓存ZK节点域名解析地址(形如codiszk.domain.com:2181)无法变更,只能原地操作集群恢复;
客户端连接到ZK集群后,能否获取到Codis-Proxy临时节点,并完成Codis集群数据的读写功能。
指定存储路径,按日切割,如何建立完整的日志滚动生命周期管理规则。
关键要素经过多轮测试,汇总,总结以下两套备选方案
准备新的ZooKeeper-3.4.13工作目录,老集群写入Codis-Proxy永久节点数据,将6号和8号老节点停服,在新目录使用老集群最新镜像启动新服务,然后老集群内存活的3节点顺序关闭的同时启动新版本ZK节点,形成3.4.13版本新集群。
此方案优点:
缺点:
由于6号/8号两节点已经不提供服务,老集群数据短时间内也不会有变动,索性重新搭建一套3节点集群,数据与线上一致,然后重新扩容恢复到5节点集群!
准备新的ZooKeeper-3.4.13工作目录,老集群写入Codis-Proxy永久节点数据,将6号和8号老节点停服并引入一个新节点,在新节点工作目录使用老集群最新镜像启动服务,然后两个离线节点机器与临时新节点组成3节点小集群,此时小集群的数据与线上一致且刷入了Codis-Porxy永久节点数据,已经可以提供服务,后面只需要尽快把老集群节点关闭转为高版本新节点重新加入新集群就好了。
此方案优点:
缺点:
人生真是寂寞如雪呀...
2024年的1月都比平时更冷了一些...
某天后半夜工位周围聚集了一圈大佬,反复确认了以下恢复流程。
log4j被爆出过安全性漏洞,ZooKeeper-3.4.13使用了log4j 1.2.17,在没有明确引用SocketServer创建监听服务时,安全性较高,如果想完全解决需要升级log4j 2.x版本或者升级到ZooKeeper更高版本。
老ZK集群节点6号/7号/8号/9号/10号都必须在实例本机ZooKeeper-3.4.13版本新工作路径操作节点替换,业务客户端缓存的ZK节点DNS解析IP地址不能变。
从配置文件conf/zoo.cfg确认dataDir(镜像保存路径)和 dataLogDir(事务日志保存路径);
{dataDir}路径更改myid: 老集群6号节点替换为2号,老集群8号节点替换为3号,1号为临时引入实例,最后清理掉;
配置文件conf/zoo.cfg注释线上7号/9号/10号/添加1号,关闭6号/8号实例,删除{dataDir}/version-2 和 {dataLogDir}/version-2 目录;
确保线上ZK集群已经刷了全量Codis-Proxy永久节点数据到/jodis下,从10号(leader)拷贝dataDir和 dataLogDir完整目录到 1号 ZK工作路径下,启动1号,再启动2号/3号节点(2号/3号仍在接收线上请求,后启动直接恢复全量数据即可接收连接提供服务)。
同样在实例本机ZooKeeper-3.4.13版本工作路径操作,各节点先提前备好server信息:
7号实例: 调整myid为4号,conf/zoo.cfg 调整实例信息为server 1234,4条;
9号实例: 调整myid为5号,conf/zoo.cfg 调整实例信息为server 12345, 5条;
10号实例: 调整myid为6号,conf/zoo.cfg 调整实例信息为server 123456, 6条;
关闭 7号/9号/10号 全部老实例,切换到ZooKeeper-3.4.13工作目录操作(关闭任意实例后,Codis-proxy即断连并向新集群注册);
启动4号新实例,加入新集群,调整123配置文件,添加4号server信息,然后顺序重启实例231(1号为leader,最后重启),此时4号为leader;
启动5号新实例,加入新集群,调整1234配置文件,添加5号server信息,然后顺序重启实例1234,此时5号为leader;
启动6号新实例,加入新集群,调整12345配置文件,添加6号server信息,然后顺序重启实例12345,此时6号为leader;
关闭1号实例,调整23456配置文件,注释1号server信息,然后顺序重启实例23456,集群恢复,此时5号为leader。
操作期间反复的确认各ZK节点的连接情况,服务状态,集群状态,日志信息
最终...
[root()@XShellTerminal bin]#echo mntr |nc localhost 2181
zk_server_state leader
zk_synced_followers 4.0
......
ZK集群恢复完成,升级完成,日志切割完成
关于作者
王嗣鑫,DBA,转转缓存/运维平台开发负责人,平平无奇的一个踩坑奇才。
想了解更多转转公司的业务实践,欢迎点击关注下方公众号: