CentOS 7上用Docker一键部署Redis集群:Shell脚本实战

发布时间:2026/10/13 1:29:12
CentOS 7上用Docker一键部署Redis集群:Shell脚本实战 简介面向 CentOS 7.x 运维人员与后端开发者这是一份 Docker 一键部署 Redis 集群的 Shell 脚本工具包。使用者仅需按 README 指引向安装脚本传递参数即可自动完成镜像加载、容器编排与集群初始化省去手工逐台配置的重复劳动适合在测试环境或生产预研中快速拉起集群。资源共 6 个文件压缩包约 22.46 MB包含 3 个 Shell 脚本自动部署、辅助构建、卸载清理、1 个 Redis 配置文件、1 个离线 Docker 镜像 tar 包与 1 份 Markdown 说明文档类型齐全目录结构直观。目前已有 1055 人学习下载。脚本预置了卸载清理逻辑离线镜像包可应对无外网环境的部署需求同时附带参数传递方法和自动执行流程说明经过实际验证能帮助使用者一次性理解从单机到集群的自动化搭建思路快速复制这套 Redis 集群方案。1. 为什么要在 CentOS 7.x 上用 Docker 一键拉起 Redis 集群做 Redis 缓存治理的人多半遇到过这种救火场景线上主节点内存打满服务端连接超时客户端直接报command timed out换个节点还是单点要么手动改配置拉起从库要么在业务侧切分片。这时候最缺的其实是一份可重复执行的部署工具。所谓 docker 一键部署 redis 集群 shell 脚本就是在 CentOS 7.x 服务器上用一条 shell 脚本把 6 个 redis 容器拉起来并完成 cluster 槽位分配把「部署」从手工操作变成幂等脚本。这篇笔记会围绕这套方案讲选型、脚本结构、参数和最容易翻车的几个坑适合维护过 redis 单机但没搭过 cluster 的运维也适合想把手动搭建过程固化成交付物的小团队。Docker 在这里承担的是环境隔离和分发的作用镜像里已经带好 redis 二进制宿主机只需要有 Docker 环境和一块数据盘不需要关心 redis 源码编译、依赖库版本这类琐事。而 shell 脚本负责把「创建目录、生成配置、启动容器、节点握手、分配槽位」这一串动作顺序执行完让集群从零到可用控制在几分钟内。这样一份脚本放到新机器上也能跑比在文档里复制粘贴几十条命令可靠得多。2. 部署前的两个选型定死Cluster 模式与 host 网络下的容器参数2.1 为什么选 Redis Cluster 而不是主从加哨兵在动手写脚本之前先要把「集群」两个字定义清楚。常见方案有两类主从复制加哨兵Sentinel以及 Redis Cluster。主从加哨兵解决的是高可用问题一个 master 挂掉Sentinel 自动把 slave 提升为 master业务侧通过 Sentinel 获取当前主节点地址。但数据总量仍受单个分片的内存限制当数据量到几十 G、QPS 到几万时单分片扛不住只能靠客户端分片或者引入中间件改动面很大。Redis Cluster 不一样。它用 16384 个 hash slot 把数据横向切到多个主节点上每个主节点负责一段槽位区间每个主节点又带若干从节点做冗余。client 通过 cluster 协议重定向到正确的节点业务侧不需要自己维护分片逻辑。等 redis 在业务里承担起中间件角色、缓存治理进入深水区之后Cluster 的横向扩展价值会越来越明显这也是它成为容器化部署默认选择的原因。从脚本自动化的角度对比Cluster 也比哨兵好封装。哨兵要额外维护一套 sentinel.conf每个 redis 实例还要配对监控关系脚本里需要处理的状态机更复杂。而 Cluster 的核心动作就两个把节点聚成一个集群、分配槽位。这两个动作 redis 官方客户端已经内置了--cluster create能力shell 脚本只需要把端点地址拼对、把等待逻辑写好就成。所以标题里说的 redis 集群我默认按 Redis Cluster 设计下面的脚本也按这个方案来。2.2 CentOS 7.x 的 Docker 环境底线yum 源装不到的那个版本CentOS 7.x 默认源里带着一个 docker 包版本号停留在 1.13。这个版本在 cluster 场景里会反复出问题容器重启时 iptables 规则偶发残留、端口映射在桥接模式下经常要手动清理、对 overlay2 存储驱动的支持也不完整。脚本要跑得稳第一步应该是检查宿主机 Docker 版本低于 20.10 就换装 docker-ce。# 检查当前 docker 版本和运行状态 docker version --format {{.Server.Version}} || echo docker not ready # 安装 docker-ce 20.10 的常见做法CentOS 7.x sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce-20.10.* docker-ce-cli-20.10.* sudo systemctl enable --now docker这段命令先确认现有版本再装 yum-config-manager 工具并添加 docker 官方仓库最后安装指定小版本的 docker-ce 并用 systemd 托管。装固定版本而不是直接yum install docker-ce是为了避免大版本升级把运行时行为改掉CentOS 7 的内核停留在 3.1020.10.x 是兼容性最稳的一代。还有一点要注意如果系统盘是 XFS 且当初分区没开ftype1overlay2 会在 docker 启动时报警日志里有failed to mount overlay字样这种情况只能换存储驱动或者重做数据盘脚本里救不回来。2.3 host 网络模式下的 redis.conf为什么不用 bridgeDocker 默认的 bridge 网络会给每个容器分配私有 IP容器对外服务需要做端口映射。Redis Cluster 节点之间是通过 gossip 协议互相发现的除了数据端口还有一个 cluster bus 端口默认是数据端口加 10000。在 bridge 模式下如果只映射了 7000-7005 而漏掉 17000-17005节点之间会一直说不上话--cluster create卡死在 Waiting for the cluster to join。这个坑太常见所以我直接选 host 网络模式。host 模式下容器不隔离网络栈redis 直接监听宿主机端口cluster 节点间广播的就是宿主机 IP不需要处理 NAT 和 announce 地址脚本逻辑能省掉一大截。代价是端口被容器占用后宿主机上不能跑同名服务但这在一台专职缓存服务器上完全可接受。host 模式下 redis.conf 里 bind 必须写成0.0.0.0否则容器内只会在回环地址监听外部连不上。配置项示例值说明port7000每个节点一个端口从 BASE_PORT 递增bind0.0.0.0host 模式下必须监听所有地址cluster-enabledyes开启 cluster 模式cluster-config-filenodes.conf保存节点 ID 和集群拓扑要落到持久化目录cluster-node-timeout15000超过该毫秒数未响应则判定节点故障appendonlyyes开启 AOF容器重建后数据可恢复requirepass与 masterauth 相同客户端认证密码masterauth与 requirepass 相同主从复制和 cluster 内部通信要用的密码maxmemory1gb每个节点内存上限防止单节点打满宿主机表格里 requirepass 和 masterauth 必须一致否则 cluster 握手能成功但主从复制会一直报MASTER aborted replication。cluster-node-timeout 也不是越大越好设成 30000 会让故障切换慢半拍业务侧已经把超时耗尽设成 3000 又可能因为一次 GC 停顿就误判节点失效。生产上我一般先给 15000观察一个完整故障周期再微调。提示host 网络模式下不要再加-p 7000:7000这类端口映射参数Docker 会直接报错冲突。3. 把脚本拆成三块模板生成、容器启动、cluster 握手3.1 文件布局与变量封装一份能反复执行的部署脚本我不会把所有变量都硬编码在 shell 里。常见做法是拆分出一个环境变量文件部署脚本只负责逻辑。这样换机器、换端口段、换密码时不用动脚本正文也方便交给 CI 或配置管理工具去填充。# nodes.env按需修改变量 REDIS_IMAGEredis:7.0 NODE_PREFIXredis BASE_PORT7000 NODE_COUNT6 REPLICAS1 REDIS_PASSWORDChangeMe2024 BASE_DATA_DIR/data/redisNODE_COUNT6是 cluster 的最小实践规模3 主 3 从REPLICAS1表示每个主节点带 1 个副本。BASE_PORT7000避免和宿主机常见服务端口冲突后面的脚本里会从 7000 递增到 7005。REDIS_IMAGEredis:7.0只是个示例标签你要用 6.2、7.2 都可以但尽量不要用 latest镜像缓存和版本追溯都会难受。BASE_DATA_DIR/data/redis建议放到独立数据盘别搁系统盘redis 的 AOF 和 RDB 写入量不算小。3.2 用 heredoc 生成 redis.conf模板里的 8 个必调参数有了环境变量下一步就是生成每个节点的配置文件。这一步我用循环加 heredoc 完成好处是模板只写一遍变量替换交给 shell 自己处理避免手动复制六份文件改坏一处。for i in $(seq 1 $NODE_COUNT); do port$((BASE_PORT i - 1)) node_dir$BASE_DATA_DIR/$port mkdir -p $node_dir/conf $node_dir/data cat $node_dir/conf/redis.conf EOF port ${port} bind 0.0.0.0 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 15000 appendonly yes dir /data requirepass ${REDIS_PASSWORD} masterauth ${REDIS_PASSWORD} maxmemory 1gb EOF done这段脚本先算出每个节点的实际端口比如 i1 时 port7000对应目录/data/redis/7000。conf目录放配置文件data目录放 AOF 文件和 nodes.conf。配置里dir /data要和后面 docker run 的-v挂载对应上redis 会把nodes.conf写到这个目录里容器销毁后拓扑信息还在。maxmemory 1gb是单节点上限如果宿主机内存紧张可以把脚本里的 1gb 改成 512mb但至少要保证主从两个节点加系统开销在物理内存内别指望去用 swapredis 在 swap 上性能会断崖式下跌。3.3 启动 6 个容器host 网络、固定容器名、自动重启配置生成完毕后进入容器启动阶段。每个节点一个docker run参数保持完全对称只替换端口和挂载目录。容器名我直接用redis-7000这种格式后面对接 cluster、查日志、清理都能一眼认出来。for i in $(seq 1 $NODE_COUNT); do port$((BASE_PORT i - 1)) node_dir$BASE_DATA_DIR/$port docker run -d \ --name ${NODE_PREFIX}-${port} \ --nethost \ --restartunless-stopped \ -v $node_dir/conf/redis.conf:/usr/local/etc/redis/redis.conf:ro \ -v $node_dir/data:/data \ ${REDIS_IMAGE} \ redis-server /usr/local/etc/redis/redis.conf done--restartunless-stopped保证宿主机重启后容器能跟着起来这是生产环境的基本要求。-v挂载把宿主机上的配置文件只读挂进容器数据目录读写挂载。配置文件用只读是为了防止容器内误改动真要调整参数就改宿主机文件然后重启容器这样每次变更都能追溯到文件层面。这里有个容易忽略的权限问题官方 redis 镜像默认以 redis 用户uid 999运行宿主机上/data/redis/7000如果属主是 root容器启动会被 permission denied 直接退出。解决办法是创建目录后执行一次chown -R 999:999 $BASE_DATA_DIR或者干脆chmod -R 777前者更稳妥。3.4 等待就绪再握手用容器内 redis-cli 建集群六台容器都起来不代表集群可用节点之间要互相发现、分配槽位。这里有个等待逻辑启动容器后直接执行 create很可能某些节点还没完成初始化报Node is not empty或者连接被拒。常见做法是循环 ping 每个节点全部通了再进入下一步。for i in $(seq 1 $NODE_COUNT); do port$((BASE_PORT i - 1)) for t in $(seq 1 30); do docker exec ${NODE_PREFIX}-${port} redis-cli -a $REDIS_PASSWORD ping /dev/null 21 break sleep 1 done done # 取宿主机第一块网卡的 IP不要用 127.0.0.1 IP$(ip route get 1.1.1.1 | awk {print $7; exit}) # 用容器内的 redis-cli 执行 cluster create docker exec ${NODE_PREFIX}-${BASE_PORT} redis-cli -a $REDIS_PASSWORD --cluster create \ $(for i in $(seq 1 $NODE_COUNT); do echo -n ${IP}:$((BASE_PORT i - 1)); done) \ --cluster-replicas ${REPLICAS} \ --cluster-yes这段脚本的关键在两点。第一docker exec redis-cli用的是容器内的客户端而不是宿主机里的 redis-cli避免了 CentOS 7 自带的旧版本 redis-cli 不认识--cluster子命令的坑。第二端点地址用宿主机 IP 而不是127.0.0.1host 网络下集群广播用的是这个 IP其他节点才能找到彼此用回环地址建出来的集群本地命令能通跨节点复制直接失败。--cluster-replicas 1由REPLICAS变量控制3 主 3 从就传 1要是 NODE_COUNT 改成 6 而 replicas 传 2create 会因为副本数不够报错。3.5 部署入口set -euo pipefail 和日志落盘最后把上面几块拼进一个入口脚本头部加上 shell 的严格模式。这一步看着不起眼但能省掉大量「脚本跑一半报错还继续执行」的玄学问题。#!/usr/bin/env bash set -euo pipefail source ./nodes.env # 检查 docker 是否就绪 docker version --format {{.Server.Version}} /dev/null 21 || { echo docker not ready; exit 1; } # 生成配置、启动容器、等待节点就绪、建立集群 # 具体实现见 3.2-3.4 节 bash deploy-steps.sh执行方式也建议统一bash deploy.sh 21 | tee deploy.log。tee把输出同时打到屏幕和日志文件出问题时直接翻deploy.log不用靠人肉回忆刚才终端滚过什么。脚本里每一步最好都追加一行日志比如echo [INFO] node 7001 config generated这样部署失败时能看到卡在哪一步是配置生成、容器启动还是集群握手。提示脚本设计成可重复执行比一次性脚本更实用。跑挂了直接重新运行中间生成的文件被覆盖不会残留脏配置。要做到这一点脚本里尽量不要用追加写模式配置文件一律重定向覆盖。4. 部署脚本最容易翻车的 5 个坑端口映射、IP 漂移与内核参数4.1 握手卡在 Waiting for the cluster to join现象redis-cli --cluster create执行后一直停在Waiting for the cluster to join集群节点日志反复报The cluster is down now。原因最常见是 cluster bus 端口不通。客户端连 7000-7005但 cluster 节点间通信还要用 17000-17005数据端口加 10000这个段。bridage 模式下往往只映射了数据端口漏掉 bus 端口host 模式虽然不需要 NAT但宿主机防火墙如果只放行了 7000-7005bus 端口的包照样被丢掉。解决把两段端口都加进 firewalld 放行名单bus 端口段规则直接按端口加 10000 写死firewall-cmd --permanent --zonepublic --add-port7000-7005/tcp firewall-cmd --permanent --zonepublic --add-port17000-17005/tcp firewall-cmd --reload这里有个细节add-port支持区间写法不用一个个端口加。如果宿主机的安全组或者云平台防火墙也有入站规则同样要放行这两段光开本机防火墙不够。4.2 容器重启后其他节点把它标记为 failed现象宿主机重启或者 Docker 重启后cluster info显示cluster_state:fail部分节点状态停留在 failed槽位分配不回来。原因bridge 模式下容器重启会换 IP旧 IP 残留在其他节点的 nodes.conf 里如果 cluster-config-file 还写在容器可写层而不是挂载卷里容器删了重建后节点 ID 和拓扑信息一起丢了。host 模式虽然规避了 IP 漂移但 nodes.conf 必须要落到持久化目录否则容器重建等于新节点加入集群老节点会认为它是陌生人。解决确认 redis.conf 里的dir /data和 docker run 的-v $node_dir/data:/data配对nodes.conf 会由 redis 自动写到/data下。排查时可以进容器确认文件确实在磁盘上docker exec redis-7000 ls -l /data/nodes.conf docker exec redis-7000 redis-cli -a $REDIS_PASSWORD cluster nodescluster nodes输出里每个节点后面有 connected 或 failed 状态如果发现节点 ID 变了说明旧拓扑记录被新节点顶掉了最干净的做法是清空数据目录重新跑一遍脚本不用手工去改 nodes.conf。4.3 vm.overcommit_memory 为 0 导致 fork 失败或 OOM现象容器正常跑着大流量写入时 redis 日志出现Background save may fail under memory pressure严重时进程直接退出集群进入降级状态。原因Linux 默认vm.overcommit_memory0redis 的 bgsave 要 fork 子进程fork 时内核按 commit charge 估算内存在内存接近上限时可能判定 fork 失败。cluster 模式下任何一个主节点挂掉涉及的槽位区间就不可用影响会被放大。解决在部署脚本里加一段前置检查把这几个内核参数固定下来sysctl -w vm.overcommit_memory1 echo vm.overcommit_memory1 /etc/sysctl.d/99-redis.conf sysctl -w vm.max_map_count262144 echo vm.max_map_count262144 /etc/sysctl.d/99-redis.conf echo never /sys/kernel/mm/transparent_hugepage/enabledvm.overcommit_memory1让内核总是放行内存申请配合 redis 自身的 maxmemory 限制够安全。vm.max_map_count影响 fork 时内存映射区数量集群节点多了以后这个参数很关键。transparent_hugepage 关闭是为了避免内存页分配变慢redis 官方文档里明确建议改成 never。这些参数重启会丢所以写入/etc/sysctl.d做持久化THP 那条可以追加到 rc.local 或 systemd unit 里。4.4 挂载目录权限与 SELinux 导致容器启动即退出现象docker run后容器状态是 Exiteddocker logs显示Cant chdir to /data: Permission denied或者 config 文件读取被拒。原因两层问题叠加。第一层是目录属主不对官方 redis 镜像以 redis 用户uid 999运行宿主机数据目录属主是 root。第二层是 CentOS 7 的 SELinux 默认 enforcing宿主机新建目录没有svirt_sandbox_file_t类型标签容器进程即使有权限也会被 SELinux 拦下来。解决创建目录后做 chown同时给目录打上容器可访问的 SELinux 上下文chown -R 999:999 $BASE_DATA_DIR chcon -R -t svirt_sandbox_file_t $BASE_DATA_DIR如果用的是 docker run 的-v挂载也可以在挂载参数后面加:z后缀让 Docker 自动打标签。但不建议直接把setenforce 0当成解决方案那样整台机器的 SELinux 防护就废了为 redis 开个小口子是更克制的做法。这里还有个血泪经验docker logs 显示的报错不一定就是根因permission denied 要同时看属主、SELinux 审计日志grep redis /var/log/audit/audit.log和容器内 uid 三处。4.5 用错宿主机的 redis-cli 导致语法错误现象在宿主机上执行redis-cli --cluster create直接报unknown subcommand或syntax error以为是命令写错。原因CentOS 7 自带的 redis 包里的 redis-cli 版本通常是 3.x 或 4.x而--cluster子命令到 redis 5.0 才成熟。宿主机 redis-cli 和容器内的 redis-server 版本不匹配协议解析都对不上。解决脚本里统一用docker exec 容器名 redis-cli执行所有 cluster 命令宿主机上不装也不依赖 redis 客户端。这样客户端版本永远跟服务端一致换机器部署也不会因为系统历史遗留翻车。要检查集群状态也用同样方式docker exec redis-7000 redis-cli -a $REDIS_PASSWORD cluster info5. 集群启动后的 3 个验证动作check、故障切换演练与扩容前的准备5.1 用 --cluster check 和 info cluster 确认状态集群建立后不要急着把脚本收工先做一次完整巡检。用容器内的 redis-cli 跑--cluster check这条命令会把 16384 个 slot 的覆盖情况、每个主节点的从节点数量、有没有孤立节点全部扫一遍。docker exec redis-7000 redis-cli -a $REDIS_PASSWORD --cluster check 127.0.0.1:7000输出末尾看到[OK] All 16384 slots covered才算通过。然后看cluster info的cluster_state:ok和cluster_known_nodes:6。再往上一层用redis-cli --cluster info能看到每个分片的主从对应关系。这个动作要养成习惯因为--cluster create成功不代表拓扑健康有时槽位分配完但某个从节点没挂上check 会直接给你报出来。用 redis desktop manager 这类图形工具连接时注意cluster 模式要一个端口一个端口建连接别指望输入一个地址就把整个集群都显示出来连接名按端口区分就行。5.2 故障迁移演练kill 一个主节点看从节点晋升验证集群高可用最直接的办法是模拟故障。先确认当前哪个是主节点docker exec redis-7001 redis-cli -a $REDIS_PASSWORD cluster nodes | grep master挑一个主节点直接docker stop redis-7000。等 15 秒左右cluster-node-timeout 设的 15000 毫秒再看cluster nodes会发现原主节点状态变成 fail它的从节点晋升为 master整个集群cluster_state仍然 ok。验证完把停掉的容器重新启动它会作为从节点重新挂回集群槽位自动做 rebalance。这条演练建议每次改完部署脚本都跑一遍特别是调过cluster-node-timeout之后要确认业务侧客户端的超时设置能接受这个切换窗口。5.3 我的一个脚本习惯把部署改成可回收的幂等操作第一次写这套部署脚本时我只做了创建没做清理。后来集群调坏了想重建只能一个个docker rm容器再手动删数据目录非常狼狈。后来我养成一个习惯脚本里补一个stop_deploy函数按容器名前缀一次性清理让整个生命周期闭环。docker ps -a --filter name${NODE_PREFIX}-${BASE_PORT} --format {{.Names}} | xargs -r docker rm -f rm -rf $BASE_DATA_DIR/*有了这段每次改完参数都能干净重建不会残留 nodes.conf 里的旧拓扑。这个习惯后来帮我省了不少事有一次生产要调整节点内存上限我就是改完 node.env 后跑清理再重新部署整个过程不到五分钟全程输出在 deploy.log 里可追溯。shell 脚本做运维本来就是「把不常做的事固化成不会忘的流程」删干净和搭起来同样重要。希望这些细节能帮到你遇到部署翻车时按「先看日志、再查 bus 端口、最后确认内核参数」的顺序排查大部分问题都能定位到具体一行。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询