前言
最近在Windows环境下使用Docker部署Redis主从+哨兵集群时,遇到了一个非常棘手的问题:哨兵容器启动时始终报错 Can't resolve instance hostname,即使容器名和服务名完全一致也无法解决。经过反复排查,最终通过静态IP方案彻底解决了DNS解析问题。本文将完整记录整个部署过程和踩坑经历。
环境说明
- 操作系统:Windows 10/11
- Docker Desktop:最新版本
- Redis版本:7.2.15
- 部署架构:一主两从 + 三个哨兵节点
一、项目目录结构
redis-sentinel/
├── docker-compose.yml
├── sentinel1_conf/
│ └── sentinel1.conf
├── sentinel2_conf/
│ └── sentinel2.conf
├── sentinel3_conf/
│ └── sentinel3.conf
├── redis_master_data/
├── redis_slave1_data/
└── redis_slave2_data/
二、完整 docker-compose.yml
version: '3.8'
services:
# ==================== Redis主节点 ====================
redis_master:
image: redis:7.2.15
container_name: redis_master
ports:
- "6379:6379"
volumes:
- ./redis_master_data:/data:rw
command: redis-server --requirepass test123# --appendonly yes
networks:
redis_net:
ipv4_address: 172.28.0.2
restart: always
# ==================== Redis从节点1 ====================
redis_slave1:
image: redis:7.2.15
container_name: redis_slave1
ports:
- "6380:6379"
volumes:
- ./redis_slave1_data:/data:rw
command: redis-server --replicaof 172.28.0.2 6379 --masterauth test123# --appendonly no --dir /tmp
networks:
redis_net:
ipv4_address: 172.28.0.3
depends_on:
- redis_master
restart: always
# ==================== Redis从节点2 ====================
redis_slave2:
image: redis:7.2.15
container_name: redis_slave2
ports:
- "6381:6379"
volumes:
- ./redis_slave2_data:/data:rw
command: redis-server --replicaof 172.28.0.2 6379 --masterauth test123# --appendonly no --dir /tmp
networks:
redis_net:
ipv4_address: 172.28.0.4
depends_on:
- redis_master
restart: always
# ==================== 哨兵节点1 ====================
redis_sentinel1:
image: redis:7.2.15
container_name: redis_sentinel1
ports:
- "26379:26379"
volumes:
- ./sentinel1_conf:/usr/local/etc/redis:rw
command: redis-sentinel /usr/local/etc/redis/sentinel1.conf
networks:
redis_net:
ipv4_address: 172.28.0.10
depends_on:
- redis_master
- redis_slave1
- redis_slave2
restart: always
# ==================== 哨兵节点2 ====================
redis_sentinel2:
image: redis:7.2.15
container_name: redis_sentinel2
ports:
- "26380:26380"
volumes:
- ./sentinel2_conf:/usr/local/etc/redis:rw
command: redis-sentinel /usr/local/etc/redis/sentinel2.conf
networks:
redis_net:
ipv4_address: 172.28.0.11
depends_on:
- redis_master
- redis_slave1
- redis_slave2
restart: always
# ==================== 哨兵节点3 ====================
redis_sentinel3:
image: redis:7.2.15
container_name: redis_sentinel3
ports:
- "26381:26381"
volumes:
- ./sentinel3_conf:/usr/local/etc/redis:rw
command: redis-sentinel /usr/local/etc/redis/sentinel3.conf
networks:
redis_net:
ipv4_address: 172.28.0.12
depends_on:
- redis_master
- redis_slave1
- redis_slave2
restart: always
networks:
redis_net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
三、哨兵配置文件
sentinel1.conf
port 26379
dir /tmp
sentinel monitor mymaster 172.28.0.2 6379 2
sentinel auth-pass mymaster test123#
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
logfile ""
sentinel2.conf
port 26380
dir /tmp
sentinel monitor mymaster 172.28.0.2 6379 2
sentinel auth-pass mymaster test123#
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
logfile ""
sentinel3.conf
port 26381
dir /tmp
sentinel monitor mymaster 172.28.0.2 6379 2
sentinel auth-pass mymaster test123#
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
logfile ""
四、部署步骤
1. 创建目录和配置文件
在项目根目录下创建所需目录:
mkdir redis_master_data redis_slave1_data redis_slave2_data
mkdir sentinel1_conf sentinel2_conf sentinel3_conf
将上述哨兵配置文件分别放入对应目录。
2. 启动所有服务
docker-compose up -d
3. 查看容器状态
docker ps
预期看到6个容器全部处于 Up 状态。
五、验证集群
1. 验证主从复制
查看主节点状态:
docker exec -it redis_master redis-cli -a test123# info replication
预期输出:
role:master
connected_slaves:2
slave0:ip=172.28.0.3,port=6379,state=online
slave1:ip=172.28.0.4,port=6379,state=online
查看从节点状态:
docker exec -it redis_slave1 redis-cli info replication
预期输出:
role:slave
master_host:172.28.0.2
master_port:6379
master_link_status:up
测试数据同步:
docker exec -it redis_master redis-cli -a test123# set test_key "hello"
docker exec -it redis_slave1 redis-cli get test_key
docker exec -it redis_slave2 redis-cli get test_key
两个从节点都应返回 "hello"。
2. 验证哨兵监控
查看哨兵信息:
docker exec -it redis_sentinel1 redis-cli -p 26379 info sentinel
预期输出:
master0:name=mymaster,status=ok,address=172.28.0.2:6379,slaves=2,sentinels=3
查看哨兵识别的主节点:
docker exec -it redis_sentinel1 redis-cli -p 26379 sentinel master mymaster
查看哨兵识别的从节点:
docker exec -it redis_sentinel1 redis-cli -p 26379 sentinel slaves mymaster
3. 验证故障转移
模拟主节点宕机:
docker stop redis_master
等待约30秒,观察哨兵日志:
docker logs redis_sentinel1 -f
查看新主节点:
docker exec -it redis_sentinel1 redis-cli -p 26379 sentinel master mymaster
查看 ip 字段,确认已切换到从节点。
恢复原主节点:
docker start redis_master
docker exec -it redis_master redis-cli -a test123# info replication
预期输出 role:slave,原主节点已降级为从节点。
六、踩坑记录
问题1:Can't resolve instance hostname
现象:哨兵容器启动时报错:
*** FATAL CONFIG FILE ERROR ***
>>> 'sentinel monitor mymaster redis_master 6379 2'
Can't resolve instance hostname.
原因分析:
- Docker Compose 的
depends_on只保证容器启动顺序,不保证服务完全就绪 - 哨兵容器启动时,DNS 解析可能尚未完成
- 容器名与服务名不一致时,DNS 解析会失败
解决过程:
- 初次尝试 :将容器名改为与服务名一致(
redis_master),仍然失败 - 二次尝试:添加启动延迟脚本,等待 DNS 解析成功,仍然不稳定
- 最终方案:使用静态 IP 地址,彻底避免 DNS 依赖
问题2:如何确定静态IP网段
检查现有网络:
docker network ls
docker network inspect bridge
确保选择的网段不与现有网络冲突。默认 bridge 网络使用 172.17.0.0/16,因此选择 172.28.0.0/16 是安全的。
问题3:Windows CMD 命令差异
在 Windows CMD 中,grep 命令不可用,需要使用 findstr 替代:
docker network inspect redis_net | findstr "Name IPv4Address"
七、配置要点总结
| 配置项 | 说明 |
|---|---|
| 静态IP网段 | 172.28.0.0/16,与默认bridge网络隔离 |
| 主节点IP | 172.28.0.2 |
| 从节点IP | 172.28.0.3 ~ 172.28.0.4 |
| 哨兵IP | 172.28.0.10 ~ 172.28.0.12 |
| 哨兵端口 | 26379、26380、26381 |
| 主节点别名 | mymaster(所有哨兵配置中必须一致) |
| 下线票数 | 2(至少2个哨兵同意才判定下线) |
| 主观下线时间 | 30000毫秒(30秒) |
| 故障转移超时 | 180000毫秒(3分钟) |
八、Spring Boot 集成示例
spring:
redis:
sentinel:
master: mymaster
nodes:
- 127.0.0.1:26379
- 127.0.0.1:26380
- 127.0.0.1:26381
password: test123#
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
结语
在 Docker 环境中部署 Redis 哨兵集群时,静态 IP 方案虽然增加了配置复杂度,但能彻底避免 DNS 解析问题,保证集群的稳定启动。如果你也遇到了类似的主机名解析失败问题,不妨尝试本文的静态 IP 方案。