简介:面向需要在 CentOS 7.x 上快速搭建 Redis 集群的运维与开发人员,这份资源以 shell 脚本实现了 Docker 环境下的一键部署,只需按 README 说明将参数传递给安装脚本,即可自动完成镜像加载、节点创建与集群初始化等步骤,实测可用。压缩包共含 6 个文件,以 sh 脚本为主,涵盖部署、卸载与辅助构建三个环节,另附 redis.conf 配置模板、redis_docker_images_latest.tar 离线镜像包和 markdown 格式的说明文档,整体约 22.46MB。目前已有 1055 人学习下载。通过阅读脚本可理解基于 Docker 的 Redis 集群搭建流程与参数化设计思路,也能直接复用或按需调整,免去手工逐节点配置的繁琐与排错成本。
1. 一键部署Redis集群,为什么shell脚本才是CentOS 7上的最优解
手动搭建Redis集群是个体力活:先把6份redis.conf的端口、总线端口、持久化路径逐项改好,再逐个启动容器,最后还要敲几十条meet、replicate和addslots命令。任何一步IP写错或端口被占用,集群状态就卡在fail,排查起来让人头疼。这套流程放在CentOS 7.x上尤其繁琐——旧内核、老版本docker都对网络模式有约束,没有脚本兜底很容易翻车。
所谓docker一键部署redis集群shell脚本,就是把配置生成、容器创建、网络分配、节点握手、主从绑定、槽位分配这六个环节全部收敛到一个可重复执行的脚本里。这一步到位的能力对两类人价值最大:一类是需要在测试环境频繁重建集群验证业务逻辑的开发者,另一类是生产环境交付前需要快速铺多个独立集群的运维。脚本解决的不只是省时间,更重要的是消除人工操作的不确定性——同样的参数跑十次,结果必须一致。
2. Redis集群的容器化选型:固定IP、总线端口与脚本要跨过的三道坎
2.1 三种部署方式对比:为什么不用docker-compose
在CentOS 7.x上把Redis集群容器化,常见做法有三种:纯docker命令裸跑、docker-compose编排、shell脚本自动化。三者各有边界,但针对"多个节点组成一个集群"这个诉求,shell脚本反而是最省心的解。
| 部署方式 | 配置管理粒度 | 固定IP支持 | 故障恢复 | 适用场景 |
|---|---|---|---|---|
| 纯docker命令 | 单容器 | 需手动指定 | 需人工介入 | 临时验证单节点 |
| docker-compose | 多容器 | 支持但需写死IP | 需配合外部脚本 | 有固定拓扑的标准化部署 |
| shell脚本 | 批量生成与统一操作 | 主动分配并写入配置 | 可内建健康检查 | 多节点集群的反复重建 |
docker-compose适合"服务集合"的编排,比如一个Web应用带一个Redis单节点。但Redis集群是"多实例+状态内联"的拓扑,节点之间需要双向发现,compose文件里每个服务都要写独立IP、独立端口映射,写出来的文件比shell脚本还要啰嗦。而且compose在CentOS 7上的旧版本对自定义网络的支持有差异,一旦涉及动态节点数量调整,脚本的for循环明显更灵活。
2.2 集群创建流程:从配置文件生成到槽位分配
Redis集群从空壳到可用的完整链路分四步:生成配置、启动实例、节点握手、分配槽位。脚本的价值不是替代redis-trib或redis-cli的集群命令,而是把前三步重复劳动自动化,最后一步用命令一键触发。
配置生成阶段,每个节点需要独立的port、cluster-config-file、appendonly路径,但这些参数除了端口,其余高度相似,用函数生成模板最合适。启动实例阶段要处理容器数量和网络互通的问题。节点握手阶段,Redis 5.0之后可以直接用redis-cli --cluster create自动完成meet和主从分配,但前提是节点之间路由可达、端口开放。槽位分配是这个命令内部完成的,包含主节点的16384个槽和从节点的replicate绑定。
脚本要控制的变量就清楚了:每个节点的IP、端口、总线端口(端口+10000)、主从配对方式。其中总线端口最容易被忽略——Redis集群节点间通信用的是port+10000的端口,容器端口映射漏掉它,节点能启动但永远无法完成meet握手。
2.3 网络模式与端口映射:脚本要解决的三个"硬问题"
CentOS 7.x上给Redis集群容器选网络模式,绕不开三个问题:容器IP漂移、总线端口暴露、宿主机端口占用。隔离这三个问题就握住了脚本设计的关键。
第一个问题是容器IP漂移。默认bridge模式下容器每次重启IP都会变化,而Redis集群的nodes.conf里记录的是节点启动时的IP,一旦变了集群就认为节点断开。解法是创建自定义bridge网络并手动指定静态IP,脚本里用docker run --ip显式分配。
第二个问题是总线端口。即使容器IP固定,如果宿主机访问不到端口映射后的总线端口,节点之间还是会握手失败。端口映射要同时覆盖数据端口和总线端口两个端口,总共6个节点就要映射12个端口,脚本里用同一份端口列表循环生成映射参数最稳妥。
第三个问题是端口占用的冲突排查。生产环境里6000-6010和16000-16010这两段经常被其他中间件占用,脚本最好支持从外部传入起始端口,而不是硬编码,这样遇到冲突时改一行启动参数就能换一段干净端口。
3. 编写一键部署脚本:从配置模板到cluster create
3.1 脚本骨架:变量定义与配置生成函数
写脚本前先把变量拆成"使用者可调"和"脚本内部使用"两组。使用者可调的部分是节点数量、起始端口、Redis镜像版本、网络网段;内部使用的部分是IP列表、端口列表、配置文件名。这样设计的好处是将来调整集群规模时不需要改动逻辑代码。
#!/bin/bash # 可调参数区域 REDIS_IMAGE="redis:7.0-alpine" # 镜像按需更换,测试环境用alpine版启动更快 BASE_PORT=7001 # 起始数据端口,总线端口自动+10000 NODE_COUNT=6 # 节点总数,生产建议至少6个 NET_NAME="redis-cluster-net" # 自定义网络名 NET_SUBNET="172.30.0.0/16" # 网段,避免与公司现有网段冲突 START_IP="172.30.0.10" # 容器起始IP,配合递增逻辑分配 # 内部变量区域 PORTS=() IPS=() TOTAL=0逻辑说明:镜像用alpine版本主要是为了减少拉取时间和容器体积,实测同一台机器跑6个alpine容器比debian版的Redis容器少占约600MB内存。网段必须手动指定,不能依赖docker自动分配的默认段,否则宿主机已有的容器网络可能冲突。注意NODE_COUNT设定为6是最小集群配置(3主3从),少于6个脚本应直接退出提示,防止建立一个没有故障转移能力的假集群。
参数说明:START_IP决定了容器IP的起点,脚本后续按顺序递增。如果你在开发环境已经有别的容器占了这个网段,把这个起始IP往后挪即可。Redis版本建议选带cluster支持的稳定版,太老的版本对总线的处理方式不同,脚本里--cluster create的参数兼容性会有差异。
3.2 配置文件生成:用一份模板渲染出多份conf
配置生成这一步的要点是"统一基础配置,差异化端口类参数"。不要为每个节点手写完整conf,而是通过cat模板配合变量替换一次性生成。下面代码把公共参数和差异参数分开处理,避免漏改。
generate_conf() { local idx=$1 local port=$2 local dir="/data/redis-cluster/conf/redis-${port}.conf" mkdir -p "$(dirname "$dir")" cat > "$dir" <<EOF port ${port} cluster-enabled yes cluster-config-file nodes-${port}.conf cluster-node-timeout 5000 appendonly yes daemonize no protected-mode no bind 0.0.0.0 EOF }逻辑说明:配置文件里的bind 0.0.0.0是关键——容器内的Redis默认只绑定本地回环地址,如果这里不放开,跨容器的节点握手必然失败。cluster-config-file为每个节点独立命名,避免多个容器共享同一个data目录时互相覆盖文件。cluster-node-timeout设为5000毫秒是经验值,设太短网络抖动就会被误判为主节点下线,触发不必要的故障迁移。
参数说明:这段模板中没有指定cluster-announce-ip,这是有意为之——在自定义bridge网络里手动指定了容器IP,且没有做NAT映射,Redis可以自动识别自己的IP。如果后续改成端口映射模式(比如容器在远程宿主机),就必须在模板里追加cluster-announce-ip为宿主机IP,这一点会在避坑章节详解。
3.3 启动容器与健康检查:等待就绪而非盲目sleep
启动容器不能一把梭直接建集群,必须等所有节点进入"集群待命"状态。常见做法是循环执行redis-cli -p PORT cluster info,从返回结果里判断cluster_state不再是fail后才继续。sleep固定秒数也算方案,但对老机器不友好——配置生成慢、容器启动慢,很容易在还没就绪时就执行建集群命令。
start_container() { local idx=$1 local ip=$2 local port=$3 docker run -d \ --name "redis-node-${idx}" \ --network ${NET_NAME} \ --ip "${ip}" \ -p ${port}:${port} -p $((port+10000)):$((port+10000)) \ -v "/data/redis-cluster/conf/redis-${port}.conf:/usr/local/etc/redis/redis.conf" \ ${REDIS_IMAGE} \ redis-server /usr/local/etc/redis/redis.conf } wait_for_cluster_ready() { local ready=0 for _ in $(seq 1 30); do ready=0 for port in "${PORTS[@]}"; do info=$(docker exec "redis-node-1" redis-cli -p "${port}" cluster info 2>/dev/null) if echo "$info" | grep -q "cluster_state:ok"; then ready=$((ready+1)) fi done [ "$ready" -eq "${#PORTS[@]}" ] && return 0 sleep 2 done return 1 }逻辑说明:wait_for_cluster_ready循环里每次都访问第一个容器来探测所有端口,而不是分别exec进每个容器,这样减少docker exec的上下文切换开销。如果30次循环内(约60秒)所有节点都没有返回cluster_state:ok,脚本返回非0退出码。退出之后建议先看容器日志,绝大多数情况是配置文件的appendonly路径没有创建对应目录导致进程直接退出。
参数说明:健康检查的端口探测用了cluster info而不是ping,因为ping只证明进程活着,而cluster info能确认集群模块已加载、节点状态正常。这里有一个细节:刚启动的节点集群状态可能是fail,需要等待它尝试连接其他节点失败后才能进入待命状态,这是正常的,等循环跑完即可。
3.4 创建集群与槽位分配:一行命令的事
节点全部就绪后,剩下的工作就是调用redis-cli --cluster create。这一行命令会自动执行meet、分配主从、迁移槽位三个动作,但需要传入正确的节点列表和副本策略。
create_cluster() { local node_list="" for ((i=0; i<NODE_COUNT; i++)); do node_list+="${IPS[$i]}:${PORTS[$i]} " done docker exec redis-node-1 \ redis-cli --cluster create ${node_list} \ --cluster-replicas 1 \ --cluster-yes }逻辑说明:--cluster-replicas 1表示每个主节点绑定一个从节点,6个节点生成3主3从的拓扑。脚本里把节点列表拼成空格分隔的字符串传输是这里最常用的方式——确保节点列表必须用IP而非容器名,因为Redis是通过TCP建立连接,它不认docker的内部DNS。--cluster-yes跳过交互确认,一键执行的前提。
参数说明:--cluster-replicas的值可以调成2表示一主双从,但节点总数必须能被(副本数+1)整除,否则命令会直接报错。脚本可以对这个数量做一次除法校验,提前暴露错误比等命令报错更友好。
4. 实战跑通:在CentOS 7.x上从零部署6节点集群
4.1 环境准备:docker安装与镜像拉取
CentOS 7.x默认自带的docker版本较旧,建议先确认docker服务状态,再决定是否需要升级。镜像拉取前先确认网络可达性,Red Hat系的机器对外部仓库的访问受限比较常见,遇到超时换一个镜像源即可。
# 检查docker服务状态 systemctl status docker | head -5 # 拉取redis镜像 docker pull redis:7.0-alpine # 创建自定义网络,手动指定网段 docker network create --subnet=172.30.0.0/16 redis-cluster-net逻辑说明:这里特意把网络创建从脚本中拆出来放前面执行,是因为网络创建是一个幂等性较差的操作——重复执行同一个名称会报错,而脚本设计上要允许反复部署。先建好网络,脚本只负责创建和销毁容器,这样脚本跑失败后,清理容器后重新执行即可,不必处理网络冲突。
参数说明:网段172.30.0.0/16可以用docker network inspect查看是否与其他网络重叠。如果你所在的服务器已经有其他docker网络占用了这个段,换成172.31.0.0/16即可。这个参数不需要和公司内部物理网络对齐,因为容器网络是隔离的,只要和宿主机已有docker网络不冲突即可。
4.2 执行一键脚本:完整输出解读
把前文的三个函数串起来,加上变量初始化和最终输出展示,就是一个可落地的完整脚本。执行时用bash deploy.sh,不要直接./deploy.sh,避免忘记赋执行权限。
#!/bin/bash # 变量初始化部分上面已定义,此处省略 for ((i=0; i<${NODE_COUNT}; i++)); do PORTS+=($((BASE_PORT + i))) IPS+=("172.30.0.$((10 + i))") done echo "开始创建配置..." for i in "${!PORTS[@]}"; do generate_conf "$i" "${PORTS[$i]}" done echo "开始启动容器,共 ${NODE_COUNT} 个节点..." for i in "${!IPS[@]}"; do start_container "$i" "${IPS[$i]}" "${PORTS[$i]}" done echo "等待节点就绪..." if ! wait_for_cluster_ready; then echo "节点未在预期时间内就绪,请检查容器状态" exit 1 fi echo "开始创建集群..." create_cluster逻辑说明:脚本先根据BASE_PORT和NODE_COUNT生成两个平行数组PORTS和IPS,所有后续操作都是遍历这两个数组。这种设计避免在函数内部动不动重复计算IP或端口,职责分离清晰。执行过程中每个阶段都有提示输出,方便在长时间部署时定位卡在哪个环节。
参数说明:IPS+=("172.30.0.$((10 + i))")这一行把起始IP设为了172.30.0.10,留出了前10个地址给以后的额外容器。如果你的宿主机内存紧张,可以把NODE_COUNT临时改成6以外的值,但注意必须是2的倍数且能被副本数加一整除,否则最后一行create_cluster会失败。
4.3 执行后的状态验证
脚本执行完,不要急着认为集群可用了,先在宿主机上做三个快速验证:端口连通性、节点状态、槽位分配情况。这三个检查能覆盖绝大多数部署问题。
# 验证所有数据端口和总线端口都在监听 for p in $(seq 7001 7006); do ss -tlnp | grep -E ":${p}|:$((p+10000))" | wc -l done # 查看集群节点状态 redis-cli -p 7001 cluster nodes | awk '{print $1, $2, $3, $7}' # 检查槽位是否分配完整 redis-cli -p 7001 cluster slots | wc -l逻辑说明:端口监听检查用ss而不是netstat,CentOS 7上ss默认安装且输出更清晰。每个端口输出至少2行(数据端口和总线端口各一行),如果有端口输出为0,说明容器映射失败或进程未启动。cluster slots输出的是分配完成的槽位区间列表,正常6节点集群会输出多个区间的组合,总数不重要,关键是没有返回错误。
4.4 redis.conf里必改的6个参数对照
容器化部署Redis集群,有6个参数几乎每次都要手动调,脚本模板里已经改好了,但理解背后的原因能帮你快速排查别人写的脚本:
| 参数名 | 默认值 | 脚本中的值 | 原因 |
|---|---|---|---|
| cluster-enabled | no | yes | 关闭则Redis以单机模式运行 |
| cluster-config-file | nodes.conf | nodes-端口.conf | 多容器共用默认值会互相覆盖 |
| cluster-node-timeout | 15000 | 5000 | 缩短故障转移判定时间 |
| appendonly | no | yes | 容器重启后数据可恢复 |
| protected-mode | yes | no | 开放跨容器访问的必要条件 |
| bind | 127.0.0.1 | 0.0.0.0 | 绑定回环地址会阻断跨节点通信 |
参数修正的逻辑:cluster-node-timeout默认值是15000毫秒,对容器网络来说太长,主节点宕机后从节点要等15秒才开始竞选,业务侧体现为一段明显的写入超时。降为5000毫秒后故障转移的响应速度更快,但要注意这个值设置太小会加重误判风险,比如GC停顿超时会频繁触发主从切换。
5. 集群落地避坑:从翻车现场到稳定运行的5条经验
5.1 容器重启后集群瘫痪:固定IP与announce-ip的联动关系
现象:某次服务器重启后,所有容器自动恢复运行,但业务侧报错"集群不可用",cluster nodes里全部节点互相标记为fail。
原因:虽然脚本创建容器时指定了--ip固定地址,但Redis集群的nodes.conf文件里记录的是节点首次启动时的IP。重启过程中如果docker网络重建、IP分配被其他容器抢占,节点启动后拿着新IP去连接旧IP,自然握手失败。
解决:在generate_conf函数中显式加上cluster-announce-ip,值为脚本分配的静态IP。这样即使节点重启后IP变了,Redis也会用announce-ip向其他节点宣告自己,而非读取网卡的实际地址。
5.2 槽位迁移失败:手动分配过多的隐性成本
现象:脚本建集群时人为指定了--cluster-replicas 1,然后想扩容,把新节点加入集群并迁移部分槽位,结果迁移中途频繁超时,部分slot显示migrating状态卡住不动。
原因:当集群的key数量较大(生产环境常见百万级别),迁移槽位内部执行MIGRATE命令是有超时上限的。脚本自动建集群时槽位为空瞬间完成,但后续手动迁移是大key或热点key时,单次迁移超过cluster-node-timeout就会被中断。
解决:扩容动作不要完全手敲命令,建议分段执行:先reshard少量槽位(比如100个),观察迁移耗时;如果单key迁移超过5秒,用redis-cli --cluster reshard配合--cluster-from、--cluster-to参数精细控制,必要时在业务低峰期操作。这里的教训是脚本只解决"从零建集群",解决不了"动态扩容"场景,要做成独立脚本分开管理。
5.3 时间不同步引发的选举异常
现象:集群偶尔出现从节点自动晋升为主节点,但主节点并未宕机,业务侧出现短暂只读。
原因:Redis集群的故障转移依赖节点间的PING/PONG心跳和选举超时机制,而选举中会对比节点间的cluster-node-timeout和客观下线时间戳。如果宿主机没有配置NTP时间同步,多个容器的时间偏差超过几秒,从节点可能误判主节点超时,提前发起选举。
解决:在宿主机配置NTP服务同步时间,同时在docker run参数里加上--sysctl不是正确解法——正确做法是在宿主机上执行timedatectl set-ntp yes,CentOS 7默认可能没开启。容器内的时间由宿主机内核统一维护,宿主机同步了,容器自然同步。此坑在虚拟化环境下尤其常见,母鸡时间漂移会导致所有容器集体中招,部署脚本里补一段时间同步检查很有必要。
5.4 docker版本过旧导致网络能力缺失
现象:在CentOS 7.x执行脚本时,docker run --ip报错"user specified IP address is not supported"。
原因:docker老版本创建自定义网络后,需要对应的网络驱动支持静态IP分配。CentOS 7自带的docker 1.13版本对自定义bridge网络的IP指定支持不完整,换docker network create --driver bridge也一样。
解决:升级docker到支持docker network create --subnet的版本,或者改用host网络模式。host模式不需要映射端口、不需要指定IP,但6个节点的端口都要手动规划且宿主机端口被独占。多数生产服务器会选择升级docker,原因在于host模式下容器互相通信都走宿主机回环网卡,隔离性差而且容易被其他服务影响端口。
5.5 数据目录权限不足导致的隐性启动失败
现象:容器启动后立即退出,docker logs显示Can't open the append-only file: Permission denied。
原因:/data/redis-cluster/conf目录挂在宿主机上,但docker容器内的Redis进程以redis用户运行,该用户对宿主机目录没有写权限。CentOS 7的SELinux还会再给一层限制,即使chmod 777也可能被SELinux拦截。
解决:在启动容器前对数据目录执行chcon -Rt container_file_t /data/redis-cluster,或直接把宿主机目录chmod -R 777。更稳妥的做法是生成配置目录时就用install -d -m 777,避免在脚本中单独依赖后续的手工权限调整。这一个坑非常隐蔽,环境上清理过的机器第一次部署大概率会踩中,脚本里加上权限初始化代码能少一次排障。
6. 压测验证与收尾:cluster info之外的三个自检技巧
集群建完不测试就等于没做。除了看一眼cluster_state:ok,我还会做三个自检,全通过后才敢把连接串发给业务方。
第一个自检是故障转移演练。用docker stop redis-node-1模拟主节点宕机,然后观察从节点的反应时间。正常的集群会在cluster-node-timeout加上选举时间(约6-10秒)内完成主从切换。如果超过15秒还没恢复写入,大概率是心跳参数或网络配置有问题。这个演练值得每次部署后都做一次,它能验证从节点的数据完整性,也能确认客户端具备重连能力。演练结束后重启停掉的容器,它会以从节点身份自动重新加入集群。
第二个自检是重启恢复验证。把部署脚本所有容器全部docker stop,再全部docker start,等待两分钟观察集群是否自动恢复。这里最容易暴露的是announce-ip配置缺失——恢复后节点间会互相报fail。一个健康的集群应当无人工介入地完成恢复,它的选举机制和节点发现机制才是真正可靠。
第三个自检是写入压测的边界验证。用redis-benchmark压一下集群的写入性能,不是为了测QPS,而是确认集群的reshard和节点的replicate不会出现瓶颈。使用redis-cli -c(集群模式)写入一批带hash tag的key,观察slot在各个节点间的分布是否均匀。不均匀时调整hash tag的使用方式,这比事后加节点更有效——在创建集群时就规划好业务key的分布规则,比后期扩缩容的成本低得多。
这三个自检做完,基本可以从容地把集群交付出去。我的习惯是把部署脚本和自检脚本放在同一个目录,每次重建集群后顺手执行一遍。毕竟Redis集群这个技术方向真正考验人的不是建起来,而是怎么证明它坏了能自己好。希望这篇笔记能帮你在CentOS 7.x上少走几步弯路,把脚本一次跑通,把坑留在文档里而不是生产环境。
本文还有配套的精品资源,点击获取