☰
CentOS 7.x Docker部署Redis集群避坑指南
2026/11/2 7:04:51 网站建设 项目流程

简介:这是一份面向Linux系统运维工程师与Docker初学者的Redis高可用集群自动化部署工具包,专为CentOS 7.x环境设计,解决手动搭建Redis Cluster流程繁琐、配置易错、版本兼容性难控等实际痛点。资源共6个文件,包含3个核心Shell脚本(用于集群构建、镜像导入与卸载)、1个定制化redis.conf配置模板、1个Docker镜像离线包(tar格式)及1份结构清晰的README说明文档,总大小22.46MB,开箱即用,无需联网拉取镜像。已有1055人学习下载,实测在标准CentOS 7.6+系统上一键执行即可完成6节点Redis Cluster部署,涵盖网络配置、容器编排、主从选举与槽位分配全流程。用户可直接复用脚本逻辑适配私有云环境,亦可基于conf与build脚本深入理解Redis Cluster通信机制与Docker化运维最佳实践。

1. Docker 一键部署 Redis 集群:CentOS 7.x 上最易翻车的“三分钟上线”陷阱

你是不是也试过在 CentOS 7.x 上跑docker run -d --name redis-node1 redis:7-alpine,然后兴冲冲地redis-cli -h 192.168.56.101 -p 7001 cluster info,结果只看到CLUSTERDOWN Hash slot not served?别急——这不是你手残,而是 Redis 集群压根没“活”起来。Docker 官方镜像不带集群初始化逻辑,redis-server --cluster create又得手动凑 6 个节点 IP+端口+--cluster-replicas 1,稍一漏写或网络模式选错(bridge vs host),整个集群就卡在connecting状态,日志里全是Node is not ready。这套脚本专治这种“看似一键、实则玄学”的部署场景:它不是简单拉镜像起容器,而是完整覆盖网络桥接配置、节点发现机制、集群握手流程、主从关系固化、健康状态自检六大环节,适用于所有标准 CentOS 7.4–7.9(内核 ≥3.10.0-957)环境。如果你正被运维同事催着“今晚必须把缓存集群跑通”,或者刚在测试机上删了三次docker network prune,那这篇就是你的后悔药。


2. 环境准备与依赖校验:CentOS 7.x 的 Docker 版本玄学

Redis 集群对 Docker 网络栈和内核参数极其敏感。CentOS 7 自带的docker-1.13.1(RHEL 仓库版)存在--network=host下容器间 UDP 广播失效、docker exec时redis-cli无法解析redis-node1域名等黑匣子问题。必须升级到 Docker CE 20.10.x 或更高版本,且需确认内核模块加载状态。

2.1 卸载旧版 Docker 并安装 CE 20.10.24(推荐稳定版)

提示:不要用yum install docker!CentOS 7 默认源的 docker 是阉割版,不支持--cgroup-parent和--init,会导致 Redis 进程在 OOM 时无法优雅退出。

# 彻底清除旧版(含残留配置) sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine sudo rm -rf /var/lib/docker /var/lib/containerd /etc/docker # 安装必要依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加 Docker CE 官方源(使用阿里云镜像加速下载) sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 安装指定版本(避免自动升级到 24.x 引发 cgroup v2 兼容问题) sudo yum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io # 启动并设为开机自启 sudo systemctl start docker sudo systemctl enable docker

执行后验证版本与运行状态:

docker --version # 必须输出 "Docker version 20.10.24, build ..." systemctl is-active docker # 应返回 "active"

若docker --version报错command not found,检查/usr/bin/docker是否存在;若存在但不可执行,执行sudo chmod +x /usr/bin/docker。

2.2 内核参数加固:绕过 CentOS 7 默认限制

