☰
多机Redis集群搭建卡在Waiting for the cluster to join?总线端口与cluster-announce-ip排查指南
2026/10/22 14:05:14 网站建设 项目流程

1. 为什么单机跑通的Redis集群脚本换到多机就卡住了

很多人第一次搭Redis集群都是在单机上用redis-cli --cluster create一把梭,三个主三个从,端口从7000排到7005,回车一敲,All 16384 slots covered,收工。然后老板说"生产环境要三台机器做高可用",你把同样的脚本复制过去,改了几个IP,结果终端上就死死卡在一行:

Waiting for the cluster to join

这一卡,可能卡几分钟,也可能永远卡下去。你去看日志,每个节点都显示Cluster state changed: ok,cluster nodes里也能看到彼此,但就是cluster info里cluster_state:fail,cluster_known_nodes数量对不上,槽位分配永远走不完。

这个问题的本质,是Redis Cluster的总线端口(cluster bus port)没有打通。Redis集群的节点之间除了用客户端端口(比如7000)通信,还会额外开一个客户端端口 + 10000的总线端口(比如17000)用于gossip协议、故障检测、配置纪元传播。单机搭建时这两个端口都在本机,防火墙根本不拦;一旦跨机器,只要有一台机器的总线端口没放行,集群就永远凑不齐法定票数,Waiting for the cluster to join就会一直挂着。

我见过太多人在这上面耗掉一整个下午,反复重启节点、删nodes.conf、换Redis版本,其实方向从一开始就错了。这篇内容我会把多机Redis集群搭建的完整链路拆开讲:从网络规划、配置模板、启动顺序,到Waiting for the cluster to join的逐层排查方法,再到集群建好之后的验证和常见运维坑。适合已经会单机搭集群、但一上多机就翻车的同学,也适合想搞清楚Redis Cluster底层通信机制的人。

2. 多机集群的网络规划与端口账要先算清楚

2.1 客户端端口和总线端口的对应关系

Redis Cluster每个节点实际监听两个端口:

端口类型默认规则作用是否可自定义
客户端端口port配置项,如7000处理普通读写命令、CLUSTER命令可自定义
总线端口port + 10000,如17000节点间gossip通信、故障检测、槽位迁移协调由cluster-port决定,默认偏移10000

关键点在于:总线端口不是你想关就能关的,只要开启了cluster-enabled yes,Redis就会强制监听总线端口。而且这个端口号默认是客户端端口加10000,如果你把客户端端口设成7000,总线端口就是17000;设成6379,总线端口就是16379。

我建议在规划阶段就把端口表列出来,三主三从的典型布局如下:

机器节点角色客户端端口总线端口
10.0.0.11master-1700017000
10.0.0.11slave-1700117001
10.0.0.12master-2700017000
10.0.0.12slave-2700117001
10.0.0.13master-3700017000
10.0.0.13slave-3700117001

注意这里每台机器上跑两个节点,端口复用是没问题的,因为IP不同。但如果你图省事,三台机器都用7000,那总线端口就都是17000,跨机器通信时防火墙规则要按IP+端口精确放行。

2.2 防火墙和安全组必须放行的规则

这是Waiting for the cluster to join最高频的根因。很多人只放行了7000,忘了17000。正确的放行规则应该是:

  • 每台机器的客户端端口,对集群内所有机器放行
  • 每台机器的总线端口,对集群内所有机器放行
  • 如果用了requirepass,还要保证所有节点密码一致

用iptables的话,规则大概长这样:

# 假设集群内网段是10.0.0.0/24 iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 7000 -j ACCEPT iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 7001 -j ACCEPT iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 17000 -j ACCEPT iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 17001 -j ACCEPT

云环境的安全组同理,入方向规则要覆盖所有节点的两个端口段。这里有个容易忽略的点:安全组规则是双向的,A机器能连B机器的17000,不代表B机器能连A机器的17000,两边都要配。

2.3 绑定地址和protected-mode的坑

redis.conf里有两个配置项会直接影响跨机通信:

