用Docker搭建Redis Cluster集群:从零实现3主3从与故障转移
2026/9/17 1:40:29 网站建设 项目流程

搞Redis集群这件事,很多人一听就头大——要准备多台机器,要配置各种参数,还要担心节点挂了怎么办。但如果你用Docker来做Redis Cluster集群环境,这事儿就能被拆得明明白白。我用Docker在一台开发机上完整搭过一套Redis Cluster集群环境,3主3从、槽位分配、故障转移全都验证了一遍,整个过程一个多小时就能搞定,每一步命令、每一个预期输出我都给你列出来,比自己截图还直观。

这篇文章的核心思路很简单:利用Docker的bridge网络模拟出6台独立的Redis服务器,在一台物理机上完成整套Redis Cluster集群环境的搭建、验证和故障演练。你不需要额外购买服务器,不需要折腾虚拟机,一台普通的开发电脑就能跑全套。无论你是刚接触Redis的初学者,还是在准备面试时需要动手实操的开发者,这篇文章都能让你少踩很多坑。

1. 确定整体思路:为什么用Docker来搭Redis Cluster

1.1 痛点与方案选型

Redis Cluster最少需要6个节点,因为我计划搭建3主3从的高可用架构。拿6台物理机或者6台虚拟机来练手,成本太高,特别是对个人开发者来说,开6个虚拟机光是内存就吃不消。用Docker就完全不一样了,每个Redis容器只占用几十MB内存,6个节点加起来还不到500MB,普通电脑跑起来毫无压力。

更重要的是,Docker容器之间天然实现了网络隔离和资源隔离,这正好模拟了真实生产环境中多台服务器各司其职的状态。通过自定义bridge网络,容器之间可以用容器名互相通信,这比传统的IP直连方式更贴近生产环境的服务发现机制。我选择Docker来搭集群的另一个原因是可复用性太强了——所有配置都存在conf文件和启动脚本里,下次想搭一套全新的集群,跑一遍脚本就搞定,不需要重新敲命令。

有人可能会问,直接用Linux的host网络模式不是更接近真实环境吗?确实,host模式在性能上和真实环境几乎一致,但有两个明显的坑:第一,host网络模式下所有Redis节点共享宿主机的网络栈,6个节点都要监听6379端口,你不改端口就必然冲突;第二,Windows和Mac上的Docker Desktop根本不支持host网络模式。所以我最终选用bridge网络加端口映射的方式,既能跨平台运行,又能保证节点间相互隔离。

1.2 环境准备与版本选择

动手之前先把环境检查一遍,免得中途卡住。我使用的是Redis 7.x系列镜像,具体选择的是redis:7.2-alpine,这个镜像体积小,整体才40MB左右,拉取速度快,而且7.x版本对Redis Cluster的支持已经非常成熟。

Docker环境方面,Windows和Mac用户建议直接安装Docker Desktop,Linux用户安装Docker Engine即可。安装完成后先跑一下docker version确认环境正常:

docker version

只要能看到Client和Server两部分版本信息,就说明Docker服务正常。如果发现只有Client信息、Server部分报错,大概率是Docker Desktop没启动成功,或者Linux上Docker服务没开。这一步不确认好,后面所有操作都白搭。

另外,因为我用的是Redis 7.2镜像,redis-cli客户端工具也会以7.2版本运行,而创建集群用的redis-cli --cluster create命令是Redis 5.0之后引入的,版本上完全没有问题。如果你机器上装了老版本的redis-cli,建议直接用容器内的命令,比如docker exec -it redis-1 redis-cli,这样能保证客户端版本和集群版本一致。

2. Redis Cluster核心概念与端口规划

2.1 集群是怎样工作的:槽位、主从与选举

在敲命令之前,我建议先花几分钟把Redis Cluster的原理弄清楚。很多人照着教程搭完集群,结果一问槽位是什么、主节点挂了怎么恢复,还是一头雾水,那这个集群搭了等于白搭。