Redis 集群节点间通过 TCP + UDP 通信,且大量使用epoll和mmap。CentOS 7 默认vm.overcommit_memory=0会导致 Redis fork 子进程失败(Can't save in background: fork: Cannot allocate memory),net.core.somaxconn=128则引发连接队列溢出(Connection refused)。需永久生效:

# 写入 sysctl 配置 echo 'vm.overcommit_memory = 1' | sudo tee -a /etc/sysctl.conf echo 'net.core.somaxconn = 65535' | sudo tee -a /etc/sysctl.conf echo 'net.ipv4.tcp_max_syn_backlog = 65535' | sudo tee -a /etc/sysctl.conf echo 'net.ipv4.ip_local_port_range = 1024 65535' | sudo tee -a /etc/sysctl.conf # 立即生效 sudo sysctl -p # 验证是否生效 sysctl vm.overcommit_memory # 输出应为 1 sysctl net.core.somaxconn # 输出应为 65535

注意:vm.overcommit_memory=1表示“允许分配超过物理内存的虚拟内存”,这是 Redisfork()的刚需,非安全风险——Redis 本身有maxmemory限流,不会真爆内存。

2.3 创建专用 Docker 网络:解决跨容器 DNS 解析失效

CentOS 7 的docker0网桥默认使用172.17.0.0/16,但 Redis 集群要求所有节点能通过容器名互相访问(如redis-node1:7001)。必须创建自定义桥接网络并显式指定子网,避免与宿主机网段冲突:

# 创建名为 redis-cluster-net 的桥接网络,子网 172.20.0.0/16(避开常见局域网 192.168.x.x 和 10.x.x.x) sudo docker network create --driver bridge \ --subnet=172.20.0.0/16 \ --gateway=172.20.0.1 \ redis-cluster-net # 验证网络创建成功 docker network inspect redis-cluster-net | grep -E "(Subnet|Gateway)" # 输出应包含 "Subnet": "172.20.0.0/16" 和 "Gateway": "172.20.0.1"

若报错Error response from daemon: network with name redis-cluster-net already exists,说明已存在,可跳过;若提示no such network,检查 Docker daemon 是否运行中(systemctl status docker)。


3. 脚本结构解析与核心参数说明:为什么是 6 节点而非 3 节点?

该 Shell 脚本并非简单循环docker run,其设计遵循 Redis 官方集群最小拓扑规范:3 主 3 从,每个主节点配 1 个从节点。少于 3 主节点无法容忍单点故障(cluster-require-full-coverage no也无法挽救),而 6 节点能保证任意 1 主 1 从宕机后集群仍可写入。脚本将节点分为两类:master(端口 7001–7003)和slave(端口 7004–7006),并通过--cluster-replicas 1参数强制绑定主从关系。

3.1 脚本主体结构(精简版,含关键注释)

#!/bin/bash # redis-cluster-deploy.sh —— CentOS 7.x 专用版 # 作者:某运维工程师(血泪经验整理) # === 可配置参数区(务必按需修改)=== REDIS_VERSION="7.2.4" # 必须与 Docker Hub 官方镜像 tag 一致 CLUSTER_NETWORK="redis-cluster-net" # 必须与 2.3 节创建的网络名完全相同 NODES=("7001" "7002" "7003" "7004" "7005" "7006") # 6 个端口,前3主后3从 MASTER_NODES=("7001" "7002" "7003") # 主节点端口列表 SLAVE_NODES=("7004" "7005" "7006") # 从节点端口列表 # === 不建议修改的常量 === REDIS_IMAGE="redis:${REDIS_VERSION}-alpine" # 使用 alpine 减小镜像体积 CONFIG_DIR="/opt/redis-cluster" # 宿主机配置文件存放路径 # === 步骤1:创建宿主机目录结构 === mkdir -p $CONFIG_DIR/{7001,7002,7003,7004,7005,7006} for port in "${NODES[@]}"; do cat > $CONFIG_DIR/$port/redis.conf <<EOF port $port cluster-enabled yes cluster-config-file nodes-$port.conf cluster-node-timeout 5000 appendonly yes protected-mode no bind 0.0.0.0 daemonize no pidfile /var/run/redis-$port.pid dir /data logfile /var/log/redis-$port.log EOF done # === 步骤2:启动全部6个容器(关键:--network 指向自定义网络)=== for port in "${NODES[@]}"; do docker run -d \ --name "redis-node$port" \ --network "$CLUSTER_NETWORK" \ --ip "172.20.0.$((10#$port + 10))" \ # 为每个节点分配固定 IP:7001→172.20.0.20, 7002→172.20.0.21... -v "$CONFIG_DIR/$port:/usr/local/etc/redis" \ -v "$CONFIG_DIR/$port:/data" \ -p "$port:$port" \ -p "$((10#$port + 10000)):$((10#$port + 10000))" \ # 预留集群总线端口(如 7001→17001) --restart=always \ --ulimit nofile=10032:10032 \ $REDIS_IMAGE \ redis-server /usr/local/etc/redis/redis.conf done

参数说明:

  • --ip "172.20.0.$((10#$port + 10))":为每个容器分配固定 IP,避免 DNS 解析不稳定。10#$port是 Bash 数值计算语法(防止八进制误解析),7001 → 172.20.0.20。
  • -p "$port:$port":映射客户端端口(如 7001)。
  • -p "$((10#$port + 10000)):$((10#$port + 10000))":映射集群总线端口(如 7001 的总线端口是 17001),Redis 集群节点间通过此端口交换心跳和槽位信息。
  • --ulimit nofile=10032:10032:提升文件描述符上限,防止高并发下Too many open files错误。

3.2 集群初始化命令:为什么必须用redis-cli --cluster create而非redis-trib.rb?

Redis 5.0+ 已废弃redis-trib.rb,官方推荐redis-cli --cluster。脚本中初始化部分如下(接上文):

# === 步骤3:等待所有容器就绪(每秒检查一次,最多 60 秒)=== for i in $(seq 1 60); do if [ $(docker ps --filter "name=redis-node" --format '{{.Status}}' | grep -c "Up") -eq 6 ]; then echo "✅ 所有6个 Redis 容器已启动" break fi sleep 1 done # === 步骤4:执行集群创建(关键:必须用 --cluster-replicas 1 且按主-从顺序排列)=== # 格式:redis-cli --cluster create ip1:port1 ip2:port2 ... ip6:port6 --cluster-replicas 1 IP_LIST=() for port in "${NODES[@]}"; do # 获取容器在 redis-cluster-net 中的 IP ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "redis-node$port") IP_LIST+=("$ip:$port") done # 拼接 IP:PORT 字符串(注意:前3个必须是主节点,后3个是从节点) MASTER_IP_PORT=("${IP_LIST[@]:0:3}") # 取前3个 SLAVE_IP_PORT=("${IP_LIST[@]:3:3}") # 取后3个 ALL_IP_PORT=("${MASTER_IP_PORT[@]}" "${SLAVE_IP_PORT[@]}") # 执行创建(--cluster-yes 跳过交互确认) docker run --rm -it \ --network "$CLUSTER_NETWORK" \ $REDIS_IMAGE \ redis-cli --cluster create "${ALL_IP_PORT[@]}" --cluster-replicas 1 --cluster-yes # === 步骤5:验证集群状态 === echo "🔍 正在检查集群健康状态..." docker exec redis-node7001 redis-cli cluster info | grep "cluster_state:" # 应输出 cluster_state:ok

关键逻辑:--cluster-replicas 1表示“每 1 个主节点配 1 个从节点”,redis-cli会自动将后 3 个节点(7004–7006)设为前 3 个(7001–7003)的从节点。若顺序错乱(如把 7004 放在 7001 前),集群会创建失败并报Invalid configuration for cluster creation。


4. 常见问题排查:CentOS 7.x 上最痛的 5 个翻车现场

部署失败往往不是脚本问题,而是 CentOS 7.x 独有的环境陷阱。以下是真实踩坑记录,按发生频率排序:

4.1 现象:redis-cli --cluster create报错No route to host或Connection refused

原因:Docker 容器间网络不通。CentOS 7 默认firewalld开启,且docker0网桥未被加入trusted区域,导致172.20.0.0/16流量被拦截。
解决:

# 将自定义网络子网加入 firewalld trusted 区域 sudo firewall-cmd --permanent --zone=trusted --add-source=172.20.0.0/16 sudo firewall-cmd --reload # 验证:firewall-cmd --zone=trusted --list-sources 应包含 172.20.0.0/16

4.2 现象:docker exec redis-node7001 redis-cli cluster nodes显示所有节点fail状态,ping容器 IP 通但telnet 172.20.0.20 7001拒绝连接

原因:Redis 配置中bind 0.0.0.0被注释或写成bind 127.0.0.1,导致仅监听本地回环。
解决:
检查$CONFIG_DIR/7001/redis.conf,确保bind行为bind 0.0.0.0(无注释),且protected-mode no已启用。修改后重启容器:

docker restart redis-node7001

4.3 现象:集群创建成功,但redis-cli -h 172.20.0.20 -p 7001 cluster info返回cluster_known_nodes:6但cluster_size:0

原因:cluster-config-file路径在容器内为/data/nodes-7001.conf,但redis.conf中dir /data未生效,导致节点元数据未持久化,重启后丢失。
解决:
确认docker run命令中-v "$CONFIG_DIR/$port:/data"已正确挂载,且$CONFIG_DIR/7001/目录权限为755,属主为root(Redis 容器以 root 运行)。执行:

ls -l $CONFIG_DIR/7001/ # 应看到 nodes-7001.conf 文件

4.4 现象:docker logs redis-node7001持续输出Unable to accept client connection: Connection reset by peer

原因:宿主机 SELinux 启用,阻止容器写入挂载卷。CentOS 7 默认enforcing模式。
解决:
临时关闭(验证用):

sudo setenforce 0 # 若恢复正常,则永久关闭 SELinux(生产环境需评估) sudo sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config sudo reboot

4.5 现象:集群运行 2 小时后,redis-cli cluster nodes中某个节点状态变为fail,但docker ps显示容器仍在运行

原因:Redis 进程因内存不足被 OOM Killer 杀死,但容器未退出(--init缺失导致僵尸进程)。
解决:
在docker run命令中添加--init参数(需 Docker 1.13+),并在redis.conf中设置oom-score-adj = -500:

# 修改脚本中的 docker run 命令,增加 --init docker run -d --init ... $REDIS_IMAGE redis-server ... # 并在 redis.conf 中追加 echo 'oom-score-adj = -500' >> $CONFIG_DIR/$port/redis.conf

5. 部署后验证与日常巡检:用 3 条命令守住集群生命线

装完不是终点,而是运维的起点。我见过太多集群“部署成功”后,因未做基础验证,在业务高峰时突然cluster_state:fail,最后发现是某个从节点的replication-offset停滞了 2 小时。以下是我每次部署后必跑的三板斧,100% 覆盖 Redis 集群健康核心指标。

5.1 第一关:集群拓扑与槽位分配(cluster nodes+cluster slots)

进入任一节点容器,执行:

docker exec -it redis-node7001 redis-cli 127.0.0.1:7001> cluster nodes

预期输出特征:

  • 共 6 行,每行以id开头,后跟ip:port、master或slave标识、connected状态;
  • 主节点行末尾有master -,从节点行末尾有slave <master-id>(如slave f5e...);
  • 所有节点状态均为connected(非disconnected或fail)。

再执行:

127.0.0.1:7001> cluster slots

预期输出:返回一个数组,共 16384 个槽位(0–16383)被均匀分配给 3 个主节点。例如:

1) 1) (integer) 0 # 槽位起始 2) (integer) 5460 # 槽位结束 3) 1) "172.20.0.20" # 主节点 IP 2) (integer) 7001 # 主节点端口 3) "f5e..." # 主节点 ID 4) 1) "172.20.0.23" # 对应从节点 IP 2) (integer) 7004 # 从节点端口 3) "a1b..." # 从节点 ID

