1. 为什么选择用 Docker Compose 部署 Redis
Redis 这个内存数据库,不少开发者第一次接触是在本地直接redis-server一把梭跑起来的。等到真正进入项目环境,问题就来了:配置散落一地,密码随手写在启动命令里,数据文件不知道落在哪个目录,换一台机器就要重新折腾一遍。我以前也经历过这种状态,后来所有 Redis 实例统一改成 Docker Compose 管理,整个部署方式才真正变成"可复制、可审计、可回滚"。
Docker Compose 解决问题的核心思路,是把 Redis 的镜像、端口、密码、持久化文件、资源限制全部写进一个docker-compose.yml文件里。相比裸机安装或者单条docker run命令,它的优势非常明显:
- 配置即代码,整个 Redis 实例的期望状态一眼就能看全。
- 环境一致性高,开发、测试、生产共用同一份编排文件,差异只在环境变量。
- 启停操作收敛为
docker compose up -d和docker compose down,不需要记长串命令。 - 数据卷独立管理,容器删了重建,数据不丢。
这套方案适合谁?只要是需要在 Linux 服务器或者个人电脑上安装 Redis 做本地开发、测试环境、甚至小规模生产使用的场景,都值得用 Compose 来管理。对刚接触 Docker 的人来说,这也是理解容器存储、网络映射和配置挂载的最佳入门案例之一。
另外说明一下,我下面的操作默认你已经在机器上装好了 Docker 和 Docker Compose 插件。如果你还在用旧版docker-compose独立命令,大部分内容同样适用,只是命令开头需要把docker compose替换成docker-compose。
2. 动手前先规划目录和镜像版本
2.1 目录结构:提前想好数据落盘位置
项目里最常见的错误,是把 Redis 数据直接放在容器内部。容器一删除,数据烟消云散。正确做法是使用绑定挂载或者命名卷,把/data目录映射到宿主机。
我习惯使用绑定挂载,因为这样能直接看到和管理数据文件。以我当时的一个模拟项目 X 为例,目录结构是这样的:
redis-stack/ ├── docker-compose.yml ├── redis.conf ├── data/ │ ├── appendonly.aof │ └── dump.rdb └── logs/ └── redis.logdata目录用于存放 RDB 快照和 AOF 日志,logs目录用于输出 Redis 运行日志。这样设计的直接好处是:排查问题时可以cat logs/redis.log直接看,不需要docker exec绕进容器;备份时也只需要打包data目录。
2.2 镜像选择:为什么优先用 Alpine 版本
Redis 官方镜像有两个主流标签,一个是redis:7.4,一个是redis:7.4-alpine。前者基于 Debian,后者基于 Alpine Linux。我一般无脑选 alpine 版本,原因无非三点:
- 镜像体积小,通常只有 Debian 版的一半左右,拉取和传输都更快。
- 容器内不必要的组件更少,受攻击面更窄。
- 官方 alpine 镜像的 Redis 功能和普通版完全一致,不存在功能缩水。
版本号我建议锁定到具体小版本,比如redis:7.4-alpine,而不是直接写redis:alpine或者redis:latest。在实际项目中,曾经遇到过 Redis 从 6.x 升级到 7.x 后 AOF 文件格式发生变化,导致回滚麻烦的情况。锁定小版本能避免这类隐性升级。
2.3 准备 redis.conf:不要指望默认配置直接上生产
官方镜像自带一份默认配置,它做本地开发没问题,但一旦涉及密码、持久化策略、最大内存限制,默认值就远远不够了。我的建议是在宿主机维护一份最小化的redis.conf,然后通过挂载覆盖容器内配置。
下面是我常用的一个基础配置模板,适合大多数业务场景:
# 绑定地址,容器内使用 0.0.0.0 便于外部访问 bind 0.0.0.0 # 保护模式,开启后未设置密码时只允许本机访问 protected-mode yes # 端口,默认就是 6379,这里显式写出来方便修改 port 6379 # 持久化策略 appendonly yes appendfsync everysec # RDB 快照规则,格式:seconds changes save 900 1 save 300 10 save 60 10000 # 数据目录,容器内路径 dir /data # 日志文件,便于排查 logfile /logs/redis.log # 最大内存策略,根据业务调整 maxmemory 256mb maxmemory-policy allkeys-lru有人会问,密码为什么不在redis.conf里写?当然也可以写,requirepass指令完全支持。但我更推荐放到 Compose 的环境变量里,原因后面会详细讲。这里有一个关键点要单独提醒:如果配置文件里配置了logfile,但宿主机对应的logs目录没有挂载到容器,Redis 会因为无法写日志直接启动失败。很多人第一次部署就被这个坑卡住。
3. 编写 docker-compose.yml:可直接套用的完整方案
3.1 完整编排文件逐段拆解
直接给出一份可以照抄的docker-compose.yml,我逐行解释关键点。
services: redis: image: redis:7.4-alpine container_name: redis-svc restart: unless-stopped ports: - "6379:6379" volumes: - ./redis.conf:/etc/redis/redis.conf:ro - ./data:/data - ./logs:/logs environment: - TZ=Asia/Shanghai command: - redis-server - /etc/redis/redis.conf这里每项配置都值得展开说一下。
container_name我建议显式指定。默认情况下 Compose 会生成项目名_服务名_序号这种名字,排查问题时不够直观。改成redis-svc之后,docker ps、docker logs都更好操作。
restart: unless-stopped是容器服务最稳妥的重启策略。服务器重启之后,容器会自动拉起来。除非你手动执行docker compose stop,否则它会一直保持运行。相比always,unless-stopped更尊重手动停止的意图。
ports映射需要注意:左边宿主机端口,右边容器端口。如果服务器上的 6379 已经被占用,可以改成16379:6379。不过要提醒一句,Redis 默认端口太容易被扫描工具盯上,公网环境建议换高位端口,同时开启密码认证和防火墙白名单。
command里指定了redis-server /etc/redis/redis.conf,这告诉容器启动时使用我们挂载进去的配置文件。挂载时我加了:ro后缀,表示容器内只读。这样即使容器被入侵,也无法篡改配置文件,可以算作一个纵深防御的小手段。
3.2 密码、持久化与资源限制的配合策略
密码这块,我习惯用环境变量配合配置文件的requirepass占位方案。但 Redis 官方镜像不像某些镜像会自动从环境变量生成配置,所以更常见的做法是直接在redis.conf里写requirepass,或者通过command追加参数。
我的推荐方案是修改redis.conf,增加一行:
requirepass your_strong_password_here然后把密码同时写进 Compose 文件的环境变量,供业务系统读取:
environment: - TZ=Asia/Shanghai - REDIS_PASSWORD=your_strong_password_here这样做的好处是:Compose 文件承担了"配置记录"的作用,业务侧需要连接 Redis 时,直接从这个变量获取密码即可。实际工作中我用过更规范的方法,引入.env文件管理密码,但那个属于进阶玩法,等技术熟练之后再迁移也不迟。
生产环境密码至少 16 位,混合大小写、数字和符号。曾经遇到某公司线上 Redis 用弱口令,被扫描工具直接提权拿下服务器的案例,这点真不能马虎。
持久化方面,我的组合是appendonly yes加appendfsync everysec。这意味着每个数据变更都会记录到 AOF 文件,但每秒才真正落盘一次。这个组合兼顾安全和性能:断电最多丢失最后一秒的写入,日常读写性能也不会有明显损耗。
这里再补充一个容易踩的坑:如果同时开启 RDB 和 AOF,Redis 重启时优先使用 AOF 文件恢复数据。所以 AOF 文件如果存在异常但未触发校验失败,可能导致恢复出旧数据。高可靠性场景建议定期做redis-check-aof检查。
3.3 首次启动和连通性验证
配置都准备好之后,执行:
docker compose up -d注意要在这个目录下执行,Compose 默认读取当前目录的docker-compose.yml。如果文件不是这个名字,可以用-f参数指定。
启动后按下面三步验证:
# 1. 查看容器运行状态 docker ps # 2. 查看日志确认没有报错 docker logs redis-svc # 3. 进入容器内执行命令测试 docker exec -it redis-svc redis-cli -a your_strong_password_here ping如果返回PONG,说明实例已经正常运行。此时再用宿主机上的客户端工具连接127.0.0.1:6379,验证端口映射是否正常。
我自己的习惯是再写一个一键部署脚本deploy.sh,内容就三行:
#!/bin/bash docker compose pull docker compose up -d docker ps | grep redis-svc这样以后每次部署新环境,执行一个脚本就够了,不用临时翻文档。
4. 上线后最容易翻车的几个问题
4.1 修改密码后的重启陷阱
很多人以为改密码就是改一下redis.conf里的requirepass,然后docker compose down && docker compose up -d就完事了。实际操作中我踩过一次坑:容器重建时,Redis 启动读取新配置成功,但docker exec进去执行命令时忘了加新密码,一直报NOAUTH Authentication required,白白排查了十分钟。
这个问题的根源在于:Redis 客户端默认不带密码,所有命令执行前都需要AUTH。排查思路很简单,先用新密码执行AUTH再PING。另外还有一个细节:修改密码前,如果有存量连接还在使用旧密码,它们不会立即断开,只有新连接才会要求新密码。所以线上环境改密码要选择在低峰期,并且应用侧要同步更新连接信息。
4.2 端口映射冲突和数据目录权限
端口冲突是最常见的启动失败原因。如果你执行docker compose up -d后容器状态一直是Exited,大概率是 6379 端口被宿主机进程占用。排查命令:
ss -lntp | grep 6379如果确实被占用,两种处理方式:杀掉占用进程,或者把宿主机端口改成16379。我个人更推荐后者,应用侧改一下连接配置就行,不动系统里的其他服务。
另一个高频问题是数据目录权限。容器内的 Redis 进程默认以名为redis的用户运行,UID 通常是 999。宿主机挂载的data和logs目录如果属于 root,容器内进程就无法写入,启动日志会出现Can't open the log file: Permission denied这类错误。
解决办法是给目录授权:
chown -R 999:999 ./data ./logs这里特别提醒:不要图省事直接chmod 777。目录权限过宽等于给服务器埋雷,正确做法是精确到容器进程的 UID。
4.3 数据持久化的验证方法
配置了appendonly yes之后,建议主动做一次持久化验证,而不是等到真丢数据了再后悔。我的验证方法是:
# 1. 写入临时 key docker exec redis-svc redis-cli -a password set test_key test_value # 2. 重启容器 docker compose restart # 3. 检查 key 是否还在 docker exec redis-svc redis-cli -a password get test_key如果返回test_value,说明 AOF 持久化生效。这个操作流程最好在新环境部署完成后立刻跑一遍,成本极低,但能提前发现挂载配置错误。
此外,建议观察data目录里的appendonly.aof文件大小变化。正常情况下它会持续增长,如果长时间不增长,说明可能没有写操作,或者持久化配置没生效。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决办法 |
|---|---|---|---|
| 容器启动后立刻退出 | 端口被占用 | ss -lntp | grep 6379 | 改宿主机端口或释放占用 |
| 日志写入失败 | 目录权限不足 | 查看容器启动日志 | chown -R 999:999 ./logs |
| 外部连接超时 | 云安全组未放行 | 检查防火墙规则 | 放行对应 TCP 端口 |
| 无法执行命令出现 NOAUTH | 密码未输入或错误 | docker logs redis-svc确认密码设置 | 重新执行AUTH |
| 重启后数据丢失 | 持久化未开启 | 检查appendonly配置 | 开启 AOF 并验证恢复 |
| 容器持续重启循环 | 配置文件语法错误 | docker logs redis-svc | 校验redis.conf语法 |
| 时区导致日志时间不符 | 容器默认 UTC | docker exec date查看 | Compose 中设置 TZ 环境变量 |
5. 容器化部署 Redis 的一些进阶心得
5.1 关于资源限制,别让 Redis 吃光服务器内存
Redis 是内存型数据库,如果服务器上还跑着其他服务,不加限制会有很大风险。Redis 本身有maxmemory配置,但容器层面最好也做一层限制。
在 Compose 文件里加上:
deploy: resources: limits: memory: 512m cpus: '1.0'这样即使 Redis 因为业务量激增突破了maxmemory限制,也会先被容器层面的 cgroup 约束住,而不是把宿主机内存全部耗尽。两种限制配合使用,内存安全性会好很多。
5.2 容器内是否要执行redis-cli --sentinel之类的高阶命令
容器化部署之后,直接进容器执行命令虽然可行,但不建议成为日常操作习惯。原因很简单:容器是瞬态的,任何手动修改都不会被编排文件记录,下次重建又恢复原样。
比如有些人在容器里临时CONFIG SET requirepass改密码,这会在容器删除后失效,而且容易让人产生"已经改好了"的错觉。正确的做法永远是改宿主机上的redis.conf,然后docker compose up -d重新创建容器。这个习惯能避免大量隐形配置漂移问题。
等到熟悉这套标准流程之后,可以考虑引入外置配置管理工具做密钥管理,比如常见的高位随机密码生成策略加定期轮换。但基础容器化部署阶段,先把 Compose 文件管理好比什么都重要。
5.3 监控与备份的小建议
容器方式部署的 Redis,监控不要只在容器层面做,还应该在宿主机侧采集。最简单的思路是定时执行docker exec redis-svc redis-cli INFO,把关键指标写入监控系统。
备份方面,我用的是定时任务,每天凌晨把data目录打包并保留最近七天:
#!/bin/bash BACKUP_DIR=/backup/redis DATE=$(date +%Y%m%d) docker exec redis-svc redis-cli -a "$REDIS_PASSWORD" BGSAVE tar czf "$BACKUP_DIR/redis-$DATE.tar.gz" -C /redis-stack/data . find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -deleteBGSAVE是后台生成 RDB 快照的命令,配合 AOF 文件一起备份,可以保证数据恢复时有多个时间点可选择。
6. 最后分享一点实际操作中的体会
用 Docker Compose 管理 Redis 这两年多,最大的感受是:部署方式的收益曲线是复利的。第一次搭建可能要花一两个小时查文档、试配置,但之后的每一次迁移、扩容、故障恢复,时间都会被压缩到分钟级。
记得有一次某环境 Redis 异常崩溃,我当时只用了三步就完成恢复:docker compose down、检查数据目录完整性、docker compose up -d。整个过程不超过三分钟,业务方甚至没感知到服务中断。这就是配置即代码带来的底气。
如果你目前还在对着一台裸服务器手动编译安装 Redis,或者还在用一长串docker run参数临时拼凑,真心建议花半小时把这个 Compose 方案搭起来。把你自己的redis.conf和docker-compose.yml沉淀到代码仓库里,下次再需要部署 Redis 时,直接git clone加docker compose up -d,十分钟搞定。
我自己最近在扩展的方向,是把这套 Redis 编排方案同时用于本地开发和测试环境,通过不同.env文件隔离配置,一套 Compose 文件管多个环境。后续如果再深入研究,会考虑加入 Redis 集群和主从复制的编排。但基础一定是今天这份最小可用方案先跑稳,再谈其他。