当前位置:首页 > 问答 > 正文

Redis集群里节点突然消失了,查了半天还是找不到到底咋回事

基于网络技术社区讨论、开发者经验分享以及Redis官方文档中关于故障排查的思路综合而成)

那天下午,系统监控突然开始尖叫,仪表盘上代表Redis集群健康状态的那个绿色图标,毫无征兆地变成了刺眼的红色,我心里“咯噔”一下,赶紧登录到集群管理工具查看,果然,六个节点中的一个,我们暂且叫它“节点C”,它的状态显示为“FAIL”,就像是凭空蒸发了一样,从集群的视野里彻底消失了。

第一反应是网络问题,这太常见了,机器之间的心跳线稍微抖一下,就可能导致误判,我立刻跳转到那台出问题的服务器上,先用最简单的ping命令去探探路,奇怪,网络是通的,延迟也很正常,不甘心,又用telnet命令去连接节点C的Redis服务端口,比如6379,连接也能成功建立,这说明至少从网络层面,节点C本身是可达的,服务进程也可能还在跑,那为什么集群认为它挂了呢?

我怀疑是集群内部的心跳通信出了问题,Redis集群节点之间依靠一种叫“Gossip”的协议来互相传递消息和状态,就像几个人在办公室里交头接耳传播消息一样,如果某个节点因为压力太大,比如CPU百分之百了,或者内存交换(SWAP)得太厉害,导致它虽然没死机,但已经没力气“说话”了,超过了集群配置的cluster-node-timeout(节点超时时间),其他节点就会认为它已经失效,从而把它踢出集群,我立刻登录到节点C的服务器,输入top命令查看资源情况,CPU使用率只有百分之十几,内存虽然用了不少,但还在可控范围内,并没有触发大量的SWAP,看起来又不像是资源瓶颈。

会不会是节点C的Redis进程自己出问题了呢?比如发生了死锁,或者内部逻辑错误,导致它虽然进程还在,但已经无法正常响应集群的通信了?我用ps -ef | grep redis命令查看,Redis进程确实好好地在那里运行着,我又去查看Redis的日志文件(通常叫redis.log),这是最有可能发现线索的地方,我小心翼翼地用tail -f命令盯着日志,同时尝试从其他节点手动执行一个cluster meet命令,想重新把节点C拉回集群,果然,在节点C的日志里,我看到了一些不寻常的报错信息,提到了“Timeout receiving messages from node”(从节点接收消息超时)之类的字眼,但这依然很模糊,是它收不到别人的消息,还是别人收不到它的消息?问题似乎指向了更深层的网络或系统配置。

既然直接登录到机器上查不出所以然,我开始把目光投向更底层的基础设施,我联系了运维的同事,问他最近有没有对那台宿主机的网络策略做过调整,他查了一下记录,一拍大腿说:“哎呀,大概一小时前,为了安全起见,我们在防火墙上加了一条新的规则,限制了一些非标准端口的出入站流量。” 我心里一紧,赶紧问:“Redis集群节点之间通信用的端口,就是服务端口加上10000的那个(比如16379),你们有没有放行?” 他愣了一下,说:“这个……好像没有特别注明,可能被默认规则拦掉了。”

问题很可能就出在这里!Redis集群节点之间通信,除了客户端连接的服务端口(如6379),还需要一个专门的集群总线端口(通常是服务端口+10000)来进行节点间的数据交换、故障探测和配置更新,如果这个总线端口被防火墙阻断,那么节点之间就无法正常“对话”,虽然节点C的Redis进程本身健在,也能处理客户端的直接连接(如果6379端口开放的话),但它已经成了集群中的“聋子”和“哑巴”,无法与其他节点交换心跳信息,其他节点在等待cluster-node-timeout规定的时间后,自然就把它标记为失效了。

我们立刻让运维同事调整了防火墙规则,确保所有集群节点之间的服务端口和集群总线端口都是双向开放的,我尝试重新将节点C加入到集群中,这一次,操作成功了,集群状态逐渐恢复正常,数据也在后台开始重新同步。

经过这次折腾,我深刻体会到,排查这种“节点突然消失”的问题,不能只盯着Redis本身,它就像一个迷路的人,你可能在他常去的几个地方(服务端口)能找到他,但他和队伍联系的对讲机频道(集群总线端口)被掐断了,所以队伍认为他失踪了,必须从一个更全局的视角,把Redis集群看作一个依赖网络、系统、配置等多个环节的完整系统来排查,特别是那些容易被忽略的“隐藏”端口和基础设施的变更。

Redis集群里节点突然消失了,查了半天还是找不到到底咋回事