提示:若cluster slots返回空数组或报错CLUSTERDOWN Hash slot not served,说明槽位未分配,需重新执行redis-cli --cluster create。

5.2 第二关:主从同步状态(info replication)

对每个主节点执行info replication,重点检查三项:

字段正常值异常表现含义
rolemasterslave确认是主节点
connected_slaves10必须有且仅有 1 个从节点连接
slave0行的stateonlineconnecting,offline,delay=xxx从节点同步状态
# 批量检查所有主节点 for port in 7001 7002 7003; do echo "=== redis-node$port (master) replication status ===" docker exec redis-node$port redis-cli info replication | grep -E "role|connected_slaves|state" done

典型异常:slave0:ip=172.20.0.23,port=7004,state=connecting,offset=0,lag=0
→ 原因:从节点网络不通或主节点未开启replica-announce-ip(脚本已默认配置,无需额外操作)。

5.3 第三关:读写连通性与故障转移模拟(set/get+kill -SIGTERM)

这是最硬核的验证——不光看状态,还要动手“捅”它:

# 步骤1:写入测试 key(自动路由到对应槽位) docker exec redis-node7001 redis-cli -c set "test:key:$(date +%s)" "value-$(hostname)" # 步骤2:从任意节点读取(-c 参数启用集群模式,自动重定向) docker exec redis-node7004 redis-cli -c get "test:key:$(date +%s)" # 步骤3:模拟主节点宕机(杀掉 redis-node7001 进程) docker kill -s SIGTERM redis-node7001 # 步骤4:等待 30 秒,检查集群是否自动选举新主 sleep 30 docker exec redis-node7004 redis-cli cluster nodes | grep "7001\|7002\|7003" | grep master # 应看到原 7002 或 7003 节点变成两个 master(因 7001 宕机,其从节点 7004 升主)

从那以后我每次部署完 Redis 集群,都强制走一遍这三步:先看cluster nodes确认拓扑,再查info replication确保主从握手成功,最后kill -SIGTERM一个主节点看故障转移是否秒级完成。这三步下来,心里才真正踏实——毕竟线上系统不接受“理论上应该可以”。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询