ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CentOS 7.6上Docker部署Redis带密码与持久化完整指南

CentOS 7.6上Docker部署Redis带密码与持久化完整指南 最近处理 Centos7.6 上用 Docker 安装 Redis 的事务来找我咨询的人十个里有八个都卡在同一个点上密码怎么才能生效、容器重启之后数据还在不在。这两个问题其实就是标题里说的“带密码 持久化”。今天我把完整过程拆开讲一遍从为什么这么做到每条命令的参数含义再到我实际跑过的坑一次性讲透。这套方案在我自己的测试环境里完整验证过线上也已经稳定跑了两周。适合第一次在 Centos 上用 Docker 起 Redis 的同学也建议老手回头检查一遍自己的配置有没有留下隐患。1. 安装前先捋清楚为什么要上 Docker密码和持久化各自解决什么问题1.1 直接 yum 装 Redis 不香吗大家都清楚Centos7.6 自带源里的 Redis 版本普遍很老默认源长期停留在 3.2.x 左右连官方自己都已经把维护重心放到了 6.x、7.x 上。直接 yum 安装除了版本老还面临一个很现实的问题安装文件散落在系统各个目录配置文件、日志、数据文件分别放在不同位置以后想升级、想迁移、想做多实例都相当于在一台机器上做大手术。Docker 的容器化在这里解决的是“环境隔离”问题。Redis 跑在容器里宿主机不用安装任何 Redis 相关依赖不污染系统环境想升级就换一个新镜像重建容器想回滚就切回旧镜像整个操作是可控、可逆的。对新手来说Docker 命令虽然多但套路非常固定比在一台机器上反复折腾各种动态链接库和系统服务要省心得多。另外还有一点很关键用 Docker 部署Redis 的进程生命周期和容器绑定日志直接输出到标准输出配合docker logs就能看到全部启动信息。出了问题排错路径简单直接不用去/var/log下面翻各种碎片文件。1.2 “带密码”和“持久化”在容器场景里不是可选项Redis 默认不设置密码同时只监听回环地址127.0.0.1。你在本机玩没问题可一旦通过端口映射暴露出去公网扫描器扫到 6379 端口绝大部分都会尝试用无认证方式写入定时任务或者恶意脚本。前几年大规模的 Redis 被黑事件核心原因就是“无密码 暴露公网”。这不是危言耸听我一直建议哪怕是内网测试环境也要带上密码因为内网同样可能有其他漏洞利用 Redis 做跳板。持久化的问题更贴近容器特性。Docker 容器一旦被删除或者重建容器内写入的所有数据都会消失除非你在启动时做了目录挂载。如果你只是docker run -p 6379:6379 redis完事那么每次重建容器你都得到一个全新的空 Redis。持久化本质上要解决两件事一是 Redis 进程重启后数据能恢复二是容器被删掉后只要数据文件还在就能用旧目录重新挂载启动数据照样能找回来。一句话总结不带密码的 Redis 是在裸奔不做持久化的 Redis 是失忆这两个问题都不应该出现在任何正经环境里。2. 开工前准备镜像版本、配置文件、目录权限一次备齐2.1 Redis 镜像选哪个版本比较稳Docker Hub 上官方redis仓库的版本线很多6.2、7.0、7.2、7.4 都有。我的建议是优先选 7.0 或 7.2 这类稳定版本尽量不要直接追latest因为latest哪天就悄悄变成下一个大版本行为变化可能让你线上环境措手不及。过旧的 6.2 也不是不行但 7.x 在 ACL、持久化、性能上都有不少优化而且绝大多数场景下完全兼容老客户端。拉取命令很简单docker pull redis:7.2如果你的机器拉取镜像比较慢国内常规做法是给 Docker 配置镜像加速器这里不展开属于基础操作。拿到镜像之后可以用docker images确认一下本地是否已经存在redis:7.2。2.2 手写 redis.conf真正决定命运的 8 个参数有些教程喜欢让你不准备配置文件直接docker run redis redis-server --requirepass xxx --appendonly yes数据也能跑起来。但我个人不推荐这种做法参数全部堆在命令行里看着方便后面想复查、想调整、想对齐生产环境根本没有一个统一的配置文件可以管理。我在宿主机/opt/redis/conf/redis.conf里放了下面这份精简配置下面逐个解释daemonize no bind 0.0.0.0 protected-mode yes port 6379 timeout 0 tcp-keepalive 300 # 密码 requirepass 你的强密码 # 持久化 appendonly yes appendfilename appendonly.aof appendfsync everysec save 900 1 save 300 10 save 60 10000 dir /data logfile daemonize no是第一个关键点。Docker 容器要求 Redis 以前台进程方式运行如果这里按照传统直觉写成daemonize yesRedis 启动后会立刻后台化容器发现主进程退出直接判定容器挂掉你会看到容器不断重启。bind 0.0.0.0解决的是访问范围问题。Redis 默认只绑定127.0.0.1容器内部没问题但宿主机从外面访问会失败。绑定所有网卡配合-p 6379:6379端口映射才能把 Redis 服务真正暴露给外部客户端。protected-mode yes配合requirepass使用本身并不冲突。很多人一遇到外连失败就顺手改成protected-mode no其实配置了强密码之后yes是更安全的选择。保护模式只在“没有密码 没有绑定受信地址”的情况下才会拒绝远程访问。appendonly yes / appendfilename / dir / appendfsync是一整套 AOF 持久化配置。dir /data特别重要因为容器里的/data目录会和宿主机数据目录做挂载RDB 和 AOF 文件都会写到这个位置你才能从宿主机直接看到数据文件。save 900 1 / save 300 10 / save 60 10000是 RDB 快照触发条件默认值就够用和 AOF 同时开启可以兼顾恢复速度和数据完整性。logfile 表示日志输出到标准输出这样docker logs redis能看到所有运行日志。如果不设置这一行Redis 默认把日志写到一个本地文件里容器日志里看不到任何输出排错会特别难受。2.3 宿主机的目录规划和权限别看轻它如果你的主目录还没有规划过 Redis 的数据位置建议按下面这样创建mkdir -p /opt/redis/conf mkdir -p /opt/redis/data cp redis.conf /opt/redis/conf/redis.conf chown -R 999:999 /opt/redis/data最后这个chown -R 999:999是关键中的关键。官方 Redis 镜像内部不是用 root 运行的它创建了一个 uid 为 999 的 redis 用户。当你把宿主机/opt/redis/data挂载进容器时如果目录权限是默认的root:root容器内的 Redis 进程向这个目录写入 AOF 或 RDB 文件时就会遇到 Permission denied轻则持久化失败重则容器无法正常启动。如果你的系统开启了 SELinux还有一个额外注意点挂载目录可能需要修正上下文否则容器内拿不到读权限chcon -Rt svirt_sandbox_file_t /opt/redis/data没开 SELinux 可以忽略这句如果开着又没处理你会看到各种莫名其妙的权限报错。与其到时候人肉猜原因不如现在就补上。3. 启动服务docker run 和 docker compose 两条路建议直接抄作业3.1 一屏看懂 docker run 启动命令命令行方式是最快能跑通的方式适合临时验证docker run -d \ --name redis \ --restart unless-stopped \ -p 6379:6379 \ -v /opt/redis/conf/redis.conf:/etc/redis/redis.conf:ro \ -v /opt/redis/data:/data \ redis:7.2 \ redis-server /etc/redis/redis.conf逐项解释一下-d后台运行容器。--name redis容器命名后面docker logs redis、docker exec -it redis ...都靠这个名称定位。--restart unless-stopped宿主机重启后容器自动拉起除非你手动docker stop。对 Redis 这种需要常驻的服务来说这个参数几乎是标配。-p 6379:6379把宿主机 6379 端口映射到容器 6379 端口。左边是宿主机端口右边是容器端口。如果你的宿主机端口被占用了可以改左边比如-p 16379:6379但客户端连接时需要留意端口变化。-v /opt/redis/conf/redis.conf:/etc/redis/redis.conf:ro把宿主机配置文件挂载到容器内:ro表示只读防止在容器里误改配置。-v /opt/redis/data:/data把宿主机数据目录挂载到容器内的/data这是持久化的物理基础。最后redis-server /etc/redis/redis.conf告诉 Redis 启动时加载哪个配置文件。很多人漏了这一句配置文件挂载了但完全没生效Redis 仍然以默认参数启动密码和 AOF 都没起来。另一种快速写法是不用 redis.conf直接在命令行传参docker run -d --name redis -p 6379:6379 -v /opt/redis/data:/data redis:7.2 redis-server --requirepass 你的强密码 --appendonly yes这种适合验证但不适合长期用因为参数暴露在 shell 历史里而且不好维护。3.2 更易维护的 docker-compose 方案如果打算把这套部署长期保留或者未来可能要扩展主从、哨兵我建议一步到位用 Docker Compose。在/opt/redis下创建docker-compose.ymlservices: redis: image: redis:7.2 container_name: redis restart: unless-stopped ports: - 6379:6379 volumes: - /opt/redis/conf/redis.conf:/etc/redis/redis.conf:ro - /opt/redis/data:/data command: [redis-server, /etc/redis/redis.conf]然后在同一目录执行docker compose up -dCompose 的好处一句话就能说清楚所有定义都在一个 YAML 文件里以后要加一个 Redis 从节点只需在这个文件里再写一个 service共用同一份配置模板然后重新docker compose up -d整套服务一起拉起不用在脑海里去记一大串 docker run 参数。3.3 完整验证流程密码、持久化、重启一个都不能跳过容器启动后先看状态docker ps | grep redis docker logs redis如果容器是 Up 状态日志也没有Cant open the append-only file或Cant open config file之类的报错说明基础启动没问题。接下来验证密码。进入容器交互模式docker exec -it redis redis-cli这时直接执行ping或者keys *应该会得到(error) NOAUTH Authentication required.然后执行AUTH 错误密码会得到ERR invalid password执行AUTH 正确密码返回OK再执行ping才能拿到PONG。你也可以用一行命令快速验证docker exec -it redis redis-cli -a 你的强密码 ping这样会输出PONG。注意 Redis CLI 会给一个警告Using a password with -a option on the command line interface may not be safe.临时验证无所谓但别在自动化脚本里养成习惯。验证持久化是最容易忽略的一步。往 Redis 里写一条数据然后重启容器再读出来docker exec -it redis redis-cli -a 你的强密码 set hello world docker restart redis docker exec -it redis redis-cli -a 你的强密码 get hello如果重启后能拿到world说明 AOF 或者 RDB 已经成功生效。最后再确认一下数据文件确实落在宿主机目录ls -lh /opt/redis/data正常情况下你能看到appendonly.aof触发过 RDB 之后还会出现dump.rdb。4. 持久化与密码的底层逻辑配置只是表原理才是里子4.1 RDB、AOF、混合持久化怎么选很多人在持久化这一步都处理得很浅只知道要开appendonly yes却没搞懂背后的机制。简单说吧RDB是周期性把内存里的全量数据压缩生成一份快照文件dump.rdb。优点是文件小、恢复速度快适合做冷备缺点是快照之间的数据无法覆盖比如 900 秒以内只有 1 次写入这 1 次写入不会立刻落盘Redis 真崩了这 900 秒内积累的数据可能全丢。AOF是把每条写命令追加到日志文件重启时通过重放命令来恢复数据。优点是数据更完整只要你把刷盘策略调得够勤损失窗口可以控制在秒级甚至命令级缺点是文件比 RDB 大得多恢复时需要重放速度相对慢日志文件长时间不重写会膨胀。混合持久化是 Redis 4.0 之后引入的方案AOF 文件会先用一个 RDB 格式的快照做基础段后续再追加增量写命令相当于两头好处都占上。Redis 7.x 默认开启所以我上面配置里只写了appendonly yes没有额外关闭aof-use-rdb-preamble保持默认即可。在实际使用中如果 Redis 只用来做缓存RDB 就够了如果 Redis 里存了业务数据至少要开 AOF最好保持 RDB AOF 同时开启。两者同时存在时Redis 重启会优先加载 AOF因为它的数据状态更接近最终时刻。4.2 生产级持久化参数这样配我在生产环境比较喜欢用下面这一组参数参数推荐值说明appendonlyyes开启 AOF 持久化appendfsynceverysec每秒刷盘一次最坏丢 1 秒数据always 每条命令刷盘性能下降明显no 完全交给系统刷盘可能丢更多no-appendfsync-on-rewriteyesAOF 重写期间不执行 fsync避免阻塞主进程auto-aof-rewrite-percentage100AOF 文件比上次重写后增长一倍时自动触发重写auto-aof-rewrite-min-size64mbAOF 文件小于 64MB 时不做自动重写save 900 1 / save 300 10 / save 60 10000默认即可RDB 触发条件满足任一条件就生成快照appendfsync everysec是我最常用的折中方案它意味着 Redis 每秒钟显式调用一次刷盘。要理解这里的取舍always是每次写命令都刷盘最安全但高并发下性能下降非常明显everysec最坏情况丢最后一秒数据性能影响小得多no是交给操作系统自己决定何时落盘理论上丢的数据可能更多但性能最好。auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb配合起来可以防止 AOF 无限制膨胀。当 AOF 文件超过 64MB 且相对上次重写时增长了一倍后台会自动做一次重写把历史命令全区重写成紧凑格式。save参数我一般保留默认理由是 RDB 快照文件在冷备场景里非常好用RDB 生成的文件可以直接拷贝走配合 AOF 日志文件一起备份不管是恢复到新机器还是排查历史数据都有据可查。4.3 密码安全实践与三个误区密码这部分我见过太多反面教材。第一个常见误区是觉得protected-mode no是必须的其实配置了requirepass之后保留protected-mode yes完全没问题反而更安全。protected-mode要解决的问题是无密码暴露你已经设置密码了它就不会拦截你。第二个误区是把密码直接写进docker run命令行参数里。只要在 shell 里执行过命令几乎都会留在 shell 历史记录中道理和你在终端贴数据库密码是一样的肉眼可见的泄露。我倾向于把密码写进redis.conf然后执行chmod 600 redis.conf让只有 root 能读取。第三个误区是忽略密码中的特殊字符。如果密码包含$、空格、、#在命令行里直接传入几乎必然被 shell 解释。比如--requirepass pass123看起来没问题但有些环境下、$会被当成变量或特殊符号导致实际生效的密码和你以为的完全不一样。稳妥做法是把密码放到配置文件里连接时用单引号包起来在连接串里做好 URL 编码。Redis 6.0 之后还支持 ACL 多用户权限体系。如果你只是单机内部使用requirepass足够如果 Redis 要面向多个应用团队提供服务建议认真了解一下 ACL给每个应用创建独立账号分别分配权限比一把万能钥匙要安全得多。5. 实战踩坑记录这些错误我全帮你跑过一遍了5.1 容器启动后秒退日志里找答案如果你执行docker run之后发现容器一直在 restart最典型的原因有三个第一个原因是daemonize yes。Redis 以前台方式启动后立刻转后台容器认为主进程退出就反复重启。改成daemonize no即可。第二个原因是配置文件没被正确加载。比如你在挂载时用了-v /opt/redis/conf/redis.conf:/etc/redis/redis.conf:ro但redis-server命令后面没有写这个文件路径Redis 根本不知道去读它。检查方法docker inspect redis | grep -A 10 Mounts确认宿主机文件确实挂载到了容器内。第三个原因是数据目录权限不对。容器里的 redis 用户是 uid 999如果/opt/redis/data不能被写Redis 启动时写 AOF/RDB 失败可能直接退出。用chown -R 999:999 /opt/redis/data解决。排查问题时第一时间看日志docker logs redis绝大部分原因都会直接打在日志里别急着瞎改配置先看报错。5.2 外部工具连不上 Redis本机用redis-cli连没问题外部工具却提示Connection refused不要急着怀疑密码。先从这几个方向排查docker ps | grep redis netstat -tunlp | grep 6379 firewall-cmd --list-ports如果容器没在运行一切白搭。如果容器在运行但netstat看不到 6379 监听检查端口映射如果防火墙没有放行 6379放行一下firewall-cmd --permanent --add-port6379/tcp firewall-cmd --reload还有一种特别隐蔽的情况Redis 配置里写了bind 127.0.0.1容器内看到的网络和宿主机不一样外部访问会被 Redis 拒绝。解决方案就是在 redis.conf 里改成bind 0.0.0.0重启容器。如果你用的是云服务器还要去安全组控制台确认 6379 端口已经放行。RedisDesktopManager 这类可视化工具连接不上除了上述网络问题还要重点检查密码里有没有特殊字符。输入框里填的密码必须和实际配置完全一致特殊字符不要被客户端转义掉。5.3 持久化文件不生成AOF 没动静我遇到过一种情况明明挂载了配置文件AOF 文件就是一直不出现。一步步查下来发现redis-server启动时根本没有加载我挂载的配置Redis 还是按默认配置跑的。验证办法docker exec -it redis redis-cli CONFIG GET appendonly如果输出是appendonly no说明你挂载的配置文件没生效。原因通常是redis-server命令后面漏写了配置文件路径或者挂载到容器内的路径和命令里写的路径不一致。还有一种可能是dir配置不对。如果你在配置文件里写死了dir /data但挂载的宿主机目录不是/dataRedis 会把文件写到容器内部的文件系统数据文件不会出现在宿主机目录里。检查一下CONFIG GET dir的输出。AOF 文件启用的前提是 Redis 里至少发生了一次写操作。如果你启动后一直只做读操作AOF 文件可能还是空的或压根没创建这不算问题先set一条数据再观察。5.4 密码明明设了客户端还是出现 NOAUTH这种情况一般出现在刚改完配置的时候。你编辑了 redis.conf 里的requirepass容器没有重启运行中的 Redis 进程仍然保留旧配置自然不生效。记得改完配置后执行docker restart redis重启后再验证一次docker exec -it redis redis-cli -a 你的强密码 ping如果已经配置了密码但客户端报NOAUTH Authentication required说明客户端还没有在连接中执行AUTH命令。有些连接池需要设置密码字段有些需要在 URL 里带密码具体看你的客户端类型。另外要留意 Redis 6.0 的 ACL 系统。如果你见过配置文件里出现user开头的指令那是在为特定用户分配权限和requirepass不是一回事。排查时可以用docker exec -it redis redis-cli ACL LIST看看当前有哪些用户、每个用户的权限状态。5.5 常见问题速查表症状可能原因处理方式容器一直重启redis.conf 里 daemonize yes改为 daemonize no重启容器日志报 Cant open config file配置文件路径不对或挂载失败检查 -v 映射和 redis-server 参数路径外部 telnet 6379 不通防火墙/安全组/端口映射未放行依次检查 firewall-cmd、云安全组、docker ps本机能连外部无法连接bind 127.0.0.1 限制了网卡conf 中改 bind 0.0.0.0重启容器AOF 文件不存在dir 配置错误、目录无权限、没产生写操作检查 CONFIG GET dirchown 999:999先写入数据密码修改后不生效配置加载路径错误或没重启容器检查 CONFIG GET requirepass重启容器客户端连接被拒绝Redis 未启动/端口映射丢失docker ps 确认容器状态大量短连接失败tcp-backlog 太小conf 里调 tcp-backlog 511同时调整内核 somaxconn额外提醒一个很常见的坑如果宿主机原本就装了别的 Redis 或者 MySQL 恰好占了 6379 端口docker run会直接报端口冲突。启动前先netstat -tunlp | grep 6379确认端口干净。最后说点实在的。这套部署流程真正执行起来不到二十分钟但踩坑的时间加起来远远不止。建议你装完之后专门花五分钟做一次完整的重启验证改配置、重启容器、再确认数据还在。等到真要上线的时候你大概率会感谢这五分钟。另外/opt/redis/data目录要记得定期备份哪怕只是每天把dump.rdb和appendonly.aof拷贝到另一台机器也比 Redis 故障后一点数据兜底都没有强得多。我在实际运维中还会额外开一个极简容器定期执行BGSAVE然后把 RDB 文件同步到备份机这套组合将来扩展主从、哨兵架构时也完全复用得上。基础打扎实了后面加节点都是复制粘贴的事。
返回列表