
很多朋友第一次接触docker安装redis以为就是一行docker run redis的功夫。实际动手才发现坑一个接一个Docker Desktop启动失败、容器起来了连不上、重启后数据全没了。这篇我把自己从零折腾到能稳定跑主从复制的完整过程写下来包括每个报错的排查思路和绕坑姿势希望能让你少走几个来回。整个流程我分成环境准备、单机部署、网络排查、进阶配置、日常运维五段来讲从能跑到好用按这个顺序走就顺了。1. 为什么我最后选定用Docker装Redis而不是直接装先说个身边很常见的场景团队里五个人A的电脑是MacB是WindowsC是Linux服务器每个人本地Redis版本还不一样。A测了半天的SET key value到B那儿因为语法差异直接报错。这种环境不一致的痛用Docker装Redis能一次性解决。Docker里的Redis跑在独立容器中和宿主机完全隔离。镜像自带完整的运行环境拿到哪台机器上跑行为都一致。这意味着版本可控想用Redis 7还是5改一下镜像标签就行不影响宿主机其他应用环境隔离不会污染系统依赖也不怕和其他软件抢端口清理干净不要了直接docker rm -f不留残留文件Redis这种无状态缓存服务用Docker跑尤其合适。它不像数据库那样对磁盘IO极其敏感跟MySQL比挂载数据卷之后性能损耗很小本机开发测试几乎无感。生产环境我已经在中等QPS的业务上跑了两年多的容器化Redis稳定得很。另一个很现实的原因是Docker化的Redis不仅是运行环境还是团队协作的一种规范形式。一份docker-compose.yml提交到仓库新同事拉下来一条命令就能得到和你完全一致的Redis环境这比任何README里的安装步骤都有效。之前我们团队有位同事在Windows上照着文档折腾安装包整个下午花在了编译依赖上后来换成Docker方案十分钟就跑通了。2. Docker Desktop安装现场最容易卡住的“虚拟化检测”装Docker这事儿Mac用户相对省心直接官网下Docker Desktop拖进Applications就行。Windows用户就绕不开一个经典报错virtualization support not detectedDocker Desktop白屏起不来。2.1 WSL 2到底是不是必须的Docker Desktop在Windows上依赖WSL 2作为后端。很多人在“启用WSL 2”这里卡住。这个报错出现时第一步不是重装Docker而是先确认Windows功能里这两项是否开启适用于Linux的Windows子系统虚拟机平台用管理员身份打开PowerShell执行dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart改完重启再装WSL 2内核wsl --update wsl --set-default-version 2这里要提醒一句BIOS里的Intel VT-x或AMD-V虚拟化技术也必须打开。不少人的Windows功能没问题但是CPU虚拟化没开启Docker照样报同样的错误。重启进BIOS开机按Del或F2找到Virtualization Technology把它设为Enabled。2.2 装好之后先用一条命令验证能看到Docker鲸鱼图标转起来了先别急着跑Redis打开终端验证守护进程真的可用docker version docker infoinfo里如果显示WARNING: No swap limit support是正常的不影响使用。但如果你看到Cannot connect to the Docker daemon说明Docker Desktop的后端没起来。这种情况在Windows上十有八九是WSL 2没设置好重新执行一遍wsl --shutdown再启动Docker试试。Mac上如果遇到Docker Desktop一直转圈看菜单栏图标点Restart一般都能解决。实在不行就pkill -f Docker强制退出再开。3. 从镜像到容器一条docker run拆开给你讲透环境通了开始正式部署。先拉取镜像docker pull redis:7.2-alpine这里我强烈建议选带alpine的标签镜像体积小基础系统干净攻击面也小。redis:latest虽然方便但版本滚动更新可能导致你之前写的命令行为变化我踩过一次CONFIG SET语法不兼容的坑之后就老老实实指定版本了。单机最简启动docker run -d --name redis-test -p 6379:6379 redis:7.2-alpine这条命令包含几个关键点-d后台运行不会占着终端--name redis-test给容器起名后面管理都靠它-p 6379:6379宿主机端口映射到容器端口左边是宿主机右边是容器注意6379:6379这种写法默认绑定宿主机所有网卡意味着其他人也能通过你的IP访问到。开发环境无所谓生产环境至少要改成127.0.0.1:6379:6379只允许本机访问。3.1 一台机器上跑多个Redis实例有时候你需要模拟主从或者多环境隔离一台机器跑多个实例比如用6380端口再开一个docker run -d --name redis-backup -p 6380:6379 redis:7.2-alpine容器名字必须唯一端口不能冲突这是两条硬性规则。多实例还有一个好处测试故障转移时可以随意启停其中一个而不影响其他的这在后面做主从切换演练时非常有用。我之前有过一次误操作把生产Redis容器重启了想改个配置忘了backup结果所有依赖缓存的服务都打到了数据库上好在有应急预案才没出大事。从那儿以后我只要动生产容器一定先确认自己操作的是哪个容器——docker ps里的容器名别起得太像redis-test和redis-prod这种一定要分清楚。3.2 验证容器里的Redis真的能用用docker ps查看运行状态然后进入容器内部操作docker exec -it redis-test redis-cli 127.0.0.1:6379 ping PONG 127.0.0.1:6379 set foo bar OK 127.0.0.1:6379 get foo bar能ping通、能读写说明Redis服务正常。4. 容器起来了却连不上网络问题的排查路径这是很多人第一次用Docker时遇到的经典问题容器内Redis一切正常但从宿主机或另一台机器上就是连不上。先分清楚访问源。在宿主机上用redis-cli -h 127.0.0.1 -p 6379连不上先看端口映射docker port redis-test # 输出6379/tcp - 0.0.0.0:6379如果这里输出正常说明端口映射没问题。接着看Redis是否只绑定了容器内部地址。Redis配置文件里有个bind参数默认是127.0.0.1容器里这就等于只让容器自己访问自己外面的流量进不来。用Docker启动时除非通过配置文件指定否则默认是允许外部访问的。排查时先看看是不是这个原因docker exec redis-test redis-cli CONFIG GET bind如果返回的是127.0.0.1就需要挂载自定义配置了。这时候就该引入数据卷把外部的redis.conf挂到容器里docker run -d \ --name redis-conf \ -p 6379:6379 \ -v /path/to/my/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.2-alpine \ redis-server /usr/local/etc/redis/redis.conf4.1 跨机器访问时的防火墙与网络模式如果你是从另一台物理机器连这个容器除了上面这些还要检查防火墙。Windows上Docker Desktop自动处理端口转发但Windows防火墙经常弹出是否允许Docker访问网络的提示如果手快点了取消外部访问就会失败。Linux上则要手动放开firewall-cmd --zonepublic --add-port6379/tcp --permanent firewall-cmd --reload云服务器还要记得在安全组规则里放行6379端口。4.2 容器间的互通不再依赖localhost实际项目里你的应用和Redis多半都容器化了。此时推荐用Docker自定义网络让容器间通过名字互访而不是走宿主机IP加映射端口docker network create mynet docker run -d --name redis-on-net --network mynet -p 6379:6379 redis:7.2-alpine docker run -d --name my-app --network mynet my-app-image这样my-app容器里直接连redis-on-net:6379就行跟DNS一样。网络流量走Docker内部的虚拟网络不经过宿主机协议栈性能更好也更安全。容器间的互访绕过了host配置是我们在实际业务中捣腾分布式部署时最常用的一套方式。刚开始我图省事所有容器都用--network host共享宿主机网络结果容器一多端口冲突和配置混乱问题就像滚雪球一样来了。后来统一改用自定义网络容器名即主机名配置简单多了。5. 进阶配置持久化、密码、主从复制一次到位开发阶段直接docker run没问题但只要考虑数据安全下面这三样就必须配齐。5.1 数据卷容器删了数据还在Redis容器一旦删除里面的所有数据都没了。要保数据必须挂载数据卷docker run -d \ --name redis-data \ -p 6379:6379 \ -v redis-data:/data \ redis:7.2-alpine \ redis-server --appendonly yes用命名卷redis-data比用路径映射更省心数据由Docker管理备份恢复都方便。--appendonly yes开启AOF持久化Redis会把每次写操作追加到文件里容器重建后重启Redis就能恢复数据。5.2 密码认证别让Redis裸奔Redis无密码裸奔被挖矿的案例在网上太多了。Docker容器最容易忽略的就是这个因为默认启动不带密码。设密码用--requirepass参数docker run -d \ --name redis-auth \ -p 6379:6379 \ -v redis-data:/data \ redis:7.2-alpine \ redis-server --appendonly yes --requirepass my-strong-password带密码后连接方式变成docker exec -it redis-auth redis-cli -a my-strong-password注意-a参数会让密码出现在终端历史记录里生产环境建议用REDISCLI_AUTH环境变量或者在容器内用redis-cli进入交互模式后执行AUTH命令输入密码。这条细节看起来小但在共享服务器上能避免不少安全风险。5.3 主从复制的完整配置配主从时记住一句话从库用--replicaof指定主库主库只负责写从库扛读流量。假设我们已经有了一个主节点redis-master跑在Docker网络mynet里。启动从节点docker run -d \ --name redis-replica \ --network mynet \ -p 6380:6379 \ -v redis-replica-data:/data \ redis:7.2-alpine \ redis-server --replicaof redis-master 6379 --appendonly yes进从库验证docker exec -it redis-replica redis-cli -a my-strong-password info replication # 输出里 role:slave 并且 master_link_status:up 就说明连上了在主库写数据从库立即能看到# 主库 docker exec -it redis-master redis-cli -a pass SET hello world # 从库 docker exec -it redis-replica redis-cli -a pass GET hello world这套配置解决了几个实际问题读写分离后应用的压力分散了为后面做哨兵高可用提前打好了基础。Redis的主从复制是异步的所以从库数据可能有轻微延迟对实时性要求高的场景要注意权衡。另外记住一点从库默认是只读的如果你尝试往从库写数据会报错这其实是Redis在保护你防止数据不一致。6. 生产环境下的容器管理日志、资源限制和升级策略最后说点日常运维的细节这些内容不会写进快速入门教程里但实际用起来个个都关键。6.1 查看Redis日志的正确方法Redis日志默认打到标准输出用docker logs查看docker logs redis-test docker logs -f redis-test # 实时跟踪 docker logs --tail 100 redis-test # 只看最后100行从日志里的MASTER - REPLICA sync相关行可以看到同步状态排查问题时这是最快的路径。嵌入的Redis如果打开了--logfile指定文件则不会打印到stdout此时docker logs会一片空白排查时容易误判建议保持默认。6.2 容器资源限制防止Redis吃掉整台机器内存没有限制的话容器可以使用宿主机全部资源。对于那些被配置了maxmemory的Redis实例如果你把maxmemory设得比内存还大一旦数据满了Redis开始频繁淘汰键性能急剧下降。建议启动时加上docker run -d \ --name redis-limited \ -p 6379:6379 \ -m 512m \ --memory-swap 512m \ --cpus 1.0 \ redis:7.2-alpine-m 512m限制内存为512MB--cpus 1.0限制最多使用1个CPU核心。Redis本身是单线程模型给再多核心也利用率不高。内存限制加上之后容器超过内存限制会被OOM Killer杀掉Redis进程退出但容器还活着处于异常状态docker ps看到的状态可能是Exited或Restarting此时务必看日志和docker inspect里的OOM标记来判断根源。6.3 平滑升级先拉新镜像再换容器Redis出了新版本想升级不要直接docker exec改配置正确姿势是先拉新镜像再重建容器docker pull redis:7.4-alpine docker stop redis-test docker rename redis-test redis-test-old docker run -d --name redis-test -p 6379:6379 -v redis-data:/data redis:7.4-alpine如果数据卷没变新容器启动后数据自动加载几乎没有感知。切换之前先用redis-cli -a pass BGSAVE触发一次持久化确保数据文件是最新的。生产环境我还会在容器添加--restartunless-stopped这样机器重启后Docker会自动拉起Redis省去手动干预。这个参数配合数据卷已经帮我扛过了好几次意外断电——服务器重启完Redis进程自动恢复业务端的缓存服务没有断档。6.4 容器挂了之后的第一步别急着restart最后想说个心态问题。容器异常退出后很多人第一反应是先docker start结果陷入“起来了又挂”的循环耽误排查。我先说我的习惯永远先看日志。docker logs --tail 50 redis-test然后看状态docker inspect redis-test --format {{.State.ExitCode}}退出码非零说明是异常退出再结合日志判断是OOM、配置错误还是端口冲突。配置错误是最容易发现也最容易避免的一类因为很多参数在启动那一刻就会报错日志里写得很清楚。端口被占用时docker run会直接报bind: address already in use这个很直接把占用端口的旧容器或进程处理掉就行。7. 日常工作中的一点体会把Docker跑起来不难难的是把Redis在容器里的运行规则摸透。这次梳理完启动流程和运维细节我自己收获最大的一个习惯是每次操作前想清楚三件事——数据要不要留数据卷挂了吗、别人能不能访问bind和requirepass配置了吗、进程挂了能不能自动拉起restart策略设了吗。有一次线上服务的缓存线程池被打满排查了半天发现是我们某次重建容器时忘了挂数据卷Redis冷启动后大量key同时回源直接把数据库打挂了。从那以后我把容器Redis的启动配置收拢到一份docker-compose文件里每次部署前先用它创建一个临时容器做读写验证通过后再切流量。这个习惯推荐给所有人。按这套流程走下来单实例、主从、高可用基本都稳了。redis的主从复制、数据持久化和连接工具我会之后再单独写几篇展开。你按这篇的顺序从环境准备开始一步步操作应该能少踩很多坑。