简介:针对 CentOS 7.x 环境下希望快速搭建容器化 Redis 集群的 Linux 运维人员与后端开发者,提供一套可传递参数后自动执行的 shell 部署方案,用于减少逐节点配置、容器编排和基础环境准备中的重复操作,适合实验环境验证及中小项目初期部署参考。压缩包共 6 个文件,由 3 个 shell 脚本、1 个镜像归档、1 份配置文件与 1 份说明文档组成,整体约 22.46MB;其中脚本分别承担自动化部署和卸载入口,配置与文档用于调整参数、核对执行条件,镜像归档可辅助准备离线运行环境。已有 1055 人学习,表明该方案在 Docker 与 Redis 集群入门实践中具备一定参考价值。读者可获取集群部署脚本、卸载脚本、配置模板、参数传递示例与使用说明,进而理解容器化 Redis 集群的目录组织、部署流程和常见排错思路,并能结合主机实际情况修改后复用。
1. 一份把 Redis 集群装进 CentOS 7 的 shell 脚本:它替你省掉了哪些重复劳动
拿到一台干净的 CentOS 7 机器,从安装 docker 到跑出cluster_state:ok,很多人要来回折腾一两个小时:改 redis.conf、放防火墙、起六个容器、拼--cluster create后面那一长串 IP 和端口。用来一键部署 docker redis 集群的这份 linux shell 脚本,核心就是把这段重复劳动压缩成几条命令,跑完后三主三从的槽位会自动分好。它不做集群监控,不带代理层,也不解决跨机房调度,定位很明确:给开发、测试、小规模预发环境一个能反复重建的 Redis Cluster。适合手上只有一两台 CentOS 7、想验证分片和故障转移逻辑、又不想自己写一套运维平台的人;如果你要的是生产级多活,这套脚本只能当起点。
2. 先看清骨架:16384 个槽位、6 个端口、6 份目录怎么落
脚本做的事并不神秘,无非是把「写配置、起容器、建集群」三步固化成顺序执行的命令。但在开跑之前,得先知道它往机器上放了多少东西、这些东西之间靠什么通信;否则后面一旦崩掉,就只能对着六个看不出差别的容器干瞪眼。这一章把集群的骨架拆开看,再给出跑脚本前必须核对的环境基线。
2.1 三主三从不是随便排的:槽位、gossip 与主从配对顺序
Redis Cluster 没有中心节点,16384 个 hash slot 平摊到三个主节点上,每个主节点大致承担 5461 个槽位;key 先做 CRC16 再对 16384 取模,落到哪个槽位就去哪个节点取。节点之间用 gossip 协议互相通信,每个节点都要和其余节点保持长连接,这意味着任意两个节点之间都必须能双向访问业务端口和总线端口,缺一个都建不起来。这条约束直接决定了后面网络方案怎么选。
从节点不参与槽位分配,只做异步复制,主节点挂掉后由集群内部投票提升。脚本里那句--cluster-replicas 1说的就是「每个主节点配一个从节点」。这里有个容易忽略的细节:redis-cli --cluster create是按传入地址的顺序分配角色的,前三个是主,后三个依次跟前面的主配对。所以如果 6 个地址全写同一个 IP,只是端口不同,那么主从会全部落在同一台物理机上——能跑,但失去了高可用的意义。脚本通常会在两个 IP 之间交替排列,形如 A:7000 B:7000 A:7001 B:7001……这种排法不是玄学,是为了让每一对主从落在不同机器上。单机部署时这条无所谓,多机时值得在脚本里多写一行判断。
还有一个常被误会的点:集群的「主从」和我们平时说的主从复制不完全是一回事。集群模式下从节点默认只读,客户端连到从节点读数据需要先发READONLY,否则会收到MOVED重定向。做压测或者读写分离之前,得先确认客户端驱动支不支持这个语义,别上来就以为是脚本装坏了。
2.2 端口与目录分配表:从 7000 到 17005 的完整对照
每个集群节点实际占用两个端口:业务端口和总线端口,后者固定是前者 + 10000。6 个节点就是 12 个端口,一个都不能少。目录结构同理,每个节点一份配置、一份数据,互相不共享——尤其是nodes.conf,共享了就是灾难,后面避坑章节会专门讲。
| 节点名 | 角色 | 业务端口 | 总线端口 | 配置目录 | 数据目录 |
|---|---|---|---|---|---|
| redis-7000 | master | 7000 | 17000 | /data/redis-cluster/7000/ | /data/redis-cluster/7000/data |
| redis-7001 | master | 7001 | 17001 | /data/redis-cluster/7001/ | /data/redis-cluster/7001/data |
| redis-7002 | master | 7002 | 17002 | /data/redis-cluster/7002/ | /data/redis-cluster/7002/data |
| redis-7003 | slave | 7003 | 17003 | /data/redis-cluster/7003/ | /data/redis-cluster/7003/data |
| redis-7004 | slave | 7004 | 17004 | /data/redis-cluster/7004/ | /data/redis-cluster/7004/data |
| redis-7005 | slave | 7005 | 17005 | /data/redis-cluster/7005/ | /data/redis-cluster/7005/data |
表里的角色是建集群之后的结果,起容器阶段六个节点地位平等,都只是「开了集群模式、还没加入任何集群」的独立实例。这个顺序很重要:先让六个实例各自健康起来,再统一建集群,中间任何一步失败都不要急着往下走。我见过有人容器还没全起来就执行 create,结果只成功了一半,后面清理起来比重装还费劲。
容器名带上端口号是个小习惯,好处是docker logs redis-7003这种命令敲起来不用查。数据目录和配置目录分开也是刻意的:重置集群时只删data目录里的nodes.conf和持久化文件,配置文件保留,省得重新生成。
2.3 跑脚本前的环境基线:CentOS 7 上必须确认的四件事
CentOS 7 的生命周期摆在那儿,默认内核 3.10,默认 yum 源里的 docker 版本偏老,这两点会让脚本的行为和新版发行版不太一样。下面这段检查建议在跑脚本之前原样执行一遍,四项全过再往下走。
# 1. docker 是否可用,版本是否够(1.13 是 CentOS 7 extras 源的默认版本,够用) docker version --format '{{.Server.Version}}' systemctl is-enabled docker # 2. 内核与透明大页,THP 开着会让 fork 时的 RDB 保存变慢 uname -r cat /sys/kernel/mm/transparent_hugepage/enabled # 3. 端口占用检查,12 个端口里有任何一个被占,脚本起容器时会直接失败 ss -lntp | grep -E ':(700[0-5]|1700[0-5])\b' # 4. 内存过载策略,没开的话后台保存可能被 OOM 杀掉 sysctl vm.overcommit_memory第一项里systemctl is-enabled docker返回enabled才说明开机自启配好了,不然机器一重启集群就没了,这种翻车在现场环境特别常见。第二项如果要关闭 THP,临时生效用echo never > /sys/kernel/mm/transparent_hugepage/enabled,永久生效得写进 rc.local 或者 systemd 单元,CentOS 7 上 rc.local 默认没有执行权限,记得chmod +x /etc/rc.d/rc.local。
第三项是最容易被跳过、又最致命的。7000 这类端口很多人的机器上已经被别的服务占着,脚本docker run的时候报bind: address already in use,但因为是循环执行,前面的节点已经起来了,现场就成了半死不活的状态。提前用ss扫一遍,比事后docker rm -f六个容器省事得多。
第四项,vm.overcommit_memory的值应该是 1。如果是 0,Redis 在内存紧张时执行后台保存,fork 出来的子进程可能被内核直接干掉,日志里只留一句Background saving terminated by signal,看起来像是玄学崩溃。改法是sysctl -w vm.overcommit_memory=1再写进/etc/sysctl.conf。
另外还有两件事不属于「检查」而属于「配置」:firewalld 要放行 7000-7005 和 17000-17005 两段 TCP;SELinux 如果是 enforcing,挂载进容器的配置文件可能被拒绝读取,脚本一般会在 README 里让你先setenforce 0或者给目录打上container_file_t标签。CentOS 7 上这两项是绕不开的,先处理掉再谈其他。
3. 从零执行:四条命令把六节点集群拉起来
环境基线过了,接下来就是真正动手。这一章按照「解压 → 改变量 → 执行 → 验收」的顺序走一遍,每条命令都说明它在干什么、失败时先看哪里。脚本本身不复杂,复杂的是失败时的判断路径,所以验收这一步我写得细一些。
3.1 落位与解压:目录结构决定后面好不好排错
先把资源包放到一个固定位置。我一般用/opt/redis-cluster,好处是路径短、权限清晰,出问题时tree一眼能看全,不会被一堆相对路径绕晕。
# 建目录并进入 mkdir -p /opt/redis-cluster cd /opt/redis-cluster # 解压资源包(按实际文件名替换) tar -zxvf redis-cluster-docker.tar.gz -C /opt/redis-cluster --strip-components=1 # 给脚本执行权限,并看一眼目录结构 chmod +x *.sh find . -maxdepth 2 -type f | sort--strip-components=1是防止压缩包里自带一层顶层目录,解开之后变成/opt/redis-cluster/xxx/redis-cluster.sh,后面所有相对路径全要跟着改。如果你不确定包内结构,先tar -tzf列一下再决定要不要加这个参数。chmod +x只对.sh生效,模板文件不需要执行权限。
执行完find之后,正常应该看到三样东西:入口脚本(一般叫redis-cluster.sh或install.sh)、配置模板(redis.conf.tpl之类)、以及可能的卸载脚本。如果没有模板文件,说明配置是脚本里用cat <<EOF直接生成的,那就要去脚本里找那段 heredoc,改参数的位置不一样。这一步多花三十秒确认,比后面改错地方强。
3.2 改三个变量再执行:IP、起始端口与数据目录
脚本顶部通常会集中放几个变量,需要按机器实际情况改。最少要确认三个:宿主机 IP、起始端口、数据目录。IP 这一项在多机部署时尤其关键,写错了集群会通告一个别人连不上的地址,表现为「创建命令返回成功但集群不可用」。
#!/usr/bin/env bash # 脚本顶部可改变量,按实际情况修改 HOST_IP="192.168.10.11" # 宿主机内网 IP,必须是其他节点能访问到的地址 BASE_PORT=7000 # 起始端口,6 个节点依次递增 NODE_NUM=6 # 节点总数,3 主 3 从 DATA_DIR="/data/redis-cluster" # 数据与配置的根目录,确保所在分区有空间 PASSWORD="" # 留空表示不设密码;设置后需成对配置 masterauth REDIS_IMAGE="redis:6.2" # 镜像标签,集群命令需要 Redis 5.0 以上HOST_IP千万别写127.0.0.1。集群建好之后,节点之间靠这个地址互相通信,客户端也靠它做重定向,写回环地址的后果是cluster nodes里全是 127.0.0.1,从别的机器上连过来必然失败。DATA_DIR要确认分区余量,AOF 开着的话写入量大的场景增长很快。
PASSWORD留空意味着集群裸奔,内网测试环境可以接受,但只要机器有公网入口就必须设。设密码的时候注意requirepass和masterauth必须同时配,只配前者的话主从复制会因为认证失败而同步不了,cluster nodes里从节点的link state会一直是connect。这个坑后面还会提。
改完保存,执行:
# 前台执行,方便看每一步的输出 bash ./redis-cluster.sh # 如果脚本支持后台模式,也可以用 nohup 挂着 # nohup bash ./redis-cluster.sh > cluster-install.log 2>&1 &第一次跑建议前台执行。脚本在起容器、等端口就绪、执行 create 这几个阶段都会有输出,前台能直接看到卡在哪一步。后台跑虽然清爽,但失败了要去翻日志,反而慢。
3.3 验收:cluster_state、槽位覆盖与一次跨槽读写
脚本跑完不等于集群可用。至少要做三层验证:容器层、集群层、数据层。三层都过,才算真的起来了。
# 第一层:六个容器是否都在跑,端口是否正确 docker ps --filter "name=redis-70" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" # 第二层:集群状态,重点看 cluster_state 和槽位覆盖 docker exec redis-7000 redis-cli -p 7000 cluster info | grep -E 'cluster_state|cluster_slots_assigned|cluster_known_nodes' docker exec redis-7000 redis-cli -p 7000 cluster nodes # 第三层:跨槽位读写,验证重定向是否正常 docker exec -it redis-7000 redis-cli -c -p 7000 set user:1001 tom docker exec -it redis-7000 redis-cli -c -p 7000 get user:1001第一层输出里如果有容器是Restarting或者Exited,说明配置有问题,先docker logs redis-700x看报错,别急着往下走。常见的是配置文件权限不对或者路径写错。
第二层里cluster_state:ok是硬指标,cluster_slots_assigned必须是 16384,少一个都不行。如果显示cluster_state:fail而槽位是满的,多半是有节点失联,看cluster nodes里有没有fail?标记。cluster_known_nodes应该是 6,如果是 1,说明其他节点根本没加入,问题出在总线端口或者通告 IP 上。
第三层是很多人跳过的。-c参数让客户端自动跟随MOVED重定向,不带这个参数写入非本节点的 key 会直接报错。这条命令能过,说明槽位分配和客户端重定向链路是通的。我一般还会再执行一次docker exec redis-7000 redis-cli --cluster check 127.0.0.1:7000,它会把槽位分布、每个主节点的从节点、以及配置一致性全打出来,比cluster nodes的信息密度高。
另外,宿主机上不必额外装 redis-cli,用docker exec进容器执行更省事,版本也和镜像一致,不会出现客户端版本和服务端不匹配的怪问题。
4. 拆脚本:redis.conf 六项、起容器的 for 循环与 create 参数
能跑通只是第一步。要在这套脚本上做改造——换端口段、加密码、扩到 8 个节点——就必须知道它内部三段关键逻辑长什么样。这一章把配置文件、容器启动、集群创建三块拆开,每块给出实际写法和我改过的参数。
4.1 redis.conf 里必须落到具体值的六项
集群模式的配置文件和平时的单机配置差别不大,但有六项必须明确写死,靠默认值会出问题。模板里通常用占位符替换端口,生成最终配置。
# 业务端口,每个节点不同,由脚本替换 port 7000 # 监听地址,集群模式下客户端可能从任何网卡进来 bind 0.0.0.0 protected-mode no # 开启集群模式,这一项不开后面所有操作都白搭 cluster-enabled yes # 节点状态文件,必须每个实例独立,且放在持久化目录里 cluster-config-file nodes-7000.conf # 节点超时,单位毫秒,决定主节点失联多久被判定为故障 cluster-node-timeout 15000 # 持久化,集群环境下建议开 AOF appendonly yes appendfsync everysec # 容器内必须前台运行,写 yes 会导致容器启动后立刻退出 daemonize no前两项里,protected-mode no和bind 0.0.0.0是配套的。只关保护模式不改 bind,外部还是连不上;只改 bind 不关保护模式,没设密码的情况下会拒接。集群模式必须两个都改。
cluster-config-file这一项值得多说一句。这个文件记录节点 ID、已知节点列表、槽位归属,是集群的「记忆」。它必须落在持久化目录里,否则容器重建后节点 ID 变了,原来的集群认不出它。同时它必须每个实例一份,用六份不同的文件名(或者六份不同目录下的同名文件)。脚本里那行sed替换端口的作用就在这里。
daemonize no是容器化的常识但经常被漏掉。有人为了省事发一份通用配置进去,容器起一个退一个,docker ps -a看到一堆Exited (0),日志里什么都没有——因为进程正常退出,docker 就认为任务结束了。
cluster-node-timeout 15000意味着主节点失联 15 秒后才触发故障转移。这个值调小会误判(网络抖一下就切主),调大会延长不可用时间。单机测试环境 5000 也能接受,生产上 15000 是常见取值。
4.2 起容器的 for 循环:为什么用 host 网络而不是端口映射
脚本起六个容器的逻辑就是一个 for 循环,核心是目录准备、配置生成、docker run三件事。网络方案的选择是这段代码里最需要解释的部分。
#!/usr/bin/env bash # 循环起 NODE_NUM 个容器,每个节点独立目录与配置 for i in $(seq 0 $((NODE_NUM - 1))); do port=$((BASE_PORT + i)) node_dir="${DATA_DIR}/${port}" # 每个节点独立的配置目录和数据目录 mkdir -p "${node_dir}/data" # 用模板生成该节点的配置,替换端口占位符 sed -e "s/__PORT__/${port}/g" \ -e "s/__PASSWORD__/${PASSWORD}/g" \ ./templates/redis.conf.tpl > "${node_dir}/redis.conf" # 关键:配置文件要能被容器内的 redis 用户读到 chmod 644 "${node_dir}/redis.conf" docker run -d \ --name "redis-${port}" \ --network host \ --restart=always \ -v "${node_dir}/redis.conf:/usr/local/etc/redis/redis.conf" \ -v "${node_dir}/data:/data" \ "${REDIS_IMAGE}" \ redis-server /usr/local/etc/redis/redis.conf done--network host是这段代码里最重要的一个决定。用 bridge 网络加端口映射也能跑,但要同时映射 12 个端口,而且集群通告的地址会变成容器内网 IP,导致节点之间互相连不上,必须在配置里额外加cluster-announce-ip和cluster-announce-port两项,一旦宿主机 IP 变了还得跟着改。用 host 网络把这些麻烦一次绕开:容器直接用宿主机网络栈,端口就是宿主机的端口,通告地址就是宿主机地址,脚本只需管一个变量。
代价是端口隔离没有了,同一台机器上不能起两套用相同端口的集群。测试环境够用,要跑多套就把BASE_PORT改成 7100、7200 这样。
reprise=always保证宿主机重启或 docker 重启后容器自动拉起。注意这只拉起容器,不保证集群状态健康——节点都起来了但互相连不上,还是得人工介入。
chmod 644那行容易被忽略。容器里 redis 进程以非 root 用户运行,宿主机上如果配置文件是600且属主是 root,容器内读不到,启动直接失败,报错信息还很含糊。这个坑单列一节在后面讲。
4.3 cluster create 的三个参数:replicas、timeout 与 --cluster-yes
六个容器都健康之后,才轮到建集群。这条命令的参数不多,但每一个都影响结果。
# 等待所有节点端口就绪后再执行,简单轮询即可 for i in $(seq 0 $((NODE_NUM - 1))); do port=$((BASE_PORT + i)) until docker exec "redis-${port}" redis-cli -p "${port}" ping 2>/dev/null | grep -q PONG; do sleep 1 done done # 用前三个地址做主节点,后面三个作为各自的从节点 docker exec redis-7000 redis-cli --cluster create \ 192.168.10.11:7000 192.168.10.11:7001 192.168.10.11:7002 \ 192.168.10.11:7003 192.168.10.11:7004 192.168.10.11:7005 \ --cluster-replicas 1 \ --cluster-yes--cluster-replicas 1决定每个主节点配几个从节点,1 就是三主三从。传 0 会建出六个主节点,槽位六等分,没有冗余;传 2 需要至少九个节点,六个节点传 2 会直接报错。这一项改之前先算清楚节点数。
--cluster-yes跳过交互式确认。没有这个参数时,命令会打印一份槽位分配方案然后等你输入yes,脚本里没有标准输入就会一直挂着,表现为「脚本跑完但集群没建起来」。这是个很典型的静默失败,日志里看不出异常。
还有两个可选参数值得知道:--cluster-timeout控制节点握手的超时时间,网络慢的环境可以调大;带密码的集群需要在每条redis-cli后面加-a,同时--cluster create也要带,否则 create 阶段能过,后续--cluster check会认证失败。
整个 create 过程会输出每个节点的槽位区间和主从关系,最后一行是[OK] All 16384 slots covered.。看到这行才算真的成功,中间任何一条ERR都不能忽略。
5. 避坑与排查:五个把集群卡在半路的现场
这套脚本在干净机器上一次过的概率很高,但在已经跑过其他服务的机器上,翻车点集中在那几个位置。下面五条是我在实际环境里反复遇到的,每条按现象、原因、解决三段写,照着对号入座基本能定位。
5.1 卡在 Waiting for the cluster to join:总线端口没通
现象:cluster create执行后停在Waiting for the cluster to join不动,等几分钟也不结束,最后超时退出。docker ps看六个容器全是Up状态。
原因:节点之间的 gossip 通信走的是总线端口(业务端口 + 10000),只放行 7000-7005 是不够的。firewalld 默认拒绝未放行端口,业务端口通了、总线端口没通,节点能互相 ping 但握不上手,表现就是这个无限等待。
解决:一次性放行两段端口再重试。
# 放行业务端口与总线端口 firewall-cmd --permanent --add-port=7000-7005/tcp firewall-cmd --permanent --add-port=17000-17005/tcp firewall-cmd --reload firewall-cmd --list-ports # 验证总线端口真的在监听 ss -lntp | grep -E ':1700[0-5]'重试之前要清理现场:docker rm -f六个容器,删掉各节点data目录下的nodes.conf,再重新执行脚本。不清理的话旧节点状态还在,可能建出一个半残的集群。
5.2 容器起来就退出:redis 用户读不到宿主机上的 conf
现象:docker ps只看到部分容器,其余在docker ps -a里显示Exited (1)。docker logs里能看到Fatal error, can't open config file之类的内容,或者干脆没有任何输出。
原因:容器内的 redis 以非 root 用户运行,宿主机上用 root 创建的配置文件默认权限是600或644但属主不对,容器内读不到。另一个变种是 SELinux 处于 enforcing,即使权限是644也会被拒绝,日志里出现Permission denied。
解决:先确认权限,再确认 SELinux。
# 看配置文件权限,应该是 644 且容器内用户可读 ls -l /data/redis-cluster/7000/redis.conf # 改成所有用户可读 chmod 644 /data/redis-cluster/7000/redis.conf # SELinux 如果是 enforcing,给目录打标签 getenforce chcon -Rt container_file_t /data/redis-clusterchcon是临时方案,重启或重新打标签后会失效。长期做法是用semanage fcontext加规则,或者干脆把 SELinux 设为 permissive。测试环境我一般直接 permissive,省得反复折腾。
5.3 重复执行脚本:nodes.conf 残留把旧节点 ID 带进来
现象:第二次执行脚本,容器都起来了,但cluster nodes里出现一堆fail或者handshake状态的节点,cluster_state:fail。
原因:nodes.conf记录了节点 ID 和已知节点列表。容器重建后节点 ID 理论上会变,但如果数据目录挂载着旧文件,新节点会带着旧 ID 启动,和集群里其他节点的记录对不上。反复执行脚本而不清理数据目录,就一定会撞上。
解决:重置时按固定顺序清理,别只删容器。
# 停并删容器 docker rm -f $(docker ps -aq --filter "name=redis-70") # 只删节点状态和持久化数据,保留 redis.conf for p in $(seq 7000 7005); do rm -rf /data/redis-cluster/${p}/data/* done # 确认没有残留进程占用端口 ss -lntp | grep -E ':(700[0-5]|1700[0-5])\b'清理完再跑脚本,集群会以全新节点身份加入。如果只是想重启集群不想丢数据,那就别删data目录,直接docker start原来的容器。
5.4 改了 conf 不生效:单文件挂载的 inode 陷阱
现象:改了宿主机的redis.conf,docker restart之后配置没变,CONFIG GET拿到的还是旧值。
原因:-v挂载单个文件时,docker 绑定的是文件的 inode。用vim直接编辑会新建文件再替换,inode 变了,容器里挂的还是旧 inode 指向的内容。sed -i同理。这个坑非常隐蔽,因为文件内容看起来确实改了。
解决:改配置后重建容器,或者改成目录挂载。
# 方式一:重建容器,让它重新绑定新文件 docker rm -f redis-7000 # 然后重新执行起容器的那段脚本 # 方式二:挂载整个配置目录(推荐) # -v /data/redis-cluster/7000:/usr/local/etc/redis # 容器内用 /usr/local/etc/redis/redis.conf方式二从根本上避开 inode 问题,代价是容器内的配置目录里会多出data子目录,不影响使用。我现在的习惯是配置文件用目录挂载,改完docker restart就生效。
5.5 firewalld 与端口占用:看着像网络问题,其实是端口冲突
现象:脚本执行到某个节点时docker run报bind: address already in use,前面的节点已经起来了,后面的全失败。
原因:端口被其他服务占用。7000 这类端口在装了其他中间件的机器上经常被占,或者上一次的容器没删干净,残留进程还挂着。
解决:先定位占用者,再决定是换端口还是杀进程。
# 定位占用端口的进程 ss -lntp | grep ':7000' # 或 lsof -i :7000 # 如果是残留容器 docker ps -a --filter "name=redis-7000" docker rm -f redis-7000如果是别的业务在用,最省事的做法是改BASE_PORT,整套集群平移到 7100 段,比协调别人让端口快得多。改完记得同步改防火墙规则和后续所有redis-cli命令里的端口。
6. 上线前再走一遍:杀主验证、槽位自检与把脚本收成模板
集群能读写不代表能扛故障。真正验收要做一次主节点故障演练:先记下 7000 的从节点是谁,然后docker stop redis-7000,盯着cluster nodes看状态变化。cluster-node-timeout是 15000,所以大约 15 秒后那个从节点会从slave变成master,槽位自动接管;再docker start redis-7000,它回来之后会以从节点身份重新加入。整个过程不需要人工干预,才算这套脚本配的 timeout 和主从关系是合理的。
# 记录当前主从关系 docker exec redis-7000 redis-cli -p 7000 cluster nodes | grep master # 杀主,观察提升 docker stop redis-7000 sleep 20 docker exec redis-7001 redis-cli -p 7001 cluster nodes # 恢复,确认降级为从 docker start redis-7000 sleep 5 docker exec redis-7001 redis-cli -p 7001 cluster nodes演练完记得跑一次全量自检,把槽位分布和配置一致性一次看全:
docker exec redis-7000 redis-cli --cluster check 127.0.0.1:7000输出里重点看三项:16384 个槽是否全覆盖、每个主节点是否都有从节点、有没有节点处于fail状态。缺从节点的话,故障时就真的没有冗余了。
要把这套脚本改成能反复用的模板,我一般做两件事。一是把可变参数抽到单独的文件里,脚本开头source ./cluster.env,换环境只改这一个文件:
# cluster.env HOST_IP="192.168.10.11" BASE_PORT=7000 NODE_NUM=6 DATA_DIR="/data/redis-cluster" PASSWORD="" REDIS_IMAGE="redis:6.2"二是给脚本加一个reset子命令,封装前面那套清理逻辑——删容器、清data目录、检查端口。有这东西,重建集群就是bash redis-cluster.sh reset && bash redis-cluster.sh install两条命令,比手敲五条安全得多。
血泪经验是:nodes.conf一定要和数据一起备份、一起清理,千万别分开操作。我早期图省事只备份了 AOF 文件,恢复的时候节点 ID 对不上,集群直接建不起来,最后只能全清重来。从那以后我每次动集群之前,都强制走一遍「停容器 → 备份 data 目录 → 执行操作」的流程,多花两分钟,省掉的是整晚的排查。希望帮到你。
本文还有配套的精品资源,点击获取