Redis Cluster的数据分片核心是哈希槽(hash slot)。整个集群固定有16384个槽位,每个key通过CRC16算法计算出一个哈希值,再对16384取模,得到的结果就是该key应该存放的槽位。集群创建时,这16384个槽位会被平均分配给所有主节点,比如3个主节点的场景下,每个主节点分到约5461个槽位。

当你向集群写入一个key时,Redis会根据哈希结果计算出槽位,然后判断这个槽位属于哪个主节点。如果恰好属于你连接的节点,就直接写入;如果不属于,节点会返回一个MOVED错误,并告诉你正确的节点地址。这正是Redis Cluster要求客户端必须支持自动重定向的原因,redis-cli -c中的-c参数就是开启集群模式的开关,它会自动处理这种重定向逻辑。

主从复制的原理也不复杂。每个主节点可以配置一个或多个从节点,从节点通过异步复制同步主节点的数据。当主节点宕机时,从节点会根据集群内所有主节点的投票结果完成故障转移,把自己提升为新的主节点。这里有硬性要求:Redis Cluster至少要3个主节点才能正常工作,因为故障转移需要多数节点参与投票,如果有两个主节点,其中一个挂了就无法达成多数共识,集群就瘫痪了。

2.2 端口规划:客户端端口和集群总线端口一个都不能少

这是整个搭建过程中最容易踩坑的地方,我特意单独拿出来讲。Redis Cluster里的每个节点需要占用两个端口:

  • 客户端通信端口(默认6379):用来接收客户端读写请求。
  • 集群总线端口(默认则是客户端端口加10000,即16379):用来进行节点间的通信,包括故障检测、配置同步、槽位迁移等。

很多人在搭建时只映射了6379端口,结果创建集群时报错,或者集群搭完后一重启就出问题,根本原因就是16379端口没通。

我这里的端口映射方案如下:

节点名称容器内端口宿主机客户端端口宿主机集群总线端口
redis-16379637116371
redis-26379637216372
redis-36379637316373
redis-46379637416374
redis-56379637516375
redis-66379637616376

这样设计的好处是,我在宿主机上可以直接用redis-cli -p 6371连接第一个节点,不需要进入容器内部操作,验证和调试都方便。同时,每个节点的端口都不一样,避免和本机其他Redis服务冲突。

另外注意一个细节:集群初始化时,redis-cli --cluster create命令使用的是映射到宿主机的6371到6376端口,但集群内部节点之间进行通信时,会通过容器名称自动解析到容器内的6379和16379端口。Docker的bridge网络在这里起到了虚拟网络隔离的作用,让6个容器就像6台独立服务器一样通过网络互访。

2.3 网络规划:创建自定义bridge网络

默认的bridge网络虽然能用,但我强烈建议为集群创建一个独立的网络,原因有两个:第一,自定义网络支持容器名DNS解析,容器之间可以通过名称通信,而默认bridge网络只能通过IP访问;第二,独立网络隔离性好,不会和宿主机上其他Docker容器互相干扰。

docker network create --driver bridge --subnet=172.20.0.0/16 redis-cluster-net

这里我把子网指定为172.20.0.0/16,主要是为了方便管理,后续如果想给某个容器设置固定IP,可以直接在这个子网内分配。创建完成后可以用docker network ls确认网络已存在,用docker network inspect redis-cluster-net查看详细信息。

3. 实操:从零开始搭建Redis Cluster集群

3.1 编写redis.conf配置文件:每个参数都有讲究

在启动容器之前,需要先准备Redis配置文件。我习惯把所有配置文件按节点编号存放,这样后续排查问题时,一眼就能看出哪个节点的配置有问题。

先在宿主机创建统一的目录结构,我这里以/data/redis-cluster为例:

mkdir -p /data/redis-cluster/redis-{1,2,3,4,5,6}

然后为每个节点写入redis.conf配置。这里先贴出redis-1的配置:

port 6379 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 15000 appendonly yes appendfsync everysec protected-mode no