bind 0.0.0.0 protected-mode no

bind 127.0.0.1是单机搭建时的默认写法,跨机器时如果没改,节点只监听本地回环,外部根本连不上。protected-mode yes在没设密码且bind不是回环时,会拒绝外部连接。生产环境我建议是设密码 +protected-mode yes,但搭建调试阶段可以先protected-mode no把链路跑通,再补安全配置。

提示:cluster-announce-ip和cluster-announce-port这两个配置项在多机环境下非常关键。如果你的机器有多个网卡,或者走了NAT,节点自动探测到的IP可能不是你以为的那个,必须手动指定。

3. 配置文件模板与启动顺序的实操细节

3.1 一份可直接复用的多机节点配置

以10.0.0.11上的7000节点为例,redis-7000.conf内容如下:

port 7000 cluster-enabled yes cluster-config-file nodes-7000.conf cluster-node-timeout 15000 cluster-announce-ip 10.0.0.11 cluster-announce-port 7000 cluster-announce-bus-port 17000 appendonly yes daemonize yes pidfile /var/run/redis-7000.pid logfile /var/log/redis/redis-7000.log dir /data/redis/7000 bind 0.0.0.0 protected-mode no

几个参数我解释一下为什么这么设:

  • cluster-node-timeout 15000:默认也是15000毫秒。这个值决定了节点多久没响应就被标记为疑似下线(PFAIL)。设太小,网络抖动就误判;设太大,故障切换慢。内网环境15000是稳妥值。
  • cluster-config-file:每个节点必须用不同的文件名,否则同一台机器上两个节点会互相覆盖集群状态。
  • cluster-announce-*:这三个是跨机搭建的命门。不写的话,节点会用自己探测到的地址去广播,多网卡或NAT环境下经常广播错。
  • appendonly yes:集群模式下建议开AOF,故障恢复时数据完整性更好。

3.2 启动顺序和目录准备

每台机器上先建好数据目录和日志目录:

mkdir -p /data/redis/7000 /data/redis/7001 mkdir -p /var/log/redis

然后按机器逐个启动,不要三台同时敲。我的习惯是先启三台机器上的主节点(7000),确认每个都起来了,再启从节点(7001):

redis-server /etc/redis/redis-7000.conf redis-server /etc/redis/redis-7001.conf

启动后先别急着create集群,用redis-cli -h 10.0.0.11 -p 7000 cluster nodes看一眼,应该每个节点只看到自己,状态是myself,master,cluster_state:fail。这是正常的,因为还没分配槽位。

3.3 用--cluster create组网的完整命令

六个节点都起来之后,在任意一台机器上执行:

redis-cli --cluster create \ 10.0.0.11:7000 10.0.0.12:7000 10.0.0.13:7000 \ 10.0.0.11:7001 10.0.0.12:7001 10.0.0.13:7001 \ --cluster-replicas 1

--cluster-replicas 1表示每个主节点配一个从节点。Redis会按你给的顺序,前三个作为主,后三个作为从,自动配对。执行后会提示你确认槽位分配方案,输入yes回车。

如果一切正常,你会看到:

>>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 ... >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join

卡点就在最后这一行。如果网络没问题,这行通常几秒内就会过去,然后显示All 16384 slots covered。如果超过30秒还卡着,基本可以确定是总线端口不通。

4. Waiting for the cluster to join的逐层排查链路

4.1 第一步:确认总线端口是否真的在监听

在每台机器上执行:

ss -tlnp | grep -E '7000|7001|17000|17001'

正常应该看到四个监听项。如果只看到7000和7001,没有17000和17001,说明cluster-enabled没生效,或者配置文件没加载对。检查redis-cli -p 7000 info cluster里的cluster_enabled:1。

4.2 第二步:从一台机器直连另一台的总线端口

这是最直接的验证方法。在10.0.0.11上执行:

telnet 10.0.0.12 17000

如果显示Connected,说明链路通;如果卡住或Connection refused,就是防火墙或安全组的问题。注意Connection refused和超时的区别:refused说明端口没监听或被本机拒绝,超时说明包被防火墙丢了。

