Redis集群新选择:Codis 3.2保姆级Docker部署教程(附避坑指南)
如果你正在为单点Redis的性能瓶颈或高可用架构的复杂性而头疼,那么将目光投向Codis,或许能打开一扇新的大门。Codis,这个由豌豆荚团队开源的分布式Redis解决方案,通过引入一个智能的代理层,巧妙地解决了数据分片、故障转移和集群管理等一系列难题。对于需要处理海量数据、支撑高并发访问,但又希望保持与原生Redis客户端高度兼容的团队来说,Codis提供了一个优雅的折中方案。尤其在容器化技术普及的今天,通过Docker部署Codis集群,能将环境依赖、版本管理和服务编排的复杂度降到最低,让开发者更专注于业务逻辑本身。本文将带你从零开始,手把手完成一个生产可用的Codis 3.2集群的Docker化部署,并分享那些官方文档里可能没细说,但实践中一定会遇到的“坑”与解决方案。
1. 部署前的核心概念与架构解析
在动手敲命令之前,花几分钟理解Codis的核心组件和工作原理至关重要。这能帮助你在后续配置和排错时,清晰地知道每个参数的意义和每个组件的作用。
Codis的架构可以看作是一个“代理-存储-协调”的三层模型。最上层是Codis-Proxy,它对外提供与单机Redis完全兼容的协议接口。你的应用程序连接的是Proxy,而不是后端的Redis实例。Proxy内部维护着一个数据分片(Slot)的路由表,根据Key的哈希值决定将请求转发到哪个后端Group。
中间层是Codis-Server,它本质上是经过定制开发的Redis实例(基于Redis 3.2),负责实际的数据存储。多个Codis-Server实例被组织成一个Group,每个Group内部采用一主多从的经典主从复制结构,实现数据冗余和读写分离。
最底层是协调与元数据存储层。在Codis 3.2中,官方推荐使用ZooKeeper或Etcd。这个组件不存储业务数据,只保存集群的元信息,例如:所有Proxy的地址列表、Slot到Group的映射关系、各个Group的主从节点状态等。正是通过这个中心化的元数据存储,Proxy才能动态感知集群拓扑的变化。
此外,还有两个管理组件:
- Codis-Dashboard:集群的大脑,负责执行诸如数据迁移、主从切换、节点上下线等管理指令。它通过读取/写入元数据存储来驱动集群状态变更。
- Codis-FE (Frontend):一个基于Web的图形化管理界面,底层通过调用Dashboard的API来操作集群,为管理员提供了直观的可视化控制台。
注意:很多初次接触Codis的开发者会混淆Proxy和Server的角色。记住一个简单的类比:Proxy是“前台接待”,负责接活和派活;Server是“后台工人”,负责干活。你的应用只需要和“前台”打交道。
理解了架构,我们来看一个请求的生命周期:
- 客户端向任意一个Codis-Proxy发起
SET user:1001 “Alice”命令。 - Proxy计算Key
user:1001的哈希值,并映射到1024个Slot中的一个(比如Slot 500)。 - Proxy查询内存中的路由表(该表定期从ZooKeeper同步),发现Slot 500当前由Group-2负责。
- Proxy将写命令转发给Group-2的Master Codis-Server。
- Master处理命令并返回结果给Proxy,Proxy再返回给客户端。
- 同时,Master会异步地将数据变更复制给同Group内的Slave节点。
整个过程对客户端透明,就像在操作一个超大内存的单一Redis实例。
2. 环境准备与Docker网络规划
我们选择Docker进行部署,这能保证环境的一致性,并简化依赖管理。首先,确保你的宿主机上已经安装了Docker和Docker Compose。我们将使用Docker Compose来编排所有服务。
关键决策:Docker网络模式的选择 这是第一个容易踩坑的地方。Codis集群内部组件之间需要频繁通信(Proxy连接Server,Dashboard连接ZooKeeper等)。为了简化网络配置并提高性能,我们创建一个自定义的Docker桥接网络。
# 创建一个名为codis-net的自定义网络
docker network create --subnet=172.19.0.0/16 codis-net
使用自定义网络有两大好处:
- 服务发现:容器之间可以通过容器名称直接互相访问,无需关心IP地址变化。
- 隔离性:将Codis集群与其他服务隔离开,更安全。
接下来,我们需要规划各个服务的容器名称、端口映射以及资源限制。以下是一个建议的配置表格,适用于开发测试环境:
| 服务组件 | 容器名称 | 自定义网络内端口 | 宿主机映射端口 | 数据卷映射 | 备注 |
|---|---|---|---|---|---|
| ZooKeeper | zk1 | 2181 | 32181 | ./zk_data:/data | 元数据存储,单节点仅用于演示,生产环境需集群。 |
| Codis-Dashboard | codis-dashboard | 18080 | 无 | ./config/dashboard.toml:/codis/config/dashboard.toml | 管理后台,通常无需对外暴露。 |
| Codis-FE | codis-fe | 9090 | 39090 | 无 | Web管理界面。 |
| Codis-Proxy (x2) | codis-proxy-1, -2 | 19000(admin), 11080(proxy) | 29000/21080, 29001/21081 | ./config/proxy.toml:/codis/config/proxy.toml | Proxy需要两个端口,admin用于管理,proxy用于业务。 |
| Codis-Server Group1 (Master) | codis-server-g1-m1 | 6379 | 36379 | ./data/g1-m1:/data | 业务数据存储。 |
| Codis-Server Group1 (Slave) | codis-server-g1-s1 | 6379 | 36380 | ./data/g1-s1:/data | |
| Codis-Server Group2 (Master) | codis-server-g2-m1 | 6379 | 36381 | ./data/g2-m1:/data | |
| Codis-Server Group2 (Slave) | codis-server-g2-s1 | 6379 | 36382 | ./data/g2-s1:/data |
提示:端口映射规则中,我们将管理端口(如Dashboard的18080)尽量不暴露到宿主机,以增强安全性。Proxy的业务端口(11080)和FE的端口(9090)需要对外暴露,供应用和管理员访问。
3. 编写核心配置文件与Docker Compose编排
Codis的各个组件都通过TOML格式的配置文件来定义行为。这是部署的核心环节,配置错误是导致集群无法启动的最常见原因。
首先,创建项目目录结构:
codis-docker-deploy/
├── docker-compose.yml
├── config/
│ ├── dashboard.toml
│ └── proxy.toml
├── zk_data/
└── data/ (子目录在启动时自动创建)
第一步:配置Codis-Dashboard (config/dashboard.toml) Dashboard的配置相对简单,主要指定协调器(ZooKeeper)的地址和自身的管理网络。
# 协调器地址,使用容器名zk1
coordinator = “zk”
zk = “zk1:2181”
# Dashboard监听的网络地址。这里使用0.0.0.0监听所有网卡。
# 注意:在Docker中,这个地址是容器内的地址。
product = “codis-demo”
dashboard_addr = “0.0.0.0:18080”
# 密码验证,生产环境务必设置
password = “”
# 会话超时时间
session_max_timeout = “30m”
session_max_bufsize = 131072
session_max_pipeline = 1024
第二步:配置Codis-Proxy (config/proxy.toml) Proxy的配置是关键,特别是网络地址部分,直接决定了集群内外能否正常通信。
product = “codis-demo”
dashboard_addr = “codis-dashboard:18080” # 使用容器名访问Dashboard
# Proxy对外提供服务的地址。这是第一个大坑!
# 这里必须填写客户端(你的应用程序)能够访问到的地址。
# 因为Proxy会把这个地址注册到ZooKeeper,供客户端查询。
# 假设宿主机IP是192.168.1.100,我们映射了21080端口。
proxy_addr = “0.0.0.0:11080” # 容器内监听地址
host_proxy = “192.168.1.100:21080” # 对外注册的地址
# Proxy管理API的地址。同样需要内外映射。
admin_addr = “0.0.0.0:19000” # 容器内监听地址
host_admin = “192.168.1.100:29000” # 对外注册的地址
# 后端Codis-Server的默认连接池配置
backend_ping_period = “5s”
backend_recv_bufsize = 131072
backend_send_bufsize = 65536
backend_recv_timeout = “30s”
backend_send_timeout = “30s”
backend_conn_timeout = “5s”
避坑指南1:
host_proxy和host_admin配置 这是Docker部署Codis最核心的配置项。proxy_addr和admin_addr是容器内部监听的地址,而host_proxy和host_admin是容器外部可访问的地址(通常是宿主机IP+映射端口)。Codis启动时,会将host_*地址注册到ZooKeeper。如果你的客户端(比如Java应用)在另一台机器上,它从ZooKeeper拿到的是这个host_proxy地址。如果这里配错了(比如配成了容器内IP),客户端将无法连接到Proxy。
第三步:编写Docker Compose文件 (docker-compose.yml) 我们将所有服务定义在一个文件中,便于统一管理。
version: ‘3.8’
services:
zk1:
image: zookeeper:3.8
container_name: zk1
restart: unless-stopped
networks:
codis-net:
ipv4_address: 172.19.0.10
ports:
- “32181:2181”
volumes:
- “./zk_data:/data”
environment:
ZOO_MY_ID: 1
codis-dashboard:
image: codis/codis:3.2
container_name: codis-dashboard
restart: unless-stopped
command: codis-dashboard --config /codis/config/dashboard.toml --log /codis/logs/dashboard.log --log-level WARN
networks:
codis-net:
ipv4_address: 172.19.0.20
volumes:
- “./config/dashboard.toml:/codis/config/dashboard.toml”
depends_on:
- zk1
codis-fe:
image: codis/codis:3.2
container_name: codis-fe
restart: unless-stopped
command: codis-fe --dashboard-list 172.19.0.20:18080 --listen 0.0.0.0:9090 --log /codis/logs/fe.log --log-level WARN
networks:
codis-net:
ipv4_address: 172.19.0.30
ports:
- “39090:9090”
depends_on:
- codis-dashboard
codis-proxy-1:
image: codis/codis:3.2
container_name: codis-proxy-1
restart: unless-stopped
command: codis-proxy --config /codis/config/proxy.toml --log /codis/logs/proxy1.log --log-level WARN --cpu 2 --ncpu 1
networks:
codis-net:
ipv4_address: 172.19.0.41
ports:
- “29000:19000”
- “21080:11080”
volumes:
- “./config/proxy.toml:/codis/config/proxy.toml”
depends_on:
- codis-dashboard
deploy:
resources:
limits:
cpus: ‘2’
memory: 1G
codis-proxy-2:
image: codis/codis:3.2
container_name: codis-proxy-2
restart: unless-stopped
command: codis-proxy --config /codis/config/proxy.toml --log /codis/logs/proxy2.log --log-level WARN --cpu 2 --ncpu 1
networks:
codis-net:
ipv4_address: 172.19.0.42
ports:
- “29001:19000”
- “21081:11080”
volumes:
- “./config/proxy.toml:/codis/config/proxy.toml”
depends_on:
- codis-dashboard
deploy:
resources:
limits:
cpus: ‘2’
memory: 1G
codis-server-g1-m1:
image: codis/codis:3.2
container_name: codis-server-g1-m1
restart: unless-stopped
command: codis-server /codis/redis.conf --maxmemory 2gb --daemonize no
networks:
codis-net:
ipv4_address: 172.19.0.51
ports:
- “36379:6379”
volumes:
- “./data/g1-m1:/data”
deploy:
resources:
limits:
memory: 2.5G
codis-server-g1-s1:
image: codis/codis:3.2
container_name: codis-server-g1-s1
restart: unless-stopped
command: codis-server /codis/redis.conf --maxmemory 2gb --daemonize no --slaveof codis-server-g1-m1 6379
networks:
codis-net:
ipv4_address: 172.19.0.52
ports:
- “36380:6379”
volumes:
- “./data/g1-s1:/data”
depends_on:
- codis-server-g1-m1
deploy:
resources:
limits:
memory: 2.5G
codis-server-g2-m1:
image: codis/codis:3.2
container_name: codis-server-g2-m1
restart: unless-stopped
command: codis-server /codis/redis.conf --maxmemory 2gb --daemonize no
networks:
codis-net:
ipv4_address: 172.19.0.61
ports:
- “36381:6379”
volumes:
- “./data/g2-m1:/data”
deploy:
resources:
limits:
memory: 2.5G
codis-server-g2-s1:
image: codis/codis:3.2
container_name: codis-server-g2-s1
restart: unless-stopped
command: codis-server /codis/redis.conf --maxmemory 2gb --daemonize no --slaveof codis-server-g2-m1 6379
networks:
codis-net:
ipv4_address: 172.19.0.62
ports:
- “36382:6379”
volumes:
- “./data/g2-s1:/data”
depends_on:
- codis-server-g2-m1
deploy:
resources:
limits:
memory: 2.5G
networks:
codis-net:
external: true
name: codis-net
这个Compose文件定义了完整的集群。注意几个细节:
- 我们为每个服务在
codis-net网络中指定了静态IP,便于管理和查看。 codis-server的Slave节点通过--slaveof参数指定了Master,建立了主从复制。- 通过
deploy.resources.limits限制了容器的CPU和内存使用,这是生产环境的好习惯。 - Proxy和Server都使用了
codis/codis:3.2这个官方镜像,它包含了所有必要的二进制文件。
4. 启动集群与初始化配置
一切就绪,现在可以启动集群了。
# 进入项目目录
cd codis-docker-deploy
# 启动所有服务(-d 后台运行)
docker-compose up -d
# 查看启动日志,确保所有容器状态为“Up”
docker-compose ps
如果一切正常,你会看到所有容器的状态都是“Up”。接下来,我们需要通过Codis-FE的Web界面来完成集群的最终初始化。这是将分散的组件“粘合”成一个可用集群的关键步骤。
- 访问管理界面:打开浏览器,访问
http://你的宿主机IP:39090。你应该能看到Codis-FE的登录页面(默认无密码,直接登录)。 - 选择/创建产品:登录后,点击左上角的“+”号,新建一个产品(Product),名称与我们配置文件中
product = “codis-demo”保持一致。然后选中这个产品进入集群管理页。 - 添加Proxy:进入后,在“Proxy”标签页,你应该能看到两个Proxy实例(
codis-proxy-1和codis-proxy-2),状态可能是“离线”。这是因为它们刚启动,还未被Dashboard完全接管。等待十几秒或点击“刷新”按钮,状态应变为“在线”。避坑指南2:Proxy状态一直离线 如果Proxy长时间处于离线状态,请检查:
- Dashboard容器日志:
docker logs codis-dashboard - Proxy容器日志:
docker logs codis-proxy-1 - 最常见的原因是
dashboard_addr配置错误(无法连接Dashboard),或者host_proxy地址不可达(网络问题)。
- Dashboard容器日志:
- 创建Group并添加Server:
- 切换到“Group”标签页。此时列表为空。
- 点击“New Group”按钮,输入Group ID为“1”,再次点击确认,创建Group-1。
- 在Group-1的行中,找到“Add Server”输入框。输入Master节点的地址。注意:这里要填容器网络内的地址,即
codis-server-g1-m1:6379。点击“Add Server”。 - 同样地,在下一行输入Slave节点的地址:
codis-server-g1-s1:6379,添加到同一个Group。 - 重复以上步骤,创建Group-2,并添加
codis-server-g2-m1:6379(Master)和codis-server-g2-s1:6379(Slave)。
- 初始化Slot:切换到“Slot”标签页。你会看到1024个Slot的状态都是“offline”。我们需要将它们分配给两个Group。
- 最快捷的方式是点击页面顶部的“Rebalance All Slots”按钮。系统会自动、均匀地将1024个Slot分配给Group-1和Group-2。
- 分配完成后,所有Slot的状态会变为“online”。
至此,一个拥有两个数据分片(Group)、每个分片一主一从、前端由两个Proxy负载均衡的Codis集群就完全搭建好了。
5. 客户端连接测试与生产环境调优建议
现在,让我们用最简单的redis-cli测试一下集群是否工作正常。
# 连接任意一个Proxy的业务端口(使用宿主机的映射端口)
redis-cli -h 你的宿主机IP -p 21080
# 在redis-cli中执行命令
127.0.0.1:21080> set test_key “hello_codis”
OK
127.0.0.1:21080> get test_key
“hello_codis”
127.0.0.1:21080> info
# 你会看到返回的信息显示这是一个“Codis-Proxy”,而不是普通的Redis。
测试成功!你的应用程序现在可以使用任何支持Redis协议的客户端(如Jedis、Lettuce、go-redis等),像连接单点Redis一样连接Proxy的地址(宿主机IP:21080或:21081)进行操作。
生产环境调优与进阶考量:
-
高可用部署:
- ZooKeeper集群:上述示例使用了单点ZooKeeper,这是单点故障源。生产环境必须部署至少3节点的ZooKeeper集群。
- 多Dashboard:虽然Dashboard本身是无状态的,但它是管理入口。可以考虑部署多个Dashboard实例,配合负载均衡器,或者至少保证能快速重建。
- Proxy数量:根据客户端连接数和吞吐量需求,动态调整Proxy数量。通常建议至少2个以实现负载均衡和故障转移。
-
监控与告警:
- Codis-FE界面提供了基本的监控,但不适合自动化告警。需要将关键指标(如Proxy QPS、延迟、后端Server内存使用率、连接数等)接入Prometheus + Grafana体系。
- 关键的监控项包括:
- Proxy的
codis_proxy_online状态 - Slot的迁移状态
- 各Group主从同步延迟(
master_repl_offset和slave_repl_offset) - 内存使用率,避免触发
maxmemory策略导致写失败。
- Proxy的
-
数据迁移与扩容:
- 当数据增长需要新增Group时,可以在FE上轻松操作。新增Group -> 添加Server -> 通过“Slots Migrate”功能,从现有Group中迁移一部分Slot到新Group。整个过程在线进行,对应用影响很小。
- 避坑指南3:迁移期间性能抖动
数据迁移本质上是将某个Slot的键从一个Server批量迁移到另一个Server。迁移期间,对该Slot的读写会有短暂阻塞或延迟。建议在业务低峰期进行,并控制单次迁移的Key数量(通过FE界面设置)。
-
配置参数优化:
backend_*_timeout:根据网络状况和后端Server负载调整超时时间。内网环境下可以适当调低。session_max_pipeline:对于大量使用pipeline的客户端,可以适当调高此值以提升吞吐。- Codis-Server的
maxmemory:务必设置,并预留一部分内存(比如20%)给系统和其他开销,防止内存溢出导致进程被系统杀死。
-
客户端最佳实践:
- 使用连接池,并合理设置池大小。
- 实现Proxy列表的自动发现与故障转移。可以使用官方推荐的Jodis(基于ZooKeeper的Java客户端),或者定期从ZooKeeper中拉取可用的Proxy列表。
- 避免使用Codis不支持的命令,如
KEYS *(在分布式环境下行为不确定)、事务MULTI/EXEC(Codis不支持跨Key事务)。
最后,记得定期备份你的数据。虽然Codis-Server支持RDB和AOF持久化,但在分布式环境下,备份策略需要考虑多个节点。一种可行的方案是定期在从节点上执行SAVE或BGSAVE,然后将RDB文件归档到对象存储中。
2632



被折叠的 条评论
为什么被折叠?