这6个参数每一个都值得展开说一下:

  • port 6379:容器内Redis监听的端口。由于每个容器网络栈独立,所以6个容器都可以同时监听6379,不会冲突。
  • cluster-enabled yes:核心开关,必须设置为yes,否则Redis实例会以单机模式启动,无法加入集群。
  • cluster-config-file nodes.conf:集群节点配置文件,Redis会自动维护这个文件,记录当前节点的ID、槽位分配信息等。注意这个文件会被Redis自动写入,不需要手动编辑。
  • cluster-node-timeout 15000:节点超时时间,单位是毫秒。如果一个主节点15秒内没有响应集群总线上的心跳消息,从节点就会发起故障转移。这个值是根据实际场景调的,我测试时发现15秒比较均衡,既不会因为网络抖动动不动就切换,也不会让故障恢复等太久。
  • appendonly yesappendfsync everysec:开启AOF持久化,每秒同步一次。这样即使容器重启,数据也不会丢太多。学习环境虽然对数据安全要求不高,但从一开始就用持久化跑,能避免很多“重启后数据没了”的误会。

写配置的时候注意每个节点都要生成一份相同的配置。我通常用一段循环脚本批量生成,省得手动敲6遍:

for port in 1 2 3 4 5 6; do cat > /data/redis-cluster/redis-${port}/redis.conf <<EOF port 6379 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 15000 appendonly yes appendfsync everysec protected-mode no EOF done

protected-mode no这个参数要特别提醒一下:如果不设置的话,Redis默认只允许本机访问,而Docker容器内所有外部访问都算“非本机”,这样宿主机上的redis-cli就没办法连接容器了。当然,这个参数也意味着没有开启密码保护,所以生产环境千万别这么搞,后续我会在避坑部分专门讲安全问题。

3.2 启动6个Redis容器:用循环脚本批量创建

配置文件写好后,就可以启动容器了。这里最推荐的做法是写一个循环脚本,一次启动6个节点,而不是逐个手动docker run,效率高且不易出错。

for port in 1 2 3 4 5 6; do docker run -d \ --name redis-${port} \ --network redis-cluster-net \ -p 637${port}:6379 \ -p 1637${port}:16379 \ -v /data/redis-cluster/redis-${port}/redis.conf:/etc/redis/redis.conf:ro \ -v /data/redis-cluster/redis-${port}/data:/data \ redis:7.2-alpine \ redis-server /etc/redis/redis.conf done

这个脚本里有几个关键点需要说明:

第一,-p参数做了两组端口映射,分别是客户端端口637x和集群总线端口1637x,这两个缺一不可。第二,-v参数把宿主机上的配置文件和数据目录都挂载进容器,配置文件以只读方式挂载(:ro),防止容器内误修改,而数据目录挂在/data下,对应Redis的工作目录,AOF持久化文件会写到这里。第三,命令末尾的redis-server /etc/redis/redis.conf覆盖了镜像默认的启动命令,确保Redis会加载我们的自定义配置。

启动完成后,先用docker ps看看6个容器是否都处于Up状态:

docker ps

正常输出中应该有6个名为redis-1到redis-6的容器都在运行。如果某个容器启动了又立刻退出,用docker logs redis-1查看日志,最常见的报错是配置文件权限问题或者端口被占用。

3.3 执行集群初始化:一条命令搞定3主3从

6个Redis容器都运行起来后,它们目前还是相互独立的单机实例,并没有组成集群。接下来就需要用redis-cli的集群管理命令把6个节点组合成一个集群。

先在宿主机上找到任意一个节点的地址,比如127.0.0.1:6371,然后执行:

redis-cli --cluster create \ 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 \ 127.0.0.1:6374 127.0.0.1:6375 127.0.0.1:6376 \ --cluster-replicas 1

这里--cluster-replicas 1的意思是每个主节点配备1个从节点,6个节点会形成3主3从的结构。命令执行后redis-cli会先做一轮节点健康检查,然后给出槽位分配方案和主从对应关系,最后会提示你是否接受这个方案,需要输入yes确认。

输入yes后,集群会自动完成槽位分配和主从绑定,整个过程大约几秒钟。最终输出中会看到类似“All 16384 slots covered”的提示,这表示所有槽位都已成功分配,集群创建完成。