4.3 第三步:用cluster meet手动验证gossip握手

如果telnet通了但集群还是建不起来,可以手动触发一次meet:

redis-cli -h 10.0.0.11 -p 7000 cluster meet 10.0.0.12 7000 redis-cli -h 10.0.0.11 -p 7000 cluster meet 10.0.0.12 17000

第一条是客户端端口meet,第二条是总线端口meet。正常情况下,meet之后几秒内cluster nodes就能看到对方。如果meet了但节点列表里出现的是handshake状态然后消失,说明总线端口握手失败。

4.4 第四步:检查cluster-announce-ip是否广播了错误地址

在节点上执行:

redis-cli -p 7000 cluster nodes

看每一行最后的IP:port。如果发现某个节点广播的是127.0.0.1或者一个不可达的内网IP,那就是cluster-announce-ip没配对。这种情况在多网卡机器上特别常见,节点默认选了第一块网卡的IP,但集群内其他机器走的是另一块网卡。

修复方法是停掉节点,在配置文件里明确写死cluster-announce-ip,删掉nodes-*.conf,重启后重新create集群。

4.5 第五步:清理残留状态重新来过

如果前面折腾过几轮,nodes.conf里可能残留了旧的集群拓扑,导致新集群建不起来。彻底清理的步骤:

# 停掉所有redis节点 redis-cli -p 7000 shutdown nosave redis-cli -p 7001 shutdown nosave # 删除集群状态文件和数据 rm -f /data/redis/7000/nodes-7000.conf rm -f /data/redis/7001/nodes-7001.conf rm -rf /data/redis/7000/appendonlydir rm -rf /data/redis/7001/appendonlydir # 重新启动 redis-server /etc/redis/redis-7000.conf redis-server /etc/redis/redis-7001.conf

清理完再执行--cluster create,成功率会高很多。我踩过的坑是:有一次反复create失败,最后发现是某台机器的nodes.conf里还记着上一次的cluster epoch,新节点join时epoch冲突,一直卡在握手阶段。

5. 集群建好之后的验证与日常运维要点

5.1 三组命令确认集群健康状态

集群create成功后,别急着写业务,先跑这三组验证:

# 1. 集群整体状态 redis-cli -h 10.0.0.11 -p 7000 cluster info # 关注 cluster_state:ok, cluster_slots_assigned:16384, cluster_known_nodes:6 # 2. 节点拓扑 redis-cli -h 10.0.0.11 -p 7000 cluster nodes # 确认三个master三个slave,slave的master-id对应正确 # 3. 槽位覆盖 redis-cli -h 10.0.0.11 -p 7000 cluster slots # 应该输出三组槽位区间,每组对应一个master及其slave

cluster_state:ok是最终目标。如果显示fail,看cluster_slots_assigned是不是16384,不是的话说明有槽位没分配完,通常是某个节点没join进来。

5.2 读写验证和重定向测试

用集群模式连接客户端:

redis-cli -c -h 10.0.0.11 -p 7000

-c参数开启重定向跟随。然后随便写几个key:

set user:1 "alice" set user:2 "bob" get user:1

如果key落在别的节点上,客户端会自动收到MOVED重定向并跳转,这是集群的正常行为。如果报CLUSTERDOWN,说明集群状态还是fail。

5.3 主从切换演练

生产环境必须验证故障切换。手动把一个master停掉:

redis-cli -h 10.0.0.12 -p 7000 shutdown nosave

然后在另一个节点上观察:

redis-cli -h 10.0.0.11 -p 7000 cluster nodes

大概15到30秒后(取决于cluster-node-timeout),对应的slave会变成master,cluster_state保持ok。这个演练能验证你的集群是真的高可用,而不是纸面上的。

5.4 扩容时槽位迁移的注意事项

集群跑起来之后要加节点,用--cluster add-node:

redis-cli --cluster add-node 10.0.0.14:7000 10.0.0.11:7000 redis-cli --cluster add-node 10.0.0.14:7001 10.0.0.11:7000 --cluster-slave

