CentOS 7 上用 Docker 快速搭建 Redis 集群实战
简介:面向需要在 CentOS 7.x 环境下快速搭建 Redis 集群的运维与开发人员,这份资源提供了一套基于 Docker 的一键部署方案,通过 shell 脚本把镜像导入、容器编排与配置生成等环节串联起来,减少手工逐台配置的重复劳动,适合具备基础 Linux 与容器操作经验、希望快速验证集群可用性的读者。压缩包共 6 个文件,以 sh 脚本为主体,配合 tar 镜像包、conf 配置文件与 md 说明文档,整体约 22.46MB,脚本、镜像与配置各司其职,按说明传入参数即可自动执行部署流程。目前已有 1055 人学习下载,说明该方案在实际环境中具备一定参考价值。读者可获得可直接复用的自动化部署脚本、预置镜像与集群配置样例,以及卸载脚本和说明文档,便于在测试或预发环境中快速拉起集群、验证节点通信与主从关系,并据此理解脚本参数传递与部署逻辑,为后续排错和二次调整提供依据。
1. 在 CentOS 7 上装 Redis 集群,为什么我坚持用 Docker 而不是源码编译
上周帮一个朋友救火,他照着某篇教程在 CentOS 7 上源码编译 Redis 集群,六个节点编译完启动,cluster meet死活握不上手,日志里全是Unable to connect to node。折腾到凌晨两点,最后发现是 firewalld 把 16379 集群总线端口拦了,而他一直以为只要开 6379 就够了。这种坑在 CentOS 7 上太常见了——系统自带的老内核、老 glibc、默认开着的 firewalld、还有那个让人又爱又恨的 SELinux,任何一个没伺候好,集群就起不来。
所以我自己在 CentOS 7.x 上搭 Redis 集群,一律走 Docker + shell 脚本这条路。原因很实在:CentOS 7 的 yum 源里 Redis 版本常年停在 3.2,想上 5.x、6.x 就得自己编译,编译又要处理 gcc 版本、jemalloc、systemd 单元文件一堆事;而 Docker 镜像把运行时依赖全打包好了,宿主机只需要装个 Docker,剩下的端口映射、数据目录、配置文件全交给脚本统一生成。这套方案特别适合两类人:一是手上只有 CentOS 7 老机器、又不想动系统底层的运维;二是需要反复重建测试集群、追求「一条命令拉起六个节点」的开发者。装不好的后果也很直接——节点互相看不见、槽位分配失败、CLUSTERDOWN报错刷屏,而这些问题九成都能在安装阶段规避掉。
2. 装前把环境钉死:CentOS 7.x 的 Docker 与内核准备
2.1 版本对照:别让 Docker 版本成为第一个坑
CentOS 7.x 的生命周期决定了它能装的 Docker 版本有上限。我一般不去追最新的 Docker CE,而是锁在20.10.x这个长期维护线上,它对 CentOS 7 的内核(3.10)兼容性最稳,docker-ce-cli和containerd.io的依赖关系也不会打架。Redis 镜像则选redis:6.2-alpine,alpine 版体积小、启动快,6.2 是最后一个支持 CentOS 7 老内核无痛运行的稳定大版本,7.x 镜像在部分老机器上会因为io_uring相关特性报错。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| 操作系统 | CentOS 7.6 ~ 7.9 | 7.9 最省心,7.6 需确认内核补丁 |
| Docker Engine | 20.10.24 | 别用 23.x,CentOS 7 上 cgroup 驱动易出问题 |
| Redis 镜像 | redis:6.2-alpine | 6.2 稳定,alpine 体积约 30MB |
| 集群规模 | 3 主 3 从 | 6 个节点,端口 7001~7006 |
先确认系统底子。内核版本低于 3.10 的(比如某些精简版 7.2)建议先yum update kernel并重启,否则 Docker 的 overlay2 存储驱动可能起不来。执行下面几条命令把现状摸清:
# 查看内核与系统版本,确认在 3.10 以上、CentOS 7.x uname -r cat /etc/centos-release # 检查是否已有旧版 docker,有就先卸干净 yum list installed | grep -i docker # 确认 firewalld 和 SELinux 状态,后面要针对性处理 systemctl status firewalld getenforce这几条不是走过场。uname -r出来如果是3.10.0-514这种早期版本,overlay2 会有兼容问题;getenforce返回Enforcing的话,后面挂载数据目录时容器可能读不到配置,得提前规划是关掉还是加:z标签。我一般图省事,测试环境直接setenforce 0并把/etc/selinux/config改成disabled,生产环境则老老实实给挂载目录打 SELinux 上下文。
2.2 安装 Docker:用官方源还是阿里云镜像源
CentOS 7 默认的extras源里有个叫docker的老包(1.13 版本),千万别装那个,它和 Docker CE 是两码事。正确做法是加 Docker 官方仓库,国内机器为了速度换成阿里云镜像源。下面这套是我用了很多次的顺序:
# 1. 装依赖工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # 2. 添加 Docker CE 仓库(这里用阿里云镜像加速下载) yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 3. 安装指定版本的 Docker,避免自动升到不兼容的新版 yum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io # 4. 启动并设开机自启 systemctl enable docker && systemctl start docker # 5. 验证 docker version第 3 步锁版本是关键。如果不写版本号直接yum install docker-ce,yum 会拉最新版,而 23.x 之后 Docker 对 cgroup v1 的支持在 CentOS 7 上时有抖动,表现为容器启动后立刻退出、docker logs里报failed to create shim。锁在 20.10.24 能避开这一整类玄学问题。第 5 步docker version要能看到 Client 和 Server 两段都正常输出,如果 Server 段报Cannot connect to the Docker daemon,多半是没启动或者当前用户不在 docker 组,systemctl status docker看具体原因。
装完 Docker 还有一步容易被忽略:配置镜像加速和日志驱动。在/etc/docker/daemon.json里写:
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }registry-mirrors换成你能用的加速地址,拉 redis 镜像会快很多;log-opts限制单个容器日志不超过 100MB、最多留 3 个,不然集群跑几天日志能把磁盘塞满——这是我在一台跑了半年的测试机上真实踩过的坑,/var/lib/docker涨到 40G 才发现。改完systemctl daemon-reload && systemctl restart docker生效。
2.3 网络与端口规划:集群总线端口才是重灾区
Redis 集群每个节点要开两个端口:客户端端口(比如 7001)和集群总线端口(客户端端口 + 10000,即 17001)。很多人只映射了 7001 忘了 17001,结果cluster meet永远超时。用 Docker 跑的时候,-p参数必须两个都映射,而且容器内redis.conf里的cluster-announce-bus-port要和映射出去的一致。
我一般把六个节点规划成 7001~7006,对应总线端口 17001~17006,全部映射到宿主机。firewalld 如果开着,要么直接systemctl stop firewalld(测试环境),要么把这段端口段放行:
# 放行 7001-7006 和 17001-17006 两段端口 firewall-cmd --permanent --add-port=7001-7006/tcp firewall-cmd --permanent --add-port=17001-17006/tcp firewall-cmd --reload这里有个细节:Docker 的端口映射走的是 iptables 的DOCKER链,理论上 firewalld 的规则管不到它,但 CentOS 7 上 firewalld 和 docker 的 iptables 规则经常互相覆盖,重启 docker 后 firewalld 规则可能失效。所以我的习惯是测试环境直接关 firewalld,生产环境用firewall-cmd放行后systemctl restart docker再firewall-cmd --reload,顺序别反。端口规划清楚了,后面脚本里就是照着这个表填参数,不会乱。
3. 用 shell 脚本拉起六个节点:目录、配置、容器一条龙
3.1 脚本整体结构:一个变量区管住所有可变参数
这套脚本我拆成三块:变量定义区、配置生成函数、容器启动循环。变量区把所有会变的东西集中,改集群规模、改端口、改密码只动这一处。核心变量大概长这样:
#!/bin/bash # redis-cluster-deploy.sh # 用途:在 CentOS 7.x 上用 Docker 一键拉起 Redis 6.2 三主三从集群 # ===== 可变参数区 ===== REDIS_IMAGE="redis:6.2-alpine" # 镜像版本,锁死避免漂移 BASE_PORT=7001 # 起始客户端端口 NODE_COUNT=6 # 节点总数,3主3从 DATA_ROOT="/data/redis-cluster" # 宿主机数据根目录 REDIS_PASS="YourStrongPass123" # 集群密码,生产必须改 NET_NAME="redis-cluster-net" # 自定义 bridge 网络名BASE_PORT和NODE_COUNT决定了循环生成 7001~7006 六个节点;DATA_ROOT下每个节点一个子目录,存redis.conf和持久化文件;NET_NAME是自定义网络,让六个容器能通过容器名互相解析——这点很重要,用默认 bridge 网络的话容器间只能用 IP 通信,节点重启 IP 变了集群就乱。密码这块,6.x 集群要求所有节点密码一致,且masterauth和requirepass都要设,脚本里统一用REDIS_PASS注入。
3.2 生成 redis.conf:集群模式的关键五项
每个节点的配置文件不能照搬单机版,集群模式有几个必改项。脚本里用 heredoc 生成,核心片段:
# 为第 i 个节点生成配置,port 为对应客户端端口 generate_conf() { local port=$1 local dir=$2 cat > "${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 127.0.0.1 cluster-announce-port ${port} cluster-announce-bus-port $((port + 10000)) appendonly yes requirepass ${REDIS_PASS} masterauth ${REDIS_PASS} daemonize no EOF }逐项说下为什么这么写。cluster-enabled yes是集群开关,不开这个后面cluster meet直接报错;cluster-config-file nodes.conf是节点自己维护的集群状态文件,必须可写,所以数据目录要挂对;cluster-node-timeout 5000是故障判定时间,5 秒对测试环境够用,生产可以调到 15000 减少误判;cluster-announce-ip这里填127.0.0.1是因为所有节点在同一台宿主机、通过端口映射对外,如果你的节点跨机器部署,这里要填宿主机真实 IP;cluster-announce-bus-port用port + 10000自动算,和前面端口规划对上;appendonly yes开 AOF 持久化,集群环境建议开,不然重启丢数据后槽位状态可能对不上;daemonize no是因为容器里必须前台运行,写 yes 容器会立刻退出。
提示:
protected-mode no在容器内是必须的,因为容器网络对 Redis 来说算「非本地回环」,不关保护模式会拒绝外部连接。但这也意味着密码必须设,requirepass那行别偷懒删掉。
3.3 启动容器:端口映射、数据挂载、网络三件套
配置生成完,循环起容器。每个节点的docker run参数是重点:
# 创建自定义网络(已存在则忽略报错) docker network create ${NET_NAME} 2>/dev/null for i in $(seq 0 $((NODE_COUNT - 1))); do port=$((BASE_PORT + i)) node_dir="${DATA_ROOT}/${port}" mkdir -p "${node_dir}" generate_conf "${port}" "${node_dir}" docker run -d \ --name "redis-${port}" \ --network ${NET_NAME} \ -p ${port}:${port} \ -p $((port + 10000)):$((port + 10000)) \ -v "${node_dir}:/data" \ -v "${node_dir}/redis.conf:/usr/local/etc/redis/redis.conf" \ ${REDIS_IMAGE} \ redis-server /usr/local/etc/redis/redis.conf done-p两行分别映射客户端端口和总线端口,少一行集群就组不起来;-v第一行把数据目录挂进去,nodes.conf和 AOF 文件都落在这里,容器删了数据还在;第二行把配置文件挂进去,改配置不用进容器。--network指定自定义网络,六个容器同网段,后面cluster meet用容器名或 IP 都行。镜像后面跟的redis-server /usr/local/etc/redis/redis.conf是覆盖默认启动命令,让 Redis 读我们生成的配置,而不是镜像自带的默认配置——这一步漏了的话,容器起来是单机模式,cluster-enabled根本没生效。
启动完docker ps应该看到六个容器都是Up状态。如果有容器反复重启,docker logs redis-7001看报错,最常见的是配置文件路径挂载错了导致 Redis 找不到 conf、或者nodes.conf所在目录没写权限。数据目录我一般chmod 777图省事,生产环境按 Redis 容器内 uid 来设更规范。
3.4 创建集群与分配槽位:redis-cli --cluster 的正确用法
六个节点都起来后,用redis-cli --cluster create一次性组集群。这条命令要在某个容器内执行,或者宿主机装了 redis-cli 也行:
# 在第一个容器内执行集群创建,3 主 3 从由 --cluster-replicas 1 自动分配 docker exec -it redis-7001 redis-cli -a "${REDIS_PASS}" --cluster create \ 172.18.0.2:7001 172.18.0.3:7002 172.18.0.4:7003 \ 172.18.0.5:7004 172.18.0.6:7005 172.18.0.7:7006 \ --cluster-replicas 1这里 IP 要换成你环境里六个容器的实际内网 IP,用docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-7001逐个查。--cluster-replicas 1表示每个主节点配一个从节点,6 个节点正好 3 主 3 从。命令执行后会列出主从分配方案,输入yes确认。如果卡在Waiting for the cluster to join,九成是总线端口没通——回到 2.3 检查-p映射和 firewalld。槽位分配是这条命令自动做的,16384 个槽平均分给 3 个主节点,不用手动cluster addslots。
4. 装完必做的验证:从集群状态到读写链路
4.1 三层验证法:状态、槽位、读写
集群建完不能只看docker ps就完事,我一般走三层验证。第一层看集群整体状态:
# 查看集群信息,重点看 cluster_state 和 known_nodes docker exec -it redis-7001 redis-cli -a "${REDIS_PASS}" cluster info # 查看节点列表,确认 3 主 3 从、槽位分配完整 docker exec -it redis-7001 redis-cli -a "${REDIS_PASS}" cluster nodescluster info里cluster_state:ok是底线,如果显示fail说明有槽位没分配或节点失联;cluster_slots_assigned:16384必须是 16384,少一个都算不完整。cluster nodes输出里每个主节点后面跟的槽位区间要能拼成 0-16383 不重叠,从节点状态是slave且connected。
第二层验证槽位路由,故意往不同 key 写数据看是否落到不同节点:
# -c 参数让 redis-cli 跟随重定向,这是集群模式必须加的 docker exec -it redis-7001 redis-cli -c -a "${REDIS_PASS}" set foo bar docker exec -it redis-7001 redis-cli -c -a "${REDIS_PASS}" set hello world docker exec -it redis-7001 redis-cli -c -a "${REDIS_PASS}" get foo-c是关键,不加的话遇到MOVED重定向就报错。如果set foo返回MOVED xxxx 172.18.0.x:700x而没自动跳转,说明-c漏了;如果跳转后连接超时,说明那个节点的端口映射有问题。
第三层做故障转移演练,手动停一个主节点看从节点是否顶上:
# 停掉 7001 主节点,观察集群是否自动选主 docker stop redis-7001 sleep 10 docker exec -it redis-7002 redis-cli -a "${REDIS_PASS}" cluster nodes | grep master正常情况下 10 秒左右原从节点会变成 master,cluster_state仍是 ok。验证完docker start redis-7001把它拉回来,它会以从节点身份重新加入。这一层能过,说明cluster-node-timeout和主从复制链路都是通的。
4.2 一个容易被忽略的收尾习惯
整套流程跑通后,我有个雷打不动的习惯:把docker ps、cluster info、cluster nodes三条输出存一份到/data/redis-cluster/baseline.txt。原因是有次集群跑了两周突然CLUSTERDOWN,排查时根本想不起初始的节点 ID 和槽位分布,只能推倒重建。有了基线文件,对比一下就知道是哪个节点掉了、槽位怎么偏的。另外脚本里那个REDIS_PASS千万别用示例值上线,我见过有人直接复制教程密码,结果公网机器被扫到、数据被清空——集群密码和端口暴露这两件事,装的时候多花五分钟,比出事后再救省五天。希望这套流程帮到你,少走我当年那些弯路。
本文还有配套的精品资源,点击获取