在这里我特意要强调一个细节:我使用的是宿主机上安装的redis-cli,连接的是映射到宿主机的6371到6376端口。但--cluster create命令真正做的事情,是让这6个节点通过集群总线端口(16371到16376)相互见面并交换集群信息。所以如果你在创建集群时卡住或报错,优先检查的绝对应该是16371到16376这些集群总线端口是否放行了。

4. 集群验证与故障转移演练

4.1 检查节点状态与槽位分配

集群搭建完成之后,不要急着写数据,先用集群命令确认一下状态。下面这些都是必看的:

redis-cli -c -p 6371 cluster info

重点关注cluster_state:ok,如果这里显示的是fail,说明集群健康检查没通过。再查看cluster_slots_assigned:16384,这个数值代表所有16384个槽位都已分配完成,如果少于16384说明分配不完整。

再看节点的具体分布:

redis-cli -c -p 6371 cluster nodes

输出中会列出6个节点,包含每个节点的ID、IP和端口、角色(master或slave)、所属主节点ID、槽位范围等信息。正常情况下应该是3个master节点,每个master节点下面挂着一个slave。例如master节点后面的槽位显示为[0-5460],slave节点后面会显示对应master的节点ID。

我还习惯用redis-cli -p 6371 cluster slots查看更直观的槽位分布表,它会以列表形式展示每个槽位区间的起始位置、结束位置和对应的主从节点地址,对理解集群架构特别有帮助。

4.2 读写测试:观察key是如何被重定向的

集群状态正常后,做一轮读写测试。这里必须使用-c参数连接,也就是集群模式,否则Redis直接返回MOVED错误,新手很容易被吓到,以为集群坏了。

redis-cli -c -p 6371 set name "redis-cluster-test"

运行这条命令时,Redis会根据key“name”计算哈希槽,然后判断槽位属于哪个节点,如果属于当前节点就直接写入;如果不属于,redis-cli会自动跟随重定向并写入到正确的节点。执行完再读取:

redis-cli -c -p 6371 get name

命令输出会显示“redis-cluster-test”,说明数据写入和读取都成功了。为了验证数据确实被分布到了不同节点,可以多写几个不同的key,然后分别通过不同端口的客户端读取。你会发现,有的key存储在6371节点上,有的key存储在6372节点上,这就是哈希槽自动分布的直观体现。

给一个我实际测试时经常用来观察重定向的操作:在集群模式客户端中执行写入时会出现-> Redirected to slot [13441] located at 127.0.0.1:6373之类的提示。这是正常的,不是报错,它只是告诉你这个key的槽位不在当前节点,客户端自动转向了正确的节点。

4.3 高可用验证:杀掉一个主节点会发生什么

Redis Cluster最吸引人的能力就是高可用故障转移,这一步一定要亲手验证一次。我在测试中杀掉了redis-1,对应的主节点前三个槽位区间中的第一个。具体操作:

docker stop redis-1

等大约15到20秒(超过cluster-node-timeout的15000毫秒),再连接任意一个存活的节点查看状态:

redis-cli -c -p 6372 cluster nodes

这时你会看到redis-1对应的节点标记为fail,原本挂在它下面的从节点(比如redis-4)已经被提升为新的主节点,并且接管了redis-1原有的全部槽位。整个过程不需要人工干预,完全由集群自动完成。

接下来再验证故障恢复。把redis-1容器重新启动:

docker start redis-1

等待几秒后再次查看cluster nodes,你会发现redis-1以从节点的身份重新加入集群,并自动挂载到新主节点(redis-4)下面,数据也会从主节点同步过来。到这里就可以确认,这套集群的故障转移和自动恢复机制都已经正常工作。

5. 常见问题与排查实录

5.1 Docker Desktop启动失败怎么办

很多Windows用户在启动Docker Desktop时遇到过类似的报错:virtualization support wasn't detected,或者提示需要启用Hyper-V、WSL2等功能。这个问题本质上是因为Docker Desktop依赖操作系统的虚拟化能力,而虚拟化功能没有完全打开。