新节点加进来是空的,需要手动reshard槽位:

redis-cli --cluster reshard 10.0.0.11:7000

reshard过程中集群仍然可读写,但会有短暂的槽位迁移窗口。我建议在业务低峰期做,并且提前用--cluster-check确认集群健康。

注意:扩容时如果新节点的总线端口没放行,add-node会成功但节点状态一直是handshake,槽位迁移也会失败。所以每次加机器,第一件事永远是确认两个端口都通。

6. 几个我实际踩过的坑和对应解法

6.1 密码配置不一致导致的握手失败

如果集群设了requirepass,所有节点的密码必须完全一致,而且masterauth也要配。只配requirepass不配masterauth的话,主从复制会失败,slave永远同步不上。配置模板:

requirepass your_strong_password masterauth your_strong_password

用--cluster create时还要加-a参数:

redis-cli -a your_strong_password --cluster create ...

6.2 时间不同步引发的诡异故障

Redis Cluster用配置纪元(config epoch)来裁决冲突,如果机器之间时间差太大,gossip消息里的时间戳会乱,可能导致节点误判。我遇到过一台机器NTP没同步,时间慢了5分钟,结果集群频繁发生主从切换。解法很简单,所有节点开NTP同步:

timedatectl set-ntp true

6.3 云环境NAT导致的announce地址错误

云主机通常有内网IP和公网IP两张网卡。Redis默认探测到的是内网IP,但如果你从公网访问,节点广播内网IP就会导致客户端连不上。这时候必须显式配置:

cluster-announce-ip 公网IP cluster-announce-port 7000 cluster-announce-bus-port 17000

但要注意,集群内部通信最好还是走内网,公网只用于客户端接入。这种混合场景下,cluster-announce-ip设公网IP,节点间实际通信走内网,需要网络层做好路由。

6.4 nodes.conf损坏后的恢复

nodes.conf是Redis自动维护的,不要手动编辑。如果文件损坏导致节点起不来,最稳妥的做法是删掉它让Redis重新生成,但这样会丢失该节点的集群视图,需要重新join。恢复步骤:

# 停节点 redis-cli -p 7000 shutdown nosave # 备份并删除损坏文件 mv /data/redis/7000/nodes-7000.conf /data/redis/7000/nodes-7000.conf.bak # 重启 redis-server /etc/redis/redis-7000.conf # 从健康节点重新meet redis-cli -h 10.0.0.11 -p 7000 cluster meet 10.0.0.12 7000

如果这个节点是master且有槽位,重新join后需要手动把槽位迁回来,或者用--cluster fix自动修复。

6.5 关于cluster-node-timeout的调优经验

默认15000毫秒对大多数内网环境够用。但如果你的网络质量一般,或者机器负载很高,可以适当调大到30000。反过来,如果追求快速故障切换,可以调到5000,但代价是网络抖动时容易误判。我的经验值是:内网稳定环境10000,跨机房或云环境20000到30000。调这个参数要所有节点一致,否则gossip判断标准不统一。

7. 写在最后的一点个人体会

多机Redis集群搭建这件事,技术难度其实不高,难的是排查思路。Waiting for the cluster to join这个报错本身信息量很少,它不会告诉你到底是哪个端口不通、哪台机器有问题。我现在的习惯是:搭集群之前先把端口连通性用telnet或nc全部验证一遍,六台机器两两互测,确认无误再动手create。这一步花五分钟,能省掉后面一小时的瞎折腾。

另外,cluster-announce-ip这个配置项我建议在多机环境下永远显式写死,不要依赖自动探测。自动探测在单网卡环境下没问题,一旦机器有多张网卡或者走了NAT,就是各种玄学问题的源头。把不确定的因素提前确定下来,集群搭建的成功率会高很多。

最后,集群建好只是开始,日常的监控、扩容、故障演练才是真正考验运维能力的地方。建议至少每季度做一次主从切换演练,确保真出问题的时候,集群能按预期自动恢复。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询