Docker搭建Redis集群完全指南

Docker 搭建 Redis 集群完全指南:从原理到生产落地,踩坑实录

一篇讲透 Docker 环境下 Redis Cluster 的搭建、调优与生产级实战。含完整代码、避坑清单和 Spring Boot 接入方案,收藏即可复用。


为什么需要这篇?

Redis 集群是后端面试的高频考点,也是生产环境的高可用基石。但网上的教程大多停留在「跑通 demo」层面,真正落地时你会遇到:容器重启后集群崩溃、客户端被 MOVED 重定向到 127.0.0.1、从节点无法自动晋升......

这篇文章把 设计思路、完整代码、核心踩坑、生产调优、应用接入 一次讲清楚。所有代码均可直接复制使用。


一、整体架构设计

采用 Redis Cluster 原生方案 + Docker Compose 编排 ,最小高可用规格为 3 主 3 从共 6 节点

  • 3 个主节点分担 16384 个哈希槽,负责读写
  • 3 个从节点分别与主节点一一对应,提供故障转移能力
  • 通过 Docker 自定义桥接网络实现容器间固定 IP 通信
复制代码
复制代码
┌──────────────────────────────────────────┐
    │         Docker Bridge Network             │
    │     (redis-cluster-net, 172.28.0.0/16)    │
    └──────────────────────────────────────────┘
        ↑          ↑          ↑          ↑

┌────────┴───┐ ┌───┴──────┐ ┌─┴────────┐ ┌─┴────────┐

│ redis-7001 │ │redis-7002│ │redis-7003│ │ ... │

│ (主) │ │ (主) │ │ (主) │ │redis-7006│

└────────┬───┘ └───┬──────┘ └─┬────────┘ └─┬────────┘

↑ ↑ ↑ ↑

┌────────┴───┐ ┌───┴──────┐ ┌─┴────────┐ ┌─┴────────┐

│ redis-7004 │ │redis-7005│ │redis-7006│ │ (从) │

│ (从) │ │ (从) │ │ (从) │ │ │

└────────────┘ └──────────┘ └──────────┘ └──────────┘

复制代码
### 节点拓扑规划

| 节点序号 | 容器名 | 服务端口 | 集群总线端口 | 初始角色 | 固定 IP |
|----------|--------|----------|--------------|----------|---------|
| 1 | redis-7001 | 7001 | 17001 | 主节点 | 172.28.0.11 |
| 2 | redis-7002 | 7002 | 17002 | 主节点 | 172.28.0.12 |
| 3 | redis-7003 | 7003 | 17003 | 主节点 | 172.28.0.13 |
| 4 | redis-7004 | 7004 | 17004 | 从节点 | 172.28.0.14 |
| 5 | redis-7005 | 7005 | 17005 | 从节点 | 172.28.0.15 |
| 6 | redis-7006 | 7006 | 17006 | 从节点 | 172.28.0.16 |

> **关键概念**:集群总线端口 = 服务端口 + 10000,是节点间心跳同步、故障转移、数据迁移的专用通道。**必须与服务端口一起映射**,否则 `CLUSTER MEET` 直接失败,这是 90% 新手组网踩的第一个坑。

---

## 二、落地实操:从 0 到集群

### Step 1:准备配置文件

每个节点独立一份 `redis.conf`,以下是生产级完整配置模板(以 7001 节点为例,其余节点仅改端口和 IP):

