Redis集群三主三从一键部署与故障转移排错
简介:一份面向 CentOS 7.x 环境搭建 Redis 集群的自动化部署脚本包,服务于需要快速验证集群方案的运维与后端开发者,把主从复制、16384 个槽位分配、故障转移等集群要点固化为可一键执行的流程,免去逐台手工编译与配置的重复劳动。压缩包共 5 个文件、约 1.86MB,以 3 个 sh 脚本为主体,分别承担主流程调度、Redis 安装配置与集群卸载清理,另含 redis-5.0.0.tar.gz 源码包和一份 txt 使用说明,覆盖从解压编译到集群成型的完整环节,已有 777 人学习。按说明修改 IP 与端口变量后依次执行,即可完成依赖安装、编译、节点配置与集群创建,卸载脚本便于回收实例与残留配置;脚本中的变量定义、条件判断与循环结构,也可作为编写同类自动化部署脚本的参考。对初次接触集群部署的读者,还可借此理解节点角色划分与槽位分配在真实环境中的含义,并沿着脚本顺序梳理常见故障的排查方向。
1. 手工搭三主三从熬到凌晨:这份 Redis 集群一键部署脚本省掉的四件事
三台 CentOS 机器、六个 Redis 实例,我第一次手工搭集群从下午两点折腾到凌晨:配置文件手改六遍,meet 完节点卡在 handshake,最后发现是 17000 端口没放行。后来拿到这份 redis 集群一键自动部署脚本.rar,真正省事的不是redis-cli --cluster create那一行,而是它前面那堆重复劳动——批量生成 conf、按顺序拉起实例、把客户端端口和集群总线端口一起放通、创建完再跑一遍 check 把状态确认下来。
这套东西是 shell 写的,贴着 CentOS 的 systemctl、firewalld、sysctl 走,没有额外运行时依赖,几百行读一遍就能按自己的机器改。它适合三类人:手上只有两三台测试机、需要反复重建集群做课程设计或压测的;线上出过槽位故障、想在预发环境原样复现的;以及刚接触 Redis Cluster、想先跑通再回头补原理的。下面从机制讲到执行链路,再讲翻车点。
2. 槽位、总线与选举:一键脚本必须替你做对的四个集群机制
脚本只做「生成配置、拉进程、建集群」三件事,但每一件背后都压着 Redis Cluster 的运行时规则。不理解这四条,脚本跑挂了连日志该往哪看都不知道。反过来,理解了这四条,脚本里那些看起来多余的 sleep、循环和判断,你就知道它在防什么。
2.1 16384 个槽位怎么分,脚本为什么默认给三主
Redis Cluster 把整个键空间切成 16384 个槽,key 落到哪个槽由CRC16(key) mod 16384决定,槽再归某个主节点管。客户端拿到 MOVED 重定向后自己缓存槽位表,所以集群的核心不是「数据分片存储」这件事本身,而是「槽位归属必须全集群一致」。任何一个槽没有主人,集群在cluster-require-full-coverage yes(默认值)下会直接拒绝所有写请求。
一键脚本之所以默认三主三从,不是审美选择,是硬约束:--cluster-replicas 1要求主节点数至少 3,因为故障转移要靠主节点多数派投票,两个主节点挂一个就凑不出多数。三主是最小可用拓扑,脚本里如果写成「节点数 / 2」来算主节点数,节点少于 6 个时就会算出 2 主,create 阶段直接报*** ERROR: Invalid configuration for cluster creation。
槽位分配默认是均分再取余:16384 / 3 = 5461、5461、5462,第三个主节点多拿 2 个槽。这个数字在很多巡检脚本里被写死成5461做断言,一旦集群做过 reshard 就会误报。我的做法是只看cluster info里的cluster_slots_assigned:16384,不校验单节点槽数。
另外要记住集群模式只支持 db 0,配置里写databases 16不会报错,但SELECT命令会被拒绝返回ERR SELECT is not allowed in cluster mode。涉及多 key 的操作必须落在同一个槽,用 hash tag 把 key 的一部分用{}包起来,比如user:{1001}:name和user:{1001}:age会被强制分到同一槽,这是设计键名时就要定下来的事,不是部署阶段能补救的。
2.2 cluster bus 与握手:meet 之后到底发生了什么
每个集群节点会额外监听一个「客户端端口 + 10000」的总线端口,7000 对应 17000。节点之间交换 gossip 消息、投票、传播槽位表都走这个端口,用的是二进制协议,和客户端协议不是一回事。手工搭集群最常见的卡点就在这里:6379 能 telnet 通,17000 不通,cluster nodes里对方节点永远是handshake状态,日志里反复刷Unable to connect to MASTER。
握手的完整链路是:脚本对 A 节点执行CLUSTER MEET B_IP B_PORT,A 通过总线端口连 B,交换节点信息,B 再把 A 的信息 gossip 给已知节点。所以只要有一个节点的总线端口不通,整个集群就可能分裂成两个互不可见的孤岛,而cluster_state在每个孤岛内部都显示 ok。这就是为什么脚本里的防火墙规则必须成对出现:客户端端口段和总线端口段一起放,少写一行就是几小时的排查。
还有个容易被忽略的点:节点的 node id 存在cluster-config-file指定的文件里,默认叫nodes.conf。这个文件是 Redis 自己维护的,脚本绝对不能删、不能提前创建、不能手改。删掉它的后果是节点重启后生成新 id,其他节点还拿着旧 id 做 gossip,集群视图彻底错乱。我在脚本里见过用rm -rf $DATA_DIR/*做「清理重来」的写法,看着干净,实际上把 nodes.conf 一起干掉了,第二次执行必翻车。
2.3 主从复制与自动故障转移的判定链路
从节点默认每秒向主节点发REPLCONF ACK,主节点把复制偏移量记在内存里。某个主节点失联后,其余主节点在cluster-node-timeout(默认 15000 毫秒)之后把它标记为fail,然后由持有该主节点槽位的从节点发起选举,拿到多数主节点投票后提升为主,接管槽位。整个过程脚本管不了,但脚本里的两个参数直接决定它能不能成。
一个参数是cluster-node-timeout。设太小,网络抖一下就触发主从切换,切完数据还没同步完又切回来;设太大,真挂了要等半分钟以上才恢复。生产上给 15000 到 30000 之间比较稳,测试机可以给 5000 加快演练节奏。另一个是cluster-replica-validity-factor(默认 10),从节点断线时间超过node-timeout × factor就失去竞选资格,这是防止用一份过期数据顶上去的保护。把它设成 0 表示不管断多久都能选,只在「宁可丢数据也要保可用」的场景下才这么干。
从节点能不能被选中,还要看它的复制偏移量和主节点差多少。如果脚本为了让启动快,把repl-diskless-sync之类的东西乱调,或者从节点刚起来还没完成全量同步就被拉进集群,选举时它的 offset 落后太多,同样会被其他主节点拒绝。所以脚本里「拉起实例」和「cluster create」之间应该留几秒让复制链路建立,这个 sleep 不是玄学,是在等master_link_status:up。
2.4 节点顺序与副本错位:脚本里最容易写错的一段逻辑
redis-cli --cluster create的节点列表顺序是有语义的:前 N 个被当作主节点,后面按顺序分配给它们做从节点。如果参数写成--cluster-replicas 1,列表是A B C D E F,那 A B C 是主,D E F 依次是 A B C 的从。但这个「依次」是按主机和端口的排列算的,脚本如果用两层 for 循环先生成所有 7000 端口再生成所有 7001 端口,最终可能出现三个主节点全在同一台物理机上,另一台机器上的三个从节点全在等它挂——机器一断电,集群直接没了一半。
正确的做法是按机器交错排列:每台机器出两个实例,列表写成A:7000 A:7001 B:7000 B:7001 C:7000 C:7001,这样主节点天然分散在三台机器上,从节点也落在不同的物理机。脚本里如果做了「按 IP 分组再拼接」的处理,读的时候要确认它是交错而不是分段。这一点在验收时可以用redis-cli --cluster check输出的主从对应关系直接看出来,从节点和它的主节点在同一台机器上就是错位了。
还有一种错位是端口和目录没对上。同一个 IP 上跑 7000 和 7001 两个实例,配置里的dir必须是两个不同目录,logfile、pidfile、dbfilename也都得带端口后缀。脚本如果只替换了port而没替换dir,两个实例会往同一个 dump 文件里写,AOF 也会互相覆盖,症状是集群能建起来但一重启数据就乱。
3. 解包看结构:CentOS 上的目录、系统参数与端口三件套
拿到 rar 之后第一件事不是急着bash deploy.sh,而是先看清包里有什么、它假设你的机器处于什么状态。这类脚本的失败,八成不是脚本逻辑错,而是它默认的前置条件在你的机器上不成立。
3.1 压缩包里的文件各管什么
rar 在 CentOS 上默认解不开,需要先装unrar,很多最小化安装的镜像里没有这个包,得先配好软件源再装。解出来的目录结构各家写法不同,但职责基本是固定的几类,读之前先按下面这张表对一遍,缺哪个文件就知道脚本会缺哪一步:
| 文件/目录 | 典型职责 | 缺了会怎样 |
|---|---|---|
deploy.sh | 主入口,串起检查、生成、启动、建集群 | 无法一键执行 |
redis.conf.tpl | 配置模板,用变量占位端口、目录、密码 | 生成的配置全是默认值 |
nodes.list/hosts.conf | 节点 IP 与端口清单 | 脚本不知道往哪几台机器铺 |
start_all.sh/stop_all.sh | 批量拉起与停止实例 | 只能手工 ps 后 kill |
check_cluster.sh | 建完集群后的状态校验 | 出问题没有统一出口 |
redis-*.tar.gz | 源码包或二进制包 | 需要自己准备同版本安装包 |
README | 端口、密码、目录约定 | 参数只能靠读脚本猜 |
读脚本时优先确认三件事:REDIS_HOME装在哪、DATA_DIR数据落在哪、密码是写死在脚本里还是从外部传入。写死密码的脚本在多人共用的测试环境里很常见,改的时候记得同步改requirepass和masterauth两处,只改一处集群建不起来。
3.2 CentOS 7/8 必须先改的四个内核与资源项
Redis 在集群模式下对系统资源更敏感,因为多了总线连接和节点间心跳。以下四项如果脚本里没做,建议在执行前手工补上,用sysctl落成配置文件比sysctl -w临时改可靠,重启不丢:
# /etc/sysctl.d/99-redis-cluster.conf # 允许内存过量提交,否则 fork 做 RDB 落盘时可能直接失败 vm.overcommit_memory = 1 # 监听队列长度,低于 511 时高并发下会出现连接被丢弃 net.core.somaxconn = 1024 # 尽量不使用 swap,避免节点间心跳因换页抖动而超时 vm.swappiness = 1 # 关闭透明大页,THP 会造成 Redis 的 fork 延迟 spikes echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag sysctl --systemvm.overcommit_memory这一项是最容易被忽略的,日志里表现为Background save is starting之后跟一条fork: Cannot allocate memory,进程直接没了,而这台机器内存看起来还很空闲。原因不是内存不够,是内核在 fork 时按 overcommit 策略拒绝了地址空间申请。somaxconn则对应TCP: request_sock_TCP: Possible SYN flooding这类告警,连接数一上来就丢包。
文件描述符也得提。Redis 默认建议ulimit -n不低于 10032,systemd 拉起的服务要在 unit 文件里写LimitNOFILE=10032,光改/etc/security/limits.conf对 systemd 服务不生效,这是 CentOS 7 之后非常常见的坑。脚本如果用redis-server xxx.conf &的方式启动,继承的是当前 shell 的限制,换个终端执行结果就不一样,我一般让脚本显式ulimit -n 10032再拉进程。
3.3 端口放行:7000-7005 只是其中一半
六个实例用 7000 到 7005,客户端连这些端口没问题,但节点之间要走 17000 到 17005,只放前半段的表现是:cluster nodes里其他节点一直是handshake,或者已经connected但cluster_state:fail,日志刷Cluster bus: connection refused。firewalld 的写法是一次把两段都加上:
# 客户端端口段 firewall-cmd --permanent --add-port=7000-7005/tcp # 集群总线端口段 = 客户端端口 + 10000 firewall-cmd --permanent --add-port=17000-17005/tcp firewall-cmd --reload firewall-cmd --list-ports如果机器在云上,安全组是另一道门,firewall-cmd --list-ports有输出不代表外部能进来。验证方式是换一台机器用telnet 目标IP 17000或者nc -zv 目标IP 17000试,本地ss -lntp | grep 1700看到监听只说明本机进程起来了,跟网络可达是两件事。
还有个细节:如果同一台机器上跑多个实例,bind建议写内网 IP 而不是0.0.0.0,同时把protected-mode关掉(集群模式和多节点访问下通常要关),并且显式设置cluster-announce-ip为内网地址。多网卡机器上不设这一项,节点会把自己广播成 127.0.0.1 或者某块业务网卡的地址,其他节点按这个地址连总线端口,永远连不上。
4. 从零跑一遍:配置生成、实例拉起、cluster create 的完整链路
这一章按执行顺序走一遍,每一步给出关键命令和需要核对的输出。脚本再自动,这四步的中间产物你都应该知道长什么样,否则出问题只能干等。
4.1 生成六份 redis.conf:用循环替掉手改
配置模板的核心是集群相关的那几行,端口、目录、密码用变量替换。手写六份的失败率远高于想象,一份漏了cluster-enabled yes,create 阶段就报Node xxx is not configured as a cluster node。用循环生成,模板只维护一份:
#!/usr/bin/env bash set -euo pipefail BASE_PORT=7000 DATA_ROOT=/data/redis-cluster NODES=("10.0.0.11" "10.0.0.12" "10.0.0.13") PASSWORD='ChangeMe_2024' for ip in "${NODES[@]}"; do for idx in 0 1; do port=$((BASE_PORT + idx)) dir="${DATA_ROOT}/${port}" mkdir -p "${dir}" cat > "${dir}/redis.conf" <<EOF port ${port} bind ${ip} protected-mode no daemonize yes pidfile ${dir}/redis.pid logfile ${dir}/redis.log dir ${dir} dbfilename dump-${port}.rdb appendonly yes appendfilename "appendonly-${port}.aof" # 集群三件套:开关、节点状态文件、失联判定毫秒数 cluster-enabled yes cluster-config-file nodes-${port}.conf cluster-node-timeout 15000 cluster-announce-ip ${ip} cluster-announce-port ${port} cluster-announce-bus-port $((port + 10000)) # 密码必须两处一致,否则从节点同步和集群创建都会失败 requirepass ${PASSWORD} masterauth ${PASSWORD} EOF done done几个参数值得单独说。cluster-config-file带上端口后缀,避免同机多实例互相覆盖,这个文件由 Redis 维护,脚本只负责给路径。cluster-announce-ip显式写成当前机器的内网 IP,多网卡环境下这是唯一可靠的广播地址来源。appendonly yes在集群里建议打开,从节点提升为主后如果只有 RDB,最后一个 RDB 之后的数据就丢了。daemonize yes让实例后台跑,脚本里要跟一个循环等待端口就绪,不能生成完就立刻建集群。
路径权限也要顺手检查:${dir}必须对运行 Redis 的用户可写,用 root 生成目录、用 redis 用户起进程是常见组合,权限没给对的表现是启动即退出,日志里一行Can't open the append-only file: Permission denied。
4.2 拉起实例并确认端口真的在监听
启动之后不要相信daemonize的返回值,它只表示 fork 成功,不表示配置正确。判断标准是端口在监听且PING有回应,这一步放在脚本里就是一个带超时的轮询:
#!/usr/bin/env bash # 逐台启动并按端口探活,超过 30 秒未就绪就打印日志尾巴退出 for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do for port in 7000 7001; do ssh "${ip}" "redis-server ${DATA_ROOT}/${port}/redis.conf" for i in $(seq 1 30); do if redis-cli -h "${ip}" -p "${port}" -a 'ChangeMe_2024' --no-auth-warning ping 2>/dev/null | grep -q PONG; then echo "OK ${ip}:${port}" break fi sleep 1 done redis-cli -h "${ip}" -p "${port}" -a 'ChangeMe_2024' --no-auth-warning ping \ | grep -q PONG || { tail -n 30 "${DATA_ROOT}/${port}/redis.log"; exit 1; } done done这里用--no-auth-warning只是避免密码提示刷屏,脚本里不要把密码明文写进 history,正式环境建议用REDISCLI_AUTH环境变量传。探活只看PING还不够,集群模式下要再确认cluster-enabled:yes和cluster_state的初始值,刚启动的节点应该是cluster_state:fail、cluster_known_nodes:1,这属于正常状态,说明它以单节点身份起来了,等着被 meet。
如果某个端口一直起不来,先看日志最后 30 行,再看ss -lntp | grep 端口有没有别的进程占着。测试机上经常出现上一次的实例没杀干净,pidfile 残留导致新进程以为自己在跑,daemonize yes下这种问题特别隐蔽。
4.3 cluster create 的节点顺序与副本分配
六个节点都活了之后才能建集群,节点列表按「主机交错、主节点在前」的顺序排。Redis 5 之后--cluster子命令是内建的,不需要额外装 ruby 的 redis-trib:
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 \ -a 'ChangeMe_2024' --cluster-yes前三个是主节点,后三个按顺序做它们的从节点,所以 7001 的三个实例分别跟着三个 7000 主节点,且从节点都不在同一个 IP 上——这就是 2.4 里说的交错排列。--cluster-yes跳过多余的交互确认,脚本里必须加,否则会卡在Can I set the above configuration? (type 'yes' to accept)等人输入。-a在带密码集群里要给,且必须和配置里的requirepass一致。
执行过程中会看到每个主节点分到5461/5461/5462个槽,接着是Waiting for the cluster to join,这一步耗时取决于总线端口是否通。如果卡在这里超过一分钟,别等,直接去另一台机器上看telnet 目标IP 17000是否可达,九成是端口或cluster-announce-ip的问题。
4.4 用 cluster check 把状态钉死
创建命令返回成功不代表集群健康,必须再跑一遍 check,它会逐个节点连过去核对槽位覆盖和主从关系:
redis-cli --cluster check 10.0.0.11:7000 -a 'ChangeMe_2024' # 关键看三行: # [OK] All 16384 slots covered. # 每个 master 下列出的 slots 区间与 replica 数量 # [OK] All nodes agree about slots configuration.All nodes agree about slots configuration这句是核心,它表示各节点的槽位视图已经收敛一致。如果这里报Different slots configuration,说明有节点的信息还没 gossip 到,等几秒重跑;如果一直不一致,检查是不是有节点被 meet 过两次、在集群里留了重复条目。验收时我会把 check 的输出存一份到日志目录,后面扩容、迁移、重启都可以拿来对比。
5. 避坑与排查:cluster_state:ok 之后仍然会翻车的五个场景
集群建起来、状态 ok,只说明拓扑成立,不代表业务能用。下面五个是我和同事在不同机器上真实踩过的,症状看起来都不像集群问题,根因全在配置和端口上。
5.1 客户端一直报 MOVED,状态却是 ok
现象:应用连上 7000 写一个 key,报MOVED 12345 10.0.0.12:7000,程序里没做重定向处理,直接抛异常。集群侧CLUSTER INFO显示一切正常。
原因:客户端没开集群模式,把 Redis 当成单机在用。MOVED 不是错误,是告诉客户端这个槽在别的节点,集群感知客户端会自动跟随并缓存槽位表,单机客户端则把重定向当异常抛出来。另一种变体是连的是代理或 VIP,请求被固定转发到某一台,槽位表永远变不过来。
解决:换成客户端库的集群模式初始化方式,连接时给出全部种子节点而不是一个 VIP。如果确实只能给单地址,必须在中间层做槽位感知转发,不能做四层负载均衡——四层会把同一个连接的所有请求固定到同一节点,跨槽访问必然失败。
5.2 节点卡在 handshake 半天不动
现象:cluster nodes里某个节点状态是handshake,cluster info的cluster_known_nodes比实际节点数少,日志反复刷Unable to connect to node。
原因:总线端口不通,或者节点广播的地址是 127.0.0.1、某块业务网卡地址。前者是防火墙只放了 7000 段,后者是多网卡机器没设cluster-announce-ip,节点按默认网卡地址互相通告,对方连过去自然不通。
解决:把总线端口段补进防火墙和安全组,cluster-announce-ip显式写内网 IP 后重启实例。已经进过集群的错误节点不能直接改配置重启,要先CLUSTER RESET HARD清掉它的节点状态,再重新CLUSTER MEET,否则旧地址会一直留在其他节点的视图里。
5.3 重启之后集群整体 fail
现象:三台机器重启,实例都起来了,cluster_state:fail,cluster_slots_assigned远小于 16384,部分主节点显示fail?。
原因:脚本的清理逻辑把nodes-*.conf删了,节点重新生成 id,全集群不认它;或者数据目录权限变了,节点读不到原状态文件只能当新节点启动;还有一种是从节点比主节点先起来且主节点长时间未就绪,从节点把自己降级成孤立状态。
解决:nodes.conf绝对不删。重启顺序先主后从,脚本里加等待逻辑,确认所有主节点cluster_state恢复到 ok 再拉起从节点。真出现了 id 变更,只能在剩下节点里用CLUSTER FORGET把旧条目录掉,再把故障节点以干净状态重新加回并迁移槽位,这个操作要停机窗口。
5.4 带密码的集群创建直接失败
现象:redis-cli --cluster create输出Node xxx replied with error: NOAUTH Authentication required或者从节点一直master_link_status:down。
原因:requirepass配了,masterauth没配或者值不一样。requirepass管客户端连它,masterauth管它连主节点时用什么密码,集群里两个方向都要通,只配一处就是半通状态。
解决:两处写成同一个值,redis-cli --cluster相关命令带-a或用REDISCLI_AUTH。改配置要六个节点全改一遍再重启,改完先用redis-cli -a 密码 info replication确认从节点master_link_status:up,再跑--cluster check。
5.5 扩容加节点后槽位覆盖不全
现象:新加两个节点、迁移了部分槽过去,--cluster check报Not all 16384 slots are covered,但每个节点单独看都正常。
原因:迁移过程中断,或者用了手工CLUSTER SETSLOT但漏了IMPORTING/MIGRATING两步的收尾,源节点把槽标成迁移中,目标节点没接管,这个槽就悬空了。cluster-require-full-coverage yes下整个集群拒写。
解决:用redis-cli --cluster reshard走完整流程,不要手工拼SETSLOT。已经悬空的槽用CLUSTER SETSLOT <slot> NODE <node-id>在相关节点上逐一修正,修完跑 check 确认All 16384 slots covered。
注意:以上所有涉及
CLUSTER RESET、FORGET、SETSLOT的操作都有数据丢失风险,先在测试环境完整走一遍再上预发。
6. 把一键脚本用成可维护工具:故障演练与巡检的固定动作
脚本能建集群只是起点。真正决定这套东西能不能长期用的,是你能不能在一次执行之后回答两个问题:故障切换要多久、下次重建要改哪几行。我给自己的固定动作是每次部署完做一次小型故障演练,把切换时间记进部署记录,后面遇到「集群是不是变慢了」这类问题时至少有基线可比。
演练很简单:找到某个主节点,kill -9掉它的进程,然后每两秒查一次cluster info里的cluster_state和cluster_slots_assigned,同时用redis-cli -c往这个主节点负责的槽写 key,看多久恢复写入。默认cluster-node-timeout 15000下,30 秒内完成切换属于正常,超过 45 秒就要检查是不是只有两个主节点、投票凑不齐多数。演练完把杀掉的实例重新拉起,它会以从节点身份回归,用redis-cli --cluster check确认主从关系复原。
巡检我习惯写成一个小循环,把每个节点的关键指标拉成一行,肉眼扫一遍就知道有没有异常:
#!/usr/bin/env bash # 逐节点采集关键状态,输出一行一个实例,便于 diff 和归档 PASS='ChangeMe_2024' for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do for port in 7000 7001; do info=$(redis-cli -h "${ip}" -p "${port}" -a "${PASS}" --no-auth-warning cluster info 2>/dev/null) role=$(redis-cli -h "${ip}" -p "${port}" -a "${PASS}" --no-auth-warning info replication 2>/dev/null | awk -F: '/^role/{print $2}' | tr -d '\r') state=$(echo "${info}" | awk -F: '/^cluster_state/{print $2}' | tr -d '\r') assigned=$(echo "${info}" | awk -F: '/^cluster_slots_assigned/{print $2}' | tr -d '\r') printf '%-12s %-5s role=%-6s state=%-5s slots=%s\n' "${ip}" "${port}" "${role}" "${state}" "${assigned}" done done这个脚本的价值在于输出稳定、可以直接 diff:部署前存一份、演练后存一份,cluster_slots_assigned从 16384 掉了就说明有槽悬空,role变了就说明发生过切换。比翻日志快得多,也不用记复杂命令。
最后是一个参数化的习惯。拿到这类脚本我第一件事是把BASE_PORT、NODES、PASSWORD、DATA_ROOT四个变量抽到单独的cluster.env里,脚本开头source进来;第二件事是把生成的配置、check 输出、演练记录按时间戳落到logs/,不做覆盖。那次重启后集群 fail 的经历之后,我每次改脚本都会先在一台干净的虚拟机上从零跑两遍,确认第二遍执行不会因为残留目录报错,再看一遍nodes.conf有没有被动过——集群这东西,一次省事的rm -rf能换来一整晚的恢复工作,后悔药是没有的。希望帮到你。
本文还有配套的精品资源,点击获取