排查顺序我建议是:先进BIOS确认CPU虚拟化开关(Intel VT-x或AMD-V)已经开启,然后确认Windows的“虚拟机平台”和“适用于Linux的Windows子系统”两个功能都已启用。完成这些操作后重启电脑,再启动Docker Desktop,绝大多数问题都能解决。如果还不行,检查WSL2的内核更新是否安装。从实际经验来看,80%以上的Docker Desktop启动失败都是这三个原因之一。

5.2 初始化集群时报Connection refused

执行redis-cli --cluster create报错Could not connect to Redis at 127.0.0.1:6371: Connection refused,这个先不用怀疑配置,多半是容器根本没起来。先用docker ps看看容器状态,发现容器处于Exited状态的话,用docker logs redis-1看日志。最常见的两种情况是:Redis配置文件里写了错误参数导致启动失败,或者宿主机端口被占用。

还有一种容易被忽略的情况:容器是起来了,但redis-server进程还没完全启动,你马上执行cluster create就会连不上。这种情况等两三秒再试一次就好。另外我在README式的教程里反复强调要映射两个端口,有些人只映射了6379端口,创建集群时会报节点握手失败,但这种情况报错往往是Timeout而不是Connection refused,注意区分。

5.3 创建集群时提示Not all 16384 slots are covered

这个报错意味着集群中有些槽位没有被任何节点接管,整个集群处于不完整状态,拒绝提供服务。触发这个问题的场景一般有两种:一是集群创建过程中节点间通信失败,槽位分配中断;二是搭建后手动修改了节点配置,导致槽位信息丢失。

最直接的解决办法是全部推倒重来。但要注意,执行redis-cli --cluster create前如果发现节点之前已经加入过集群,需要先对每个节点执行redis-cli --cluster reset,否则会报节点非空错误。实在不行,把挂载的数据目录下的nodes.conf文件删掉,重启容器,让Redis重新生成节点配置,这样最干净。

5.4 容器重启后节点掉线或拒绝加入集群

这是我在实际环境中踩过最大的坑:用docker stopdocker start重启容器没有问题,因为容器的IP没有变;但如果用docker rm删掉容器再重新docker run,容器IP几乎一定会变化,而Redis的nodes.conf文件里记录的是旧IP,新节点拿着旧IP去通信,自然连不上集群。

解决办法有三个可选:最简单的是尽量用docker stop/start,别轻易删容器;其次是创建容器时指定固定IP地址,例如docker run --ip 172.20.0.11,这样无论容器怎么重建,IP都不会变;最后就是每次容器重建后,到数据目录里删除nodes.conf文件,让节点以全新身份加入集群。我在日常实验中,优先用前两种方案,第三种作为兜底。

5.5 安全提醒:端口别裸奔

这套教程里使用的配置把所有端口直接映射到了宿主机的所有网卡上,意味着局域网内任何机器都能访问这些Redis节点。在本地学习环境里问题不大,但如果你是在公司内网或者云服务器上做实验,这就太危险了。Redis默认没有密码保护,一旦被扫描到,数据泄露、被勒索挖矿都是真实发生过的事。

我的建议是,端口映射时明确绑定到本机回环地址,比如-p 127.0.0.1:6371:6379,这样只有本机可以访问容器端口,外部网络完全看不到。如果是生产环境,还需要在Redis配置里加上requirepass设置密码,并在集群节点间配置masterauth。这套安全机制需要单独整理一篇来讲,但至少本地实验时养成绑定127.0.0.1的习惯,是绝对有好处的。

最后再分享一个我自己的习惯:搭建集群这类多节点环境时,我永远把配置目录、数据目录、脚本文件放在同一个固定地方,比如/data/redis-cluster下,每个节点一个子目录。这么做的好处是,实验失败后可以直接删除整个目录重来,日志和配置也都在一眼能看到的地方。如果你也想把这套流程固化成自己的工具箱,强烈建议从一开始就保持这个目录结构,后面做自动化脚本会非常省事。

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

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

立即咨询