Redis 容器化最容易出的问题不是装不上,而是装上了没密码、重启丢数据、或者配置根本没被加载。这篇按顺序把这几件事定下来:密码、持久化、内存上限,以及出错时先看哪里。
一、镜像和目录
docker pull redis:7.2
sudo mkdir -p /data/redis/{conf,data}
官方镜像默认以 redis 用户(uid 999)运行,/data 要能让它写:
sudo chown -R 999:999 /data/redis/data
宿主机目录属主不改成 999,后面要么存 RDB 失败,要么直接起不来。
二、先起一个空壳验证
先在没有任何配置的情况下跑一遍,确认镜像和端口没问题:
docker run -d --name redis-test -p 6379:6379 redis:7.2
docker exec redis-test redis-cli ping
返回 PONG 就说明镜像正常。验证完删掉,正式部署要带配置文件和密码:
docker rm -f redis-test
三、redis.conf
/data/redis/conf/redis.conf:
bind 0.0.0.0
protected-mode yes
port 6379
timeout 0
tcp-keepalive 300
requirepass 换成你的强密码
daemonize no
loglevel notice
logfile ""
dir /data
# RDB
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
# AOF
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 内存
maxmemory 512mb
maxmemory-policy allkeys-lru
几个参数的作用:
| 参数 | 说明 |
|---|---|
bind 0.0.0.0 |
容器内监听所有地址,宿主机能否访问由 -p 决定 |
protected-mode yes |
没密码时拒绝外部连接,有密码时保留也没问题 |
daemonize no |
容器里必须前台运行,写 yes 会启动即退出 |
dir /data |
RDB 和 AOF 都写这里,必须挂卷 |
appendonly yes |
开 AOF,重启不丢最近写入 |
maxmemory + policy |
防止把宿主机内存吃光 |
requirepass 写在配置文件里,不要写在 docker run 的 --requirepass 参数上,否则 docker inspect 和 ps 里都能直接看到。
四、docker run 或 compose
docker run 版本:
docker run -d --name redis \
-p 6379:6379 \
-v /data/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf:ro \
-v /data/redis/data:/data \
-e TZ=Asia/Shanghai \
--restart=always \
redis:7.2 redis-server /usr/local/etc/redis/redis.conf
compose 版本:
services:
redis:
image: redis:7.2
container_name: redis
restart: always
ports:
- "6379:6379"
environment:
TZ: Asia/Shanghai
REDIS_PASSWORD: 换成你的强密码
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- /data/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf:ro
- /data/redis/data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli --no-auth-warning -a \"$$REDIS_PASSWORD\" ping | grep -q PONG"]
interval: 30s
timeout: 5s
retries: 3
两处说明:
- healthcheck 里的
$$是 compose 的转义,最终传给容器的是$REDIS_PASSWORD,由 shell 展开。写成单个$会被 compose 在解析阶段替换掉,密码就丢了。 - 配置文件里已经写了
requirepass,环境变量REDIS_PASSWORD只是为了给健康检查用,两边的值要一致,改密码时一起改。
command 一定要带配置文件路径。不带的话镜像会用自己的默认配置启动,requirepass、appendonly 全部不生效,看起来容器起得好好的,实际是个没有密码的空库。
五、验证
# 不带密码访问,应该被拒绝
docker exec redis redis-cli ping
# NOAUTH Authentication required.
# 带密码
docker exec redis redis-cli --no-auth-warning -a "$(grep requirepass /data/redis/conf/redis.conf | awk '{print $2}')" ping
# 确认配置真的加载了
docker exec redis redis-cli --no-auth-warning -a '密码' CONFIG GET appendonly
docker exec redis redis-cli --no-auth-warning -a '密码' CONFIG GET maxmemory
# 确认持久化文件
ls -lh /data/redis/data
CONFIG GET appendonly 返回 yes,/data/redis/data 下能看到 dump.rdb 或 appendonlydir,才说明配置生效、持久化在工作。
六、持久化和备份
Redis 有两种持久化:
| 方式 | 原理 | 丢数据窗口 | 开销 |
|---|---|---|---|
| RDB | 定时 fork 子进程写快照 | 两次快照之间 | fork 时内存翻倍风险 |
| AOF | 追加写命令,everysec 每秒刷盘 |
最多 1 秒 | 磁盘 IO 和文件体积 |
生产环境一般两个都开:AOF 保证少丢,RDB 方便搬走做备份。
备份:
# 触发一次快照,然后复制文件
docker exec redis redis-cli --no-auth-warning -a '密码' BGSAVE
sleep 2
docker exec redis redis-cli --no-auth-warning -a '密码' LASTSAVE
sudo cp /data/redis/data/dump.rdb /backup/redis-$(date +%F).rdb
BGSAVE 是异步的,复制前用 LASTSAVE 确认时间戳已经更新。
不落盘直接拉一份也可以,适合数据量不大的场景:
docker exec redis redis-cli --no-auth-warning -a '密码' --rdb /tmp/backup.rdb
docker cp redis:/tmp/backup.rdb ./backup.rdb
恢复就是把 dump.rdb 放回 /data,重启容器。注意恢复前先停 Redis,否则可能被正在运行的实例覆盖。
七、常见报错
| 现象 | 原因 | 处理 |
|---|---|---|
NOAUTH Authentication required |
客户端没带密码 | 客户端配置密码,或 AUTH 密码 |
ERR Client sent AUTH, but no password is set |
配置没加载,服务端还是无密码状态 | 检查 command 是否带了配置文件路径,重启容器 |
| 容器启动后立刻退出,日志只有一两行 | conf 里写了 daemonize yes |
改成 no |
MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk |
磁盘满、目录没权限、或 overcommit 限制 | 看磁盘和 /data 属主,处理完执行 CONFIG SET stop-writes-on-bgsave-error no 只是临时放行 |
日志 WARNING overcommit_memory is set to 0 |
内核内存策略,fork 快照可能失败 | 宿主机 sysctl -w vm.overcommit_memory=1,写进 /etc/sysctl.d/ 持久化 |
WARNING The TCP backlog setting of 511 cannot be enforced |
somaxconn 太小 |
sysctl -w net.core.somaxconn=1024 |
Permission denied 写 dump.rdb |
/data 属主不是 999 |
chown -R 999:999 /data/redis/data |
Bad directive or wrong number of arguments |
conf 语法错,常见于行尾多余空格或复制时带了中文引号 | 按报错行号改 |
| 重启后数据全没了 | 没挂 /data,或者没开 AOF/RDB |
挂卷并确认 dir /data |
READONLY You can't write against a read only replica |
配置里残留 replicaof |
删掉该配置重启 |
八、几条限制
- 单机 Redis 没有高可用,主库挂了就是不可用。要主从或哨兵,是另一套部署,别指望
restart: always顶上。 maxmemory-policy allkeys-lru会淘汰任何键,包括没设过期时间的。做缓存可以,当存储用要换成noeviction并密切盯内存。appendfsync everysec是性能和数据安全之间的折中,要求零丢失得用always,代价是每次写都等磁盘。
访问控制上还有一条:-p 6379:6379 会把 Redis 直接暴露到宿主机所有网卡。只给内网用的服务,端口映射写成 -p 127.0.0.1:6379:6379,让外面连不进来。