
前阵子在云服务器上重新部署Redis我把Docker部署Redis的全过程从头到尾捋了一遍最核心的痛点还是那两个密码访问和数据持久化。很多人第一次用Docker跑Redis眼睛只盯着“docker run redis”这句话能出结果结果容器一重启数据没了或者6379端口直接裸奔在公网上被扫描器打爆。这篇文章就是来把这些事讲透的面向的是要在服务器上真正落地Redis的朋友不管你是第一次装还是想系统梳理一遍完整部署姿势都能直接照着做。1. 先想清楚为什么你需要的不是“把Redis跑起来”而是“一套部署方案”拉镜像、跑容器、看到启动日志这三步连起来也就一分钟。但生产环境里真正要命的问题全部发生在一分钟之后容器删了数据还在不在没有密码的Redis对公网开放意味着什么参数堆在命令里下次还记不记得线上部署和本地开发最大的区别就是“可恢复性”。本地你用docker run redis跑一下测试完删了无所谓的Redis里那几条临时数据丢了也没人找你。但服务器上的Redis一旦承担缓存、session、分布式锁这些职责你就要把它当成一个“有状态的长期服务”来部署而不是一个“临时进程”。这个思维转换过来后面每一步为什么这么做就都顺了。1.1 镜像选型不要上来就latest我在服务器上部署时用的是redis:7-alpine没有用redis:latest。原因有两点redis:latest虽然在持续更新但你今天拉的和明天拉的可能不是同一个版本环境可复现性差。生产部署应该锁定一个大版本比如redis:7-alpine或更精确的redis:7.2-alpine。alpine版本基于Alpine Linux构建体积小内存占用低Redis本身是C语言写的几乎不依赖glibc跑在musl libc上完全没问题。服务器资源能省一点是一点。如果你之后要用Redis模块或者特殊编译参数那可能得考虑官方带编译工具链的镜像或者自己打镜像但常规部署场景选redis:7-alpine就够了。1.2 服务器上的Docker环境准备很多朋友在本地用的是Docker Desktop但服务器上装的是Docker Engine这是两个东西。Docker Desktop是带图形界面的适合开发机服务器上一般直接用命令行操作Docker Engine。装好之后先验一下环境docker version docker compose version第一命令确认Docker Engine正常第二个命令确认Compose插件可用。有些老机器上只装了docker-compose带横杠、Python实现的旧版新版是docker compose不带横杠、Go实现的插件两条命令的语法基本兼容但优先建议用新版。这里先把基础打好后面所有操作用到的就是docker和docker compose这两条命令。2. 用一条命令先跑起来密码和持久化参数逐个拆给你看先把最简单的部署命令给出来后面再逐步升级成生产可用的方案。先建目录防止容器写入时因为目录不存在而出现权限或挂载异常mkdir -p /data/redis然后运行容器docker run -d \ --name redis-server \ --restart unless-stopped \ -p 6379:6379 \ -v /data/redis:/data \ redis:7-alpine \ redis-server --requirepass 你的强密码 --appendonly yes这条命令看着不长但每个参数背后都有讲究我一个个拆-d后台运行容器不然你的终端会一直卡在Redis日志输出上。--name redis-server给容器起个固定的名字后面docker exec、docker logs、docker restart都靠它别让Docker随机生成一个看不懂的名字。--restart unless-stopped服务器重启后容器能自动拉起来。unless-stopped比always更保守一点——如果你手动停下来它不会在你意料之外强行启动但机器重启时它会跟着起来。-p 6379:6379把容器内的6379端口映射到宿主机。这一步决定了外部能不能访问到Redis后面安全组、防火墙的配置也是基于这个端口。-v /data/redis:/data把宿主机的/data/redis目录挂载为容器内的/data目录。Redis默认把RDB快照和AOF日志都写在/data下你挂载了宿主机目录这些文件就直接落在服务器磁盘上容器删除后数据还在。redis-server --requirepass 你的强密码 --appendonly yes这段是在覆盖镜像默认的启动命令。官方Redis镜像默认就是启动redis-server你在它后面追加参数相当于告诉Redis服务“启动时开启密码认证、开启AOF持久化”。跑起来之后马上验证密码是不是真的生效了docker exec -it redis-server redis-cli -a 你的强密码 ping输出PONG如果不带密码试试docker exec -it redis-server redis-cli ping你会看到NOAUTH Authentication required.看到NOAUTH说明服务端已经设置了密码只是你这次请求没有通过认证。这是正常的而且是你想要的结果。2.1 为什么网上说的REDIS_PASSWORD环境变量对你没用这里要专门停下来提醒一个常见误区。网上有很多教程这么写docker run -d --name redis-server -e REDIS_PASSWORDxxx redis但实际上官方Redis镜像的启动命令就是简单的redis-server它不会去读取REDIS_PASSWORD这个环境变量。你加了这个-e参数容器里虽然多了个环境变量但Redis进程根本不看它密码自然也就没有生效。那为什么有人用着用着发现好像密码是生效的因为他用的是第三方封装的镜像或者他自己写了entrypoint脚本去处理这个变量。官方镜像不干这事。如果你实在不想把密码直接写在docker run的命令行参数里比如担心Shell历史记录、或者docker inspect能看到进程启动参数可以改用Shell变量做一层间接传递docker run -d \ --name redis-server \ --restart unless-stopped \ -p 6379:6379 \ -v /data/redis:/data \ --env REDIS_PASSWORD你的强密码 \ redis:7-alpine \ sh -c exec redis-server --requirepass $REDIS_PASSWORD --appendonly yes这种方式下docker inspect redis-server看到的启动命令是sh -c exec redis-server --requirepass $REDIS_PASSWORD...不会直接暴露明文密码。但注意容器内的进程列表里最终还是会出现密码这个方案只是减少了一部分暴露面并不是万能保险箱。真正要做安全管理用配置文件方式更清晰下面一章展开。3. 从“能跑”到“能上线”用redis.conf管理密码与持久化命令行参数的问题在于参数一多就乱也容易被忘。如果哪次重启容器时漏写了--requirepassRedis又变回裸奔状态。所以线上真正落地的形态是——把配置沉淀到文件里。3.1 准备一份最小可用的redis.conf我习惯在宿主机上建这样一套目录结构/data/redis/ ├── config/ │ └── redis.conf └── data/配置文件和数据文件分开以后要改配置只需要动config/redis.conf要备份数据只需要打包data/目录互不干扰。下面是常用的一份最小配置文件你可以直接存为/data/redis/config/redis.confbind 0.0.0.0 protected-mode yes port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile databases 16 always-show-logo no save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /data appendonly yes appendfilename appendonly.aof appendfsync everysec requirepass 你的强密码几个关键项我展开说一下后面排查问题都用得上bind 0.0.0.0让Redis监听所有网卡。这步决定了外部网络能不能连上来。如果保持默认的127.0.0.1那你只能在本机访问外部工具永远连不上。0.0.0.0意味着暴露在网络上所以一定要配合requirepass和安全组限制使用。protected-mode yesRedis的保护模式。它和requirepass是配合的关系——当你设置了密码保护模式可以安全地保持开启如果没设置密码又监听了所有网卡Redis会拒绝外部访问。appendonly yes开启AOF持久化。这个决定了Redis在重启后能恢复到最新状态而不是只靠RDB快照。appendfsync everysec每秒把AOF缓冲刷到磁盘。兼顾性能和数据安全。dir /data指定持久化文件的写入目录。镜像里Redis的工作目录本来就是/data你挂载宿主机目录后dump.rdb和appendonly.aof都会落在宿主机对应目录里。requirepass密码。如果你不想手写配置也可以从官方镜像里复制一份默认配置出来再改docker run --rm redis:7-alpine cat /usr/local/etc/redis/redis.conf /data/redis/config/redis.conf注意不同版本的镜像里配置文件路径可能略有差异复制前可以用docker run --rm redis:7-alpine ls /usr/local/etc/redis/确认一下。我自己更推荐手写上面那份精简配置。原因很简单官方默认配置几百行大部分注释对新手反而是噪音还容易改错一个选项造成启动失败。精简配置只保留最核心的项每行都知道它是干什么的出了错也容易定位。3.2 挂载配置并启动容器配置文件准备好之后重新创建容器docker rm -f redis-server docker run -d \ --name redis-server \ --restart unless-stopped \ -p 6379:6379 \ -v /data/redis/config/redis.conf:/usr/local/etc/redis/redis.conf \ -v /data/redis/data:/data \ redis:7-alpine \ redis-server /usr/local/etc/redis/redis.conf注意最后一条命令redis-server /usr/local/etc/redis/redis.conf。这会告诉Redis“别用默认配置去读我挂载进去的这份文件”。挂载文件的好处是容器外改配置、容器内立即生效重启容器即可完全不依赖命令行参数。3.3 启动失败多半是目录权限问题如果你在这一步遇到容器反复重启或者日志里出现Cant open the append-only file: Permission denied那几乎可以断定是宿主机挂载目录的属主不对。原因在于官方Redis镜像为了安全默认用redis用户运行进程这个用户在镜像里的UID是999。你挂载进来的宿主机目录如果属主是root且权限是755容器内的redis用户没有写权限自然没法在/data下创建AOF文件。解决办法很简单在宿主机上执行chown -R 999:999 /data/redis这里直接用数字999不需要在宿主机上真的创建一个UID为999的用户chown命令就是修改文件系统记录的属主ID和系统里有没有这个用户名没关系。改完再重启容器docker restart redis-server这个权限问题几乎每个人都会遇到一次属于典型的“文档不会专门写、但实战天天碰”的坑。保姆级教程就得把它点出来。4. 数据持久化到底保住了什么RDB、AOF与重启验证很多人对“持久化”的理解就是“数据不会丢”但Redis的持久化其实是两套机制配合的RDB和AOF。RDB是快照Redis会在满足一定条件时把全量数据写成一个二进制文件dump.rdb。它的加载速度快、文件紧凑适合做备份。缺点是快照之间的数据可能丢失比如你设置的save 900 1是“900秒内有1次写入就生成快照”如果服务器在快照生成前突然断电这900秒内的数据就没了。AOF是追加日志Redis把每一条写命令追加到appendonly.aof文件里恢复时重新执行一遍命令。配合appendfsync everysec最多丢一秒的数据。它的缺点是文件比RDB大恢复时要重放命令相对慢一些。生产环境的正解是两者同时开启RDB负责快速加载和备份AOF负责把数据丢失窗口压缩到一秒以内。上面那份配置里已经都配好了。4.1 重启一次看数据还在不在配置完成后做一次最直观的验证。往Redis里写一条数据docker exec -it redis-server redis-cli -a 你的强密码进去后执行set attack_key do not lose me然后退出容器重启容器docker exec -it redis-server redis-cli -a 你的强密码 shutdown docker start redis-server再进去查docker exec -it redis-server redis-cli -a 你的强密码 get attack_key正常会返回do not lose me这一步验证的是“容器重启后数据不丢”。更极端一点你还可以验证“容器整个删除重建后数据也不丢”docker stop redis-server docker rm redis-server然后用第3.2节那条docker run命令原样把容器建回来再get attack_key数据依然在。原因就是你挂载了/data/redis/data:/data持久化文件在宿主机上容器本身只是个“无状态外壳”。4.2 备份与恢复的实际操作持久化不等于备份这点要拎清楚。持久化解决的是“进程重启不丢数据”备份解决的是“磁盘坏了、误删了还能找回”。我习惯用这样一套备份动作mkdir -p /backup/redis docker exec redis-server redis-cli -a 你的强密码 BGSAVE cp /data/redis/data/dump.rdb /backup/redis/dump-$(date %F).rdb cp /data/redis/data/appendonly.aof /backup/redis/appendonly-$(date %F).aofBGSAVE是让Redis在后台生成一份最新的RDB快照生成完再复制文件确保备份的是最新状态。恢复则反过来把备份的dump.rdb或appendonly.aof放回/data/redis/data/目录改成对应的文件名再执行chown -R 999:999 /data/redis/data docker restart redis-server这里有个容易翻车的细节Redis启动时如果同时发现了dump.rdb和appendonly.aof默认会优先加载AOF文件。所以如果你从旧备份只恢复了RDB但data/目录里还残留着一份更早的AOF文件那Redis加载的可能不是你以为的那份数据。恢复时要么把文件放干净、要么干脆把data/目录整个清空后只放你要恢复的那一份避免混着用。5. 连接验证命令行、可视化工具和安全组部署完了肯定要连上去看一眼。连接验证分几个层面我逐个说。5.1 服务器本机验证在宿主机上执行docker exec -it redis-server redis-cli -a 你的强密码 ping返回PONG说明容器内部的服务和认证都正常。这一步只能证明Redis进程活着不能证明端口映射正确。5.2 外部工具连接我最常用的图形化工具是Another Redis Desktop Manager轻量、跨平台、免费和Redis Desktop Manager。两者连接参数基本一样地址你的云服务器公网IP端口6379密码就是requirepass设置的那个如果是在本地电脑上跑工具连不上一般就两个原因一是云服务商的安全组没放行6379端口二是服务器系统防火墙没放行。阿里云、腾讯云、华为云这些平台除了服务器自己的防火墙规则还有一个“安全组”层级的管控也必须配置。两个地方都要放行。Linux服务器通常还开着ufw或firewalld。以ufw为例最小授权方式是只放行你的办公IPufw allow from 你的办公公网IP to any port 6379 proto tcp而不是简单粗暴地ufw allow 6379后者会让整个互联网都能扫到你的Redis端口。5.3 常见连接失败排查表现象原因解决办法NOAUTH Authentication required.服务端已设置密码请求未通过认证连接时填上密码Connection refused端口没映射或者服务没起来docker ps看容器状态确认-p 6379:6379连接超时安全组或防火墙拦截检查安全组入方向、ufw status、iptables -LDenied Redis protected-modeRedis没设密码但允许了外部访问设置requirepass或把protected-mode yes保持开启这个表基本覆盖了我帮别人排查问题时报错的前五名按表格从上往下排查大概率能定位。6. 用Docker Compose把部署参数固化下来docker run命令写一次还好写两次、三次你一定会怀念“一份文件搞定部署”的感觉。Docker Compose就是干这个的把镜像、端口、数据卷、启动命令全部写进一个YAML文件里版本化管理换机器重新部署也就一条命令的事。在/data/redis/目录下新建docker-compose.ymlservices: redis: image: redis:7-alpine container_name: redis-server restart: unless-stopped ports: - 6379:6379 volumes: - /data/redis/config/redis.conf:/usr/local/etc/redis/redis.conf - /data/redis/data:/data command: [redis-server, /usr/local/etc/redis/redis.conf]简单解释一下这个文件的映射关系image: redis:7-alpine镜像来源。restart: unless-stopped开机自启策略和docker run里的--restart一致。ports端口映射注意YAML里的字符串写法6379:6379不加引号有些解析器会当成数字处理。volumes两个挂载一个是配置文件一个是数据目录。command覆盖默认启动命令加载我们挂载进去的配置。启动和日常操作命令汇总docker compose up -d # 启动/重建容器 docker compose ps # 查看容器状态 docker compose logs -f redis # 跟踪日志 docker compose restart redis # 重启服务 docker compose exec redis redis-cli -a 你的强密码 ping # 进容器执行命令 docker compose down # 停止并删除容器注意不会删宿主机的挂载目录用Compose之后容器删除和重建变得非常“廉价”——因为配置和数据都在宿主机上docker compose down再docker compose up -d几秒钟就起来了数据一点不少。有个警告必须写清楚执行docker compose down -v时Compose会把声明为volumes的命名卷一并删除。上面那个文件里用的全是宿主机路径挂载bind mount所以即使带-v也不会删掉宿主机目录里的数据。但如果你哪天改用命名卷了down -v就是真正的“粉碎性删除”手滑一次数据全没了。所以我的习惯是线上环境一律用宿主机路径挂载备份、恢复都直观Compose操作也更安全。7. 线上运维的几个小提醒部署完成只是开始Redis跑在服务器上之后还有几件事值得花几分钟做掉。7.1 看日志容器日志查看方式docker logs -f redis-serverRedis的启动信息、AOF文件报错、客户端连接断开等信息都会打在这里。之前提到的Permission denied就是从这个日志里看到的。7.2 限制容器日志大小Docker默认不限制容器日志大小长时间运行日志文件可能膨胀到几个GB占满磁盘分区。可以在/etc/docker/daemon.json里加一段{ log-driver: json-file, log-opts: { max-size: 20m, max-file: 3 } }然后重启Dockersystemctl restart docker注意这个配置是全局的重启后新创建的容器默认都受它约束老容器要重建后才会生效。7.3 升级Redis版本要备份先行Redis大版本升级前先从配置文件里确认当前的dump.rdb和AOF文件格式与新版是否兼容。稳妥流程是docker exec redis-server redis-cli -a 你的强密码 BGSAVE cp -r /data/redis/data /backup/redis/before-upgrade-$(date %F)然后修改docker-compose.yml里的image版本再执行docker compose pull docker compose up -d升级后第一时间做“写读验证”写一个测试key重启容器确认数据还在。我自己的体会是Redis部署这件事本身不难难的是把“密码访问”和“数据持久化”这两件事从“好像设置了”变成“确定生效了”。每一次部署完我都会用三个动作验收第一redis-cli不带密码时是否被NOAUTH拦掉第二写一个key重启容器查一下是否还在第三确认云服务商安全组只放行了自己的办公IP而不是0.0.0.0/0。这三件事过了心里才踏实。