CentOS 7 下用 Shell 脚本 + Docker 一键部署 Redis 3主3从集群
简介这份资源提供了一套基于 Docker 的 Redis 集群一键部署方案面向需要在 CentOS 7.x 环境下快速搭建 Redis 集群的运维与后端开发人员。使用者只需按说明将参数传递给安装脚本即可自动完成镜像加载、容器编排与集群初始化省去手工配置节点与槽位的繁琐过程适合具备基础 Linux 与 Docker 操作能力、希望快速验证或落地集群环境的技术人员。资源包共 6 个文件包含 3 个 shell 脚本、1 个镜像 tar 包、1 个 conf 配置文件与 1 个 md 说明文档压缩包约 22.46MB脚本负责自动化部署与卸载配置文件定义集群参数镜像包则免去联网拉取步骤。目前已有 1055 人学习下载。整体方案亲测可用读者可借此获得一套可直接复用的部署脚本与排错参考快速理解 Redis 集群在容器环境下的搭建流程与参数组织方式。1. 为什么我宁愿写 200 行 shell也不手动搭一次 Redis 集群在 CentOS 7.x 上手工搭一套 Redis 集群我干过不止一次每次都要重复这几步装 docker、拉镜像、写 6 份 redis.conf、改 bind 和 cluster-announce-ip、逐个redis-server起容器、redis-cli --cluster create分配槽位、再手动验证主从。中间任何一步 IP 写错、端口冲突、防火墙没放行就得从头排查。所以后来我把整套流程压成了一个 shell 脚本配合 docker 一条命令拉起 3 主 3 从的 Redis 集群专门适配 CentOS 7.x 这套老环境。这篇笔记讲的就是这个脚本怎么写、参数怎么定、哪些坑我踩过。适合手上还有一批 CentOS 7 机器、想用 docker 快速起 Redis 集群做缓存或做 Spring Cloud 这类微服务中间件的同学也适合刚学 shell 脚本、想找一个真实可跑的项目练手的人。脚本不依赖任何外部编排工具纯 bash docker能复现。2. 集群拓扑与脚本整体设计先想清楚再动手2.1 为什么选 3 主 3 从而不是 6 主Redis Cluster 官方推荐的最小生产拓扑是 3 主 3 从共 6 个节点。原因是集群做故障转移需要「多数派」投票3 个主节点里挂掉 1 个剩下 2 个仍占多数能把它的从节点提升为主集群继续可用。如果只起 3 个主节点不带从一旦某个主挂了它负责的 16384 个哈希槽里那部分就没人管整个集群进入 fail 状态写请求直接报CLUSTERDOWN。那为什么不上 6 主因为主节点越多槽位分片越细客户端维护的连接和重定向开销越大而单机 docker 环境下 6 个主节点全挤在一台机器上故障域根本没隔离多出来的主只是徒增内存和端口占用。3 主 3 从在单机演示和中小规模缓存场景里是性价比最高的选择。常见做法是每个节点分配 512MB 到 1GB 内存上限6 个节点加起来对一台 8G 内存的 CentOS 7 机器压力可控。端口规划上我一般用 7001 到 7006 这 6 个端口奇数端口做 master偶数端口做对应的 slave这样看端口号就能猜到角色。每个节点还需要一个「集群总线端口」规则是服务端口 10000也就是 17001 到 17006这个端口用于节点间 gossip 通信防火墙必须一起放行很多人只开了 7001 忘了 17001结果cluster create一直卡在Waiting for the cluster to join。2.2 脚本要解决的四个核心问题一个能用的部署脚本本质要处理四件事缺一个都跑不起来。第一是环境准备CentOS 7.x 默认的 yum 源里 docker 版本很老需要换源、装 docker-ce、启动并设置开机自启。第二是配置生成6 个节点不能共用一份 redis.conf每个节点的端口、集群总线端口、节点自身 IP 都要单独写否则节点之间互相找不到。第三是集群初始化容器起来只是 6 个孤立的 Redis 实例必须用redis-cli --cluster create把它们组成集群并分配槽位。第四是幂等与清理脚本重复执行时不能因为容器已存在就报错退出要能先清理旧容器再重建。下面这张表是我脚本里用到的核心变量先列出来后面代码会引用变量名含义示例值REDIS_PORT_START起始端口7001NODE_COUNT节点总数6MASTER_COUNT主节点数3REDIS_PASSWORD集群密码自定义HOST_IP宿主机内网 IP192.168.1.100DATA_DIR数据挂载目录/data/redis-clusterREDIS_IMAGE镜像redis:6.2HOST_IP这个变量是整份脚本里最容易翻车的地方。集群模式下节点向其他节点宣告自己的地址用的是cluster-announce-ip如果你填了127.0.0.1那么其他容器拿到这个地址后根本连不上集群永远组建不成功。必须填宿主机在局域网里的真实 IP用ip addr或hostname -I取。2.3 目录结构与数据持久化设计我习惯把每个节点的配置和数据分开存放目录结构长这样/data/redis-cluster/ ├── 7001/ │ ├── redis.conf │ └── data/ ├── 7002/ │ ├── redis.conf │ └── data/ ... └── 7006/每个节点一个目录redis.conf和data都在里面容器启动时把这两个路径挂载进去。这样做的好处是删容器不删数据重建集群时数据还在排查问题时直接进对应目录看配置和日志不用docker exec进去翻。数据目录一定要挂载出来否则容器一删RDB 和 AOF 全没了这在生产里是灾难。配置生成用 shell 的 for 循环配合 heredoc 写比用 sed 去改模板文件更直观也不容易因为转义问题出错。下面这段就是生成单个节点配置的核心逻辑# 循环生成 6 个节点的 redis.conf for i in $(seq 0 $((NODE_COUNT - 1))); do PORT$((REDIS_PORT_START i)) BUS_PORT$((PORT 10000)) NODE_DIR${DATA_DIR}/${PORT} mkdir -p ${NODE_DIR}/data cat ${NODE_DIR}/redis.conf EOF port ${PORT} bind 0.0.0.0 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 cluster-announce-ip ${HOST_IP} cluster-announce-port ${PORT} cluster-announce-bus-port ${BUS_PORT} appendonly yes requirepass ${REDIS_PASSWORD} masterauth ${REDIS_PASSWORD} daemonize no dir /data EOF done这段代码里几个参数值得单独说。bind 0.0.0.0配合protected-mode no是为了让容器外部能连进来生产环境如果只在 docker 内网通信可以收紧成具体网段。cluster-announce-ip填宿主机 IP这是集群能组起来的关键。requirepass和masterauth必须同时设前者是客户端连进来的密码后者是主从之间同步用的密码只设一个会导致从节点连不上主节点日志里报NOAUTH。appendonly yes开启 AOF 持久化集群场景下比纯 RDB 更稳重启后数据丢失窗口更小。cluster-node-timeout 5000是节点失联判定时间单位毫秒。设太小网络抖动就触发故障转移集群频繁切主设太大真挂了半天不切换。5000 是官方给的常见起点内网环境够用。如果机器负载高、GC 停顿明显可以调到 10000 到 15000。3. 从零到集群可用脚本分步实现与执行3.1 环境准备CentOS 7 装 docker 与换源CentOS 7.x 自带的 yum 源里 docker 叫docker版本停在 1.13太老很多新镜像的语法不支持。我一般直接换官方 docker-ce 源。先装依赖工具再添加仓库最后装 docker-ce。# 安装基础依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加 docker-ce 官方仓库 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装 docker-ce 并启动 yum install -y docker-ce systemctl start docker systemctl enable docker # 验证 docker version如果服务器访问官方源慢可以把仓库地址换成国内镜像站这一步属于 CentOS 7 换源的常规操作不影响脚本逻辑。装完后docker version能看到 Client 和 Server 两段输出才算成功。有个常见现象是只显示 Client 不显示 Server那说明 docker 守护进程没起来systemctl status docker看报错多半是 SELinux 或存储驱动的问题。CentOS 7 默认开着 firewalld集群节点之间要通信必须放行 7001-7006 和 17001-17006 这两段端口。我一般直接按范围放行# 放行集群服务端口和总线端口 firewall-cmd --zonepublic --add-port7001-7006/tcp --permanent firewall-cmd --zonepublic --add-port17001-17006/tcp --permanent firewall-cmd --reload提示如果测试环境图省事可以临时systemctl stop firewalld但生产环境别这么干老老实实放行端口。另外有些云主机外层还有安全组记得同步放行否则本机通了外面还是连不上。3.2 拉镜像与启动 6 个容器镜像我选redis:6.2这个版本对集群支持成熟体积也适中。先拉下来docker pull redis:6.2启动容器时每个节点用--net host模式让容器直接共享宿主机网络栈。这样做的好处是省去端口映射的麻烦集群总线端口也能正常通信。如果用默认 bridge 网络加-p映射17001 这类总线端口很容易漏映射导致集群组建失败。--net host下容器里的 Redis 直接监听宿主机端口配置里的cluster-announce-ip填宿主机 IP 就完全对得上。# 启动 6 个 Redis 节点容器 for i in $(seq 0 $((NODE_COUNT - 1))); do PORT$((REDIS_PORT_START i)) NODE_DIR${DATA_DIR}/${PORT} # 先清理同名旧容器保证脚本可重复执行 docker rm -f redis-${PORT} /dev/null 21 docker run -d \ --name redis-${PORT} \ --net host \ --restart always \ -v ${NODE_DIR}/redis.conf:/usr/local/etc/redis/redis.conf \ -v ${NODE_DIR}/data:/data \ redis:6.2 \ redis-server /usr/local/etc/redis/redis.conf donedocker rm -f那行是幂等的关键脚本重复跑不会因为容器名冲突中断。--restart always保证宿主机重启后容器自动拉起配合appendonly yes数据和服务都能恢复。挂载配置时注意容器内路径是/usr/local/etc/redis/redis.conf这是 redis 官方镜像约定的配置目录别写错。启动后可以用docker ps看 6 个容器是不是都 Up 状态有 Exited 的用docker logs redis-7001看日志。3.3 用 redis-cli 组建集群并分配槽位6 个容器起来后它们还是各自独立的实例cluster-enabled yes只是让它们具备集群能力还没组成集群。组建集群用redis-cli --cluster create把 3 个主节点和 3 个从节点按顺序传进去--cluster-replicas 1表示每个主节点配 1 个从节点。# 组装节点列表前 3 个是 master后 3 个是 slave NODES for i in $(seq 0 $((NODE_COUNT - 1))); do PORT$((REDIS_PORT_START i)) NODES${NODES} ${HOST_IP}:${PORT} done # 创建集群--cluster-yes 跳过交互确认 docker run --rm --net host redis:6.2 \ redis-cli -a ${REDIS_PASSWORD} --cluster create \ ${NODES} \ --cluster-replicas 1 \ --cluster-yes这里我用了一个临时容器来跑redis-cli因为宿主机上不一定装了 redis 客户端用镜像里的最省事。--cluster-yes让脚本非交互执行适合放进自动化流程。执行成功后输出里会看到All 16384 slots covered这句话是集群健康的标志说明 16384 个槽位全部分配完毕。如果卡在Waiting for the cluster to join八成是总线端口没通或者cluster-announce-ip填错了。排查顺序是先docker exec -it redis-7001 redis-cli -a 密码 -p 7001 cluster nodes看节点有没有互相发现再检查防火墙 17001-17006 是否放行最后确认配置里的 IP 是不是宿主机真实 IP。集群建好后验证一下状态# 查看集群信息 docker exec -it redis-7001 redis-cli -a ${REDIS_PASSWORD} -p 7001 cluster info # 查看节点角色和槽位分配 docker exec -it redis-7001 redis-cli -a ${REDIS_PASSWORD} -p 7001 cluster nodescluster info里cluster_state:ok表示集群正常cluster_slots_assigned:16384表示槽位分配完整。cluster nodes会列出 6 个节点前面带master的是主带slave的是从每个主节点后面能看到它负责的槽位区间。3.4 写入测试与主从切换验证集群可用不代表主从复制正常得实际写数据验证。用-c参数让 redis-cli 跟随重定向往集群里写 key# 写入测试-c 表示集群模式 docker exec -it redis-7001 redis-cli -a ${REDIS_PASSWORD} -c -p 7001 set testkey hello docker exec -it redis-7001 redis-cli -a ${REDIS_PASSWORD} -c -p 7001 get testkey写入成功会返回OK读取返回hello。如果报MOVED错误说明没加-c客户端不知道要跳到负责该槽位的节点。生产里用 Jedis、Lettuce 这类客户端会自动处理重定向不用手动加。验证主从复制可以往某个主节点写然后去它的从节点读。先通过cluster nodes找到某个 master 的 slave 端口直接连从节点# 假设 7002 是 7001 的从节点 docker exec -it redis-7002 redis-cli -a ${REDIS_PASSWORD} -p 7002 get testkey能读到同样的值说明主从同步正常。想验证故障转移可以手动停掉一个主节点容器等几秒后看它的从节点有没有被提升为主docker stop redis-7001 sleep 10 docker exec -it redis-7002 redis-cli -a ${REDIS_PASSWORD} -p 7002 cluster nodes如果 7002 的角色从slave变成了master说明故障转移成功。测完记得把 7001 重新启动它会以从节点身份重新加入集群。这个验证过程建议在正式上线前跑一遍心里有底。4. 避坑与排查那些让我熬夜的集群故障4.1 集群一直卡在 Waiting for the cluster to join现象是执行cluster create后长时间无响应最后超时失败。原因通常是两类一是集群总线端口 17001-17006 没放行节点之间 gossip 通信被防火墙拦了二是cluster-announce-ip填了127.0.0.1或容器内网 IP其他节点拿到这个地址后连不上。解决办法是先确认防火墙放行了总线端口再把配置里的 IP 改成宿主机局域网真实 IP重建容器。用docker exec进容器ping一下其他节点的 IP能通才说明网络层没问题。4.2 从节点连不上主节点日志报 NOAUTH现象是cluster nodes里从节点状态一直是fail或者connectingdocker logs里能看到NOAUTH Authentication required。原因是配置里只设了requirepass没设masterauth主从同步时从节点拿不到密码。解决很简单两个参数都写上值保持一致然后重建容器。这个坑很隐蔽因为客户端连进来是正常的只有主从复制这条链路受影响。4.3 容器重启后集群状态丢失现象是宿主机重启或容器重建后cluster info显示cluster_state:fail槽位分配不全。原因是cluster-config-file nodes.conf没持久化或者数据目录没挂载出来。nodes.conf记录着集群的拓扑和槽位分配丢了就得重新cluster create。解决办法是确保dir /data指向的目录挂载到宿主机nodes.conf会生成在里面。我一般把整个节点目录都挂出来配置和数据一起持久化。4.4 内存超限导致节点被 OOM 杀掉现象是某个 Redis 容器突然 Exiteddocker logs里看到OOM command not allowed或者直接被系统 kill。原因是没设maxmemoryRedis 把宿主机内存吃满被内核 OOM Killer 干掉。解决办法是在配置里加maxmemory 512mb和maxmemory-policy allkeys-lru给每个节点设上限。6 个节点各 512MB一台 8G 机器留足余量。集群模式下每个节点内存别设太大否则故障转移时数据迁移会很慢。4.5 客户端报 CLUSTERDOWN 但集群看着正常现象是应用连集群报CLUSTERDOWN The cluster is down但cluster info显示cluster_state:ok。原因多半是客户端连的节点和实际集群不在一个网络或者客户端缓存了旧的拓扑。还有一种情况是集群里某个主节点挂了且没有从节点接管部分槽位不可用整体进入 fail。排查时先用cluster info确认cluster_slots_assigned是不是 16384再用cluster nodes看有没有节点处于fail状态。如果是客户端拓扑缓存问题重启客户端连接池即可。5. 把脚本打磨成能反复用的工具几个进阶技巧脚本能跑通只是第一步真正省心的是让它能反复用、能自检、能应对不同环境。我后来在这份脚本上加了三样东西分享给你。第一是执行前的环境自检。在脚本开头加一段检查docker 没装就提示端口被占用就报错避免跑到一半才失败# 检查 docker 是否可用 if ! command -v docker /dev/null 21; then echo docker 未安装请先安装 docker-ce exit 1 fi # 检查起始端口是否被占用 if ss -tlnp | grep -q :${REDIS_PORT_START} ; then echo 端口 ${REDIS_PORT_START} 已被占用请修改 REDIS_PORT_START exit 1 fi第二是集群健康检查函数。部署完自动跑一遍验证把结果打印出来不用手动一条条敲# 集群健康检查 check_cluster() { local state state$(docker exec redis-${REDIS_PORT_START} \ redis-cli -a ${REDIS_PASSWORD} -p ${REDIS_PORT_START} \ cluster info | grep cluster_state | tr -d \r) echo 集群状态: ${state} if echo ${state} | grep -q ok; then echo 集群健康 else echo 集群异常请检查日志 fi }第三是参数外置。把HOST_IP、REDIS_PASSWORD、端口范围这些放到脚本顶部的变量区或者用一个单独的cluster.env文件 source 进来。这样同一份脚本换台机器只改变量不用动逻辑。我一般还会加一个--clean参数传了就先把旧容器和数据目录清掉再重建方便测试环境反复折腾。进阶能力解决什么问题实现要点环境自检跑到一半才报错检查 docker、端口占用健康检查部署完不知道成没成解析 cluster info 输出参数外置换环境要改脚本变量区或 env 文件clean 模式测试环境反复重建参数控制清理逻辑最后说个我自己的习惯每次改完脚本我一定先在一台干净的 CentOS 7 虚拟机上从零跑一遍而不是在已经装过 docker、跑过集群的机器上测。因为老环境里残留的容器、端口、数据目录会掩盖很多问题只有干净环境跑通了才敢拿到别的机器上用。这个习惯帮我省了无数次「在我这明明能跑」的尴尬。希望帮到你。本文还有配套的精品资源点击获取