```conf
# ========== 基础网络 ==========
bind 0.0.0.0
protected-mode no
port 6379
daemonize no

# ========== 集群核心 ==========
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000

# 🔥 关键三件套:显式声明对外访问地址,彻底解决 MOVED 重定向到内网/回环地址的问题
cluster-announce-ip 172.28.0.11        # 容器固定 IP
cluster-announce-port 6379             # 容器内服务端口
cluster-announce-bus-port 16379        # 容器内总线端口

# ========== 安全认证(生产必开)==========
# 所有节点密码必须完全一致
requirepass your_redis_pwd
masterauth your_redis_pwd

# ========== 持久化 ==========
appendonly yes
appendfsync everysec
save 900 1
save 300 10

# ========== 内存管理 ==========
maxmemory 400mb
maxmemory-policy allkeys-lru

亮点解读:

  • cluster-announce-* 三件套配置:显式声明节点对外地址,防止容器重启后 IP 漂移导致集群脑裂,这是最容易被忽略的关键配置
  • requirepass + masterauth 双保险:前者管控客户端访问,后者保障主从同步和总线通信认证,缺一不可
  • maxmemory + allkeys-lru:前置内存上限和淘汰策略,防止大 key 或流量突增导致节点 OOM

Step 2:编写 docker-compose.yml

yaml 复制代码
version: '3.8'

# 自定义静态子网,彻底解决容器重启 IP 漂移问题
networks:
  redis-cluster-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16

services:
  redis-7001:
    image: redis:7.2.5-alpine
    container_name: redis-7001
    restart: always
    ports:
      - "7001:6379"       # 客户端通信端口
      - "17001:16379"     # 集群总线端口(Gossip 协议专用)
    volumes:
      - ./cluster/7001/redis.conf:/etc/redis/redis.conf
      - ./cluster/7001/data:/data    # 全量挂载:含 AOF/RDB + 集群元数据 nodes.conf
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.11
    mem_limit: 512m       # 资源配额,防止单节点 OOM 拖垮宿主机
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

  redis-7002:
    image: redis:7.2.5-alpine
    container_name: redis-7002
    restart: always
    ports:
      - "7002:6379"
      - "17002:16379"
    volumes:
      - ./cluster/7002/redis.conf:/etc/redis/redis.conf
      - ./cluster/7002/data:/data
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.12
    mem_limit: 512m
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

  redis-7003:
    image: redis:7.2.5-alpine
    container_name: redis-7003
    restart: always
    ports:
      - "7003:6379"
      - "17003:16379"
    volumes:
      - ./cluster/7003/redis.conf:/etc/redis/redis.conf
      - ./cluster/7003/data:/data
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.13
    mem_limit: 512m
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

  redis-7004:
    image: redis:7.2.5-alpine
    container_name: redis-7004
    restart: always
    ports:
      - "7004:6379"
      - "17004:16379"
    volumes:
      - ./cluster/7004/redis.conf:/etc/redis/redis.conf
      - ./cluster/7004/data:/data
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.14
    mem_limit: 512m
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

  redis-7005:
    image: redis:7.2.5-alpine
    container_name: redis-7005
    restart: always
    ports:
      - "7005:6379"
      - "17005:16379"
    volumes:
      - ./cluster/7005/redis.conf:/etc/redis/redis.conf
      - ./cluster/7005/data:/data
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.15
    mem_limit: 512m
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

  redis-7006:
    image: redis:7.2.5-alpine
    container_name: redis-7006
    restart: always
    ports:
      - "7006:6379"
      - "17006:16379"
    volumes:
      - ./cluster/7006/redis.conf:/etc/redis/redis.conf
      - ./cluster/7006/data:/data
    networks:
      redis-cluster-net:
        ipv4_address: 172.28.0.16
    mem_limit: 512m
    cpus: "0.5"
    command: redis-server /etc/redis/redis.conf

配置亮点总结:

设计点 解决的问题
自定义静态子网 + 固定 IP 分配 容器重启后 IP 漂移导致集群节点失联
双端口严格映射(服务 + 总线) 覆盖服务访问与 Gossip 通信两条核心链路
配置与数据双目录挂载 持久化 nodes.conf 集群身份元数据,容器重建不丢失节点身份
内存/CPU 资源配额 避免单节点 OOM 挤占宿主机资源
alpine 精简镜像 + always 自愈 兼顾镜像体积与服务可用性

Step 3:启动容器 & 一键组建集群

bash 复制代码
# 启动所有容器
docker-compose up -d

# 等待节点完全启动
sleep 10

# 一键创建集群(--cluster-yes 跳过交互确认,适配 CI/CD)
docker exec -it redis-7001 redis-cli --cluster create \
  172.28.0.11:6379 \
  172.28.0.12:6379 \
  172.28.0.13:6379 \
  172.28.0.14:6379 \
  172.28.0.15:6379 \
  172.28.0.16:6379 \
  --cluster-replicas 1 \
  --cluster-yes

Redis 会自动分配主从关系和 16384 个哈希槽,输出类似:

复制代码
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica ...
[OK] All nodes agree about slots configuration.

致命提醒 :组网命令中 绝对不能用 127.0.0.1,必须填写宿主机真实 IP 或容器固定 IP。否则节点会把集群地址注册为本地回环,跨节点通信直接失败。

Step 4:验证集群状态

bash 复制代码
# 查看集群节点(确认 3 master + 3 slave)
redis-cli -h 127.0.0.1 -p 7001 cluster nodes

# 查看槽位分布
redis-cli -h 127.0.0.1 -p 7001 cluster slots

# 读写验证(-c 开启集群重定向)
redis-cli -c -h 127.0.0.1 -p 7001 -a your_redis_pwd set foo bar

能正常重定向并返回 OK,说明分片和迁移机制工作正常。


三、生产级自动化部署脚本

以下脚本集成了 连通性预检 → 自动组网 → 健康校验 全流程,可直接接入 CI/CD 流水线:

bash 复制代码
#!/bin/bash
# ========== 全局变量 ==========
HOST_IP="宿主机真实IP"
PASSWORD="your_redis_pwd"
START_PORT=7001
NODE_COUNT=6

# ========== 前置校验:全节点连通性检测 ==========
echo "🔍 开始校验所有节点连通性..."
for ((i=0; i<NODE_COUNT; i++))
do
  port=$((START_PORT + i))
  if ! redis-cli -h $HOST_IP -p $port -a $PASSWORD ping > /dev/null 2>&1; then
    echo "❌ 节点$port 连通失败,请检查配置!"
    exit 1
  fi
  echo "✅ 节点$port 连通正常"
done

# ========== 非交互式组建集群 ==========
echo "🚀 开始组建 Redis 集群..."
echo "yes" | redis-cli -a $PASSWORD --cluster create \
  $HOST_IP:7001 $HOST_IP:7002 $HOST_IP:7003 \
  $HOST_IP:7004 $HOST_IP:7005 $HOST_IP:7006 \
  --cluster-replicas 1

# ========== 组网后健康校验 ==========
if [ $? -eq 0 ]; then
  echo "🎉 集群组建成功!集群状态:"
  redis-cli -h $HOST_IP -p 7001 -a $PASSWORD cluster info | grep cluster_state
else
  echo "❌ 集群组建失败,请排查节点日志!"
  exit 1
fi

脚本亮点:

  • 前置连通性校验:提前拦截端口不通、密码错误等基础问题,避免组网中途异常
  • --cluster-yes 非交互模式:无需人工确认,直接接入自动化部署流水线
  • 组网后自动健康校验:快速输出集群状态,降低部署后验证成本

四、Spring Boot 接入 Redis 集群

集群搭好了,应用端怎么连?以下是 Spring Boot + Lettuce 的生产级配置:

yaml 复制代码
# application.yml
spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.100:7001
        - 192.168.1.100:7002
        - 192.168.1.100:7003
        - 192.168.1.100:7004
        - 192.168.1.100:7005
        - 192.168.1.100:7006
    password: your_redis_pwd
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
      cluster:
        refresh:
          adaptive: true       # 自适应拓扑刷新
          period: 30s          # 定时刷新周期

关键配置解读:

  • adaptive: true:开启自适应拓扑刷新,当集群发生扩缩容或故障转移时,客户端自动感知新拓扑,无需重启
  • period: 30s:定时兜底刷新,防止自适应事件丢失
  • 使用 Lettuce 而非 Jedis:Lettuce 原生支持集群拓扑刷新和连接池共享,线程安全,高并发场景性能更优

五、核心踩坑与技术难点

这是全文最值钱的部分------每一个都是真实踩过的坑:

序号 技术难点 问题表现 根因 解决方案
1 集群 IP 寻址异常(最高频) 客户端报 MOVED 127.0.0.1;跨节点迁移失败;心跳失联 Redis 默认注册容器内网 IP/回环地址,外部无法路由 配置 cluster-announce-ip 显式声明对外 IP;组网命令禁用 127.0.0.1
2 集群总线端口未映射 CLUSTER MEET 超时,Gossip 协议不通 只映射了服务端口,遗漏了总线端口 每个节点同时暴露服务端口和总线端口(+10000),防火墙一并放行
3 自动故障转移失效 主节点宕机,从节点无法晋升,集群只读 仅配了 requirepass,未配 masterauth;或各节点密码不一致 所有节点同时配置 requirepass + masterauth,密码全集群统一
4 容器重启后集群崩溃 节点丢失集群身份,槽位数据不可用 nodes.conf 未持久化,容器重建后生成新节点 ID 数据目录完整挂载到宿主机;搭配静态 IP,避免 IP 漂移
5 跨宿主机网络不通 多服务器部署时节点无法建立心跳 Docker bridge 网络为宿主机隔离,容器 IP 无法跨主机路由 方案一:cluster-announce-ip + 宿主机端口映射;方案二:Docker Overlay 跨主机网络
6 单宿主机脑裂风险 宿主机宕机全集群不可用 3 个主节点全在同一台机器,不满足「半数以上存活」选举规则 生产环境必须跨 3 台及以上宿主机部署主节点
7 在线扩缩容阻塞 reshard 迁移槽位时请求超时 大 key 迁移或业务高峰期操作 指定 --cluster-timeout,使用 pipeline 批量迁移,选择低峰期操作
8 监控盲区 集群出问题后才发现,定位困难 缺少监控体系 接入 redis-exporter + Prometheus + Grafana,重点监控 cluster_statecluster_slots_fail

六、生产环境进阶建议

持久化策略

  • 开启 appendonly yes + appendfsync everysec,兼顾性能与安全
  • 可叠加 aof-use-rdb-preamble yes 混合持久化,加速重启恢复
  • 配合 save 900 1 / save 300 10 RDB 快照,双保险

监控告警

接入 redis-exporter + Prometheus + Grafana,核心告警规则:

yaml 复制代码
# 典型告警规则
- alert: RedisClusterDown
  expr: redis_cluster_state != 1
  for: 1m
  labels:
    severity: critical
  annotations:
    summary: "Redis 集群状态异常"

- alert: RedisClusterSlotsFail
  expr: redis_cluster_slots_fail > 0
  for: 30s
  labels:
    severity: warning
  annotations:
    summary: "Redis 集群存在失败槽位"

重点监控指标:cluster_statecluster_slots_failcluster_known_nodes、各节点内存使用率。

动态扩缩容

  • 扩容redis-cli --cluster add-node 加入新节点 → reshard 迁移槽位
  • 缩容 :先 rebalance 迁移槽位 → 再 del-node 移除节点,全程在线不中断
  • K8s 环境:生产建议使用 StatefulSet + Headless Service,让 Redis Cluster 自行管理节点 IP

故障转移验证

手动停止一个主节点容器,观察从节点是否自动晋升为主;原主节点恢复后应自动转为从节点。如果失败,优先排查 masterauth 配置和密码一致性。


七、整体架构全景图


总结

Docker + Compose 搭建 Redis Cluster,核心抓住 五个关键点

  1. 端口双开:服务端口 + 集群总线端口(+10000),缺一不可
  2. IP 固定 :静态子网 + 固定 IP 分配,或 cluster-announce-ip 显式声明,杜绝 IP 漂移
  3. 密码统一requirepass + masterauth 全节点一致,保障故障转移正常
  4. 数据持久化 :完整挂载数据目录,持久化 nodes.conf + AOF/RDB
  5. 客户端适配 :开启集群拓扑刷新(Lettuce adaptive),正确处理 MOVED 重定向

掌握这五点,无论是面试回答还是生产落地,都能稳扎稳打。


公众号"Rain的Java大神之路"

个人博客"www.javadashen.com"

相关推荐
Rain的Java大神实战圈3 天前
短信接口被狂刷怎么处理
场景设计题
Rain的Java大神实战圈4 天前
如何避免订单重复提交
场景设计题
Rain的Java大神实战圈5 天前
MySQL误删数据如何恢复
场景设计题
Rain的Java大神实战圈6 天前
介绍一下分布式事务
场景设计题
Rain的Java大神实战圈7 天前
频繁FullGC如何优化
场景设计题
Rain的Java大神实战圈11 天前
MQ重复消费问题怎么解决
场景设计题
Rain的Java大神实战圈12 天前
线上服务器反应慢如何排查
场景设计题
Rain的Java大神实战圈13 天前
如何保证MQ消息顺序消费
场景设计题
Rain的Java大神实战圈14 天前
线上MQ消息积压了怎么处理
场景设计题