我不太确定别人是怎么部署 Redis 的但就我自己这几年的实操体会来说Docker 装 Redis 从来不是一个“docker run 一把梭”就能收工的事。Redis 5.0.12 这个版本在不少跑了几年的业务系统里依然占据主流它既有 5.0 系列稳定性上的收敛又没有 6.0、7.0 带来的一些兼容性迁移成本。这篇文章我会围绕 Docker Redis 5.0.12 这条主线从版本选型、环境检查、配置文件准备、容器启动到常见问题排查把一套能直接抄作业的详细步骤完整过一遍也会把我在实施过程中踩过的坑穿插在里面。无论你是第一次用 Docker 部署 Redis还是以前裸机安装过 Redis 现在想迁到容器环境这篇文章的思路和命令都能直接拿来用。1. 为什么是 Redis 5.0.12 Docker而不是追求最新版1.1 版本选型得看兼容性和稳定性不只是追新Redis 5.0 是一个典型的“承上启下”版本。它在 4.0 的基础上引入了 Stream 数据结构把消息队列这类场景从 List、Pub/Sub 的诸多限制中解放出来同时对外部模块的加载机制也做了重构。之后 6.0、7.0 虽然带来了客户端缓存、ACL 强化、多线程 IO 等新能力但如果你手头的业务代码还在用旧版客户端库或者运维体系里依赖着一套比较老的主从复制脚本直接跨大版本升级很容易遇到 Redis 协议细节变化带来的兼容性问题。5.0.12 是 5.0.x 这条分支上的最后一个修复版本。什么意思呢就是 5.0 系列早期版本里那些内存泄漏、主从切换异常、AOF 重写卡顿等常见问题在这个小版本上基本都补完了但又没有引入新版本带来的新行为。所以很多生产环境在“不敢乱动”的前提下会优先锁定这样一个次末代的稳定版。我之前处理过一个内部项目业务代码里大量使用了 Lua 脚本加 Stream 消费Redis 从 4.0 升到 5.0 的过程中基本没有感知但后来测试 6.2 时发现某些边界条件下客户端重连的日志行为不同排查成本一下就上来了。因此如果没有人明确要求新特性选择 5.0.12 这类已经经过大量场景验证的版本会省掉很多心智负担。1.2 Docker 部署比裸机部署到底强在哪很多人觉得 Docker 装 Redis 不就是把安装包换成了镜像其实差别很大。裸机部署 Redis 时你至少要关心系统里的依赖库、gcc 版本、编译参数、systemd 服务文件、日志轮转甚至还要管理 Redis 进程崩溃后的守护逻辑。Docker 把这些全部封装到镜像里你面对的接口只有一个镜像名 版本号。这意味着本地开发和服务器部署用的是完全一致的操作系统层、编译参数和运行目录极大减少“在我这是好的到你那就跑不起来”的环境差异。另外Docker 让资源管控变得明确。你可以通过--memory限制 Redis 容器内存防止单实例把宿主机内存吃满可以通过--cpuset-cpus做 CPU 绑定还可以通过独立的容器网络让 Redis 只对指定的应用容器可见避免直接暴露到公网。这种隔离级别是裸机很难快速做到的。再加上删除重建容器几乎零成本测试完一个配置方案后想回滚再起一个新的就行根本不会再看到环境残留的垃圾进程和配置文件。所以我认为只要是新部署的 Redis用 Docker 包装是优先级很高的方案除非你的场景强依赖 host 网络、或者有特殊内核模块需要直接加载否则 Docker 完全够用且更好维护。下表是我对裸机部署和容器部署几个维度的对比感受维度裸机Docker安装耗时编译安装十分钟起依赖多拉镜像几秒钟到几分钟环境一致性依赖基础镜像和系统版本镜像内系统层固定一致性高回滚手动改配置、重编或重装直接删容器换镜像资源限制依赖 systemd cgroup 手动配置启动参数直接限制端口和IP管理进程占端口网络栈暴露宽泛网络隔离灵活端口映射明确运维复杂度要维护进程守护、日志轮转容器生命周期托管配合日志驱动2. 环境检查与镜像拉取的几个细节2.1 先把 Docker 基础环境确认好任何部署动作之前我都会先做一遍最基础的环境检查。原因很简单很多后续异常根本不是 Redis 配置的问题而是 Docker 服务本身有问题。先执行docker -v看版本Red Hat 系发行版上 Docker 版本低于 19.03 的话我会建议先升级因为旧版本对--restart策略、健康检查等特性的支持有明显差异而且遇到容器异常退出时行为不够可靠。接着用docker info查看 Docker 服务状态和存储驱动。如果这里直接报错比如权限不足说明当前用户不在 docker 用户组里需要sudo usermod -aG docker 你的用户名然后重新登录如果显示Cannot connect to the Docker daemon多半是 Docker 服务没起来先执行systemctl start docker。还有一个我经常见的环境坑磁盘空间不足导致镜像拉取失败。Docker 默认的 root 目录可能和根分区共用空间而 Redis 镜像虽然不算大但 AOF 和 RDB 持久化数据增长很快因此我在部署前会再瞄一眼df -h确认/和/var/lib/docker所在分区有足够剩余空间。自己项目倒没什么生产环境空间被瞬时写满导致容器崩溃的例子我见得太多了。检查完基础环境后最好顺手执行一下docker ps看看现存活容器确认当前系统里有没有占用 6379 端口或者在跑其他 Redis 实例的残留容器。这一步虽然机械但能避免后面端口映射冲突时排查半天。2.2 镜像版本别拉错官方镜像也要防“latest”拉取 Redis 镜像我通常只认官方镜像命令是docker pull redis:5.0.12。这里有一个非常容易踩的问题如果只写docker pull redis实际上拉的是 latest 标签也就是官方仓库里最新的版本在将来可能是 7.x 甚至更高的版本这就和你写文档、做配置时参考的 5.0.12 行为完全对不上了。所以任何部署脚本和文档里镜像标签一定写全。除了默认的官方镜像还有一个选择是redis:5.0.12-alpine。Alpine 版本镜像体积非常小大概只有几十兆适合磁盘紧张或追求启动速度的轻量场景。但我个人在生产里用得不多原因有二一是 Alpine 用的 musl libc 和 Ubuntu/Debian 的 glibc 在某些极端底层调用上有差异部分第三方 Redis 模块如果是在 glibc 环境编译的跑在 Alpine 镜像中可能行为异常二是后来排查问题时Alpine 里精简掉的调试工具会带来不便。如果你不是对体积特别敏感优先用默认的 Debian 基础镜像版本。拉取完成后用docker images | grep redis确认镜像已经存在并仔细核对 REPOSITORY 和 TAG 两列分别是redis和5.0.12。如果你处于内网离线环境也可以提前在其他联网机器上把镜像导成 tar 包再通过docker load导入这里不多展开但思路是成立的。2.3 镜像加速器的配置与网络侧问题如果你发现镜像拉取速度非常慢或者一直超时先确认网络链路本身没问题然后考虑 Docker 的镜像加速配置。这一步不涉及任何非常规网络操作就是给 Docker daemon 配一个镜像加速地址让拉取请求走更快的通道。配置方法在/etc/docker/daemon.json中加一段{ registry-mirrors: [https://你的加速地址] }改完先执行systemctl daemon-reload再systemctl restart docker。重新拉取时注意看拉取进度是否明显加快。这里要特别提醒不同时期、不同地区的加速地址效果差异很大如果你的网络环境访问官方仓库本身没问题那就不需要配置这个配置了反而可能因为加速器同步延迟而拉到一个缓存旧版本。每次拉取的关键操作是用完好的网络与最少变量去完成其它东西能不动就不动。3. 先把 redis.conf 准备到位再去启动容器3.1 宿主机目录规划conf、data、logs 各司其职容器跑起来容易但要让 Redis 的数据和日志在容器销毁后依然保留就必须把持久化目录挂载出来。我一般在宿主机上建立三个独立目录mkdir -p /opt/redis/conf mkdir -p /opt/redis/data mkdir -p /opt/redis/logs有人会图省事只挂载 data 目录配置文件直接写进镜像里或者每次启动时通过命令参数传入。这样短期没问题但时间一长别人看到容器里的配置时根本不知道这配置是从哪来的也无法直接版本化。所以我建议 conf 目录单独放一份 redis.conf管理上完全透明修改后重启容器就能生效。logs 目录挂载出来的价值主要在于排查问题Redis 容器本身默认会把日志打到标准输出用docker logs能看到一部分但如果出了问题容器频繁重启日志被冲掉的风险比较大直接落盘到宿主机目录更稳定。目录权限在这里也要提前想好。Redis 容器内一般以redis用户运行数据目录如果挂载出来宿主机目录权限太严可能导致 Redis 无法写 RDB 或 AOF 文件。实际操作中如果遇到权限报错直接执行chown -R 999:999 /opt/redis/data /opt/redis/logs能解决大部分情况因为很多官方镜像里 redis 用户的 UID 是 999。当然更严谨的做法是先 exec 进容器里看一眼/etc/passwd再按实际 UID 授权。3.2 从容器里抄一份官方配置再动刀精简很多初学者会到处找 redis.conf 下载其实最快也最权威的方式是从镜像里直接拷一份出来。先临时起一个容器docker run --name redis-temp -d redis:5.0.12 docker cp redis-temp:/etc/redis/redis.conf /opt/redis/conf/redis.conf docker rm -f redis-temp拷贝完成后先别急着启动因为官方原版配置默认情况下跑起来虽然能用却不一定适合容器场景和生产需求。我一般会对这份配置做几处关键调整。先看这个我这个常用的最小化版本# 监听端口 port 6379 # 非常重要不要后台化否则容器会立即退出 daemonize no # 关闭保护模式便于容器网络访问 protected-mode no # 默认监听所有网卡配合端口映射使用 bind 0.0.0.0 # 访问密码生产环境必须开 requirepass YourStrongPassword # 日志文件路径留空表示输出到 stdout同时可以配合容器日志驱动 logfile # 数据持久化目录 dir /data # RDB 快照规则 save 900 1 save 300 10 save 60 10000 # 开启 AOF 持久化 appendonly yes appendfilename appendonly.aof appendfsync everysec # 最大内存限制和淘汰策略 # maxmemory 256mb # maxmemory-policy allkeys-lru这个配置里的daemonize no是整个容器方案里最核心的一行。我专门把这一条拉到最前面说是因为 90% 用 Docker 跑 Redis 失败的新手都栽在这。Redis 默认配置里daemonize yes会让进程进入后台运行但 Docker 容器的根进程一旦退出整个容器就被判定为停止了。换句话说如果 Redis 在容器内自己后台化了容器立刻变成 Exited 状态看起来就像刚启动就挂了。所以容器里必须前台运行redis-server要一直占着 PID 1 的位置。protected-mode这一项也要注意。默认值 yes 意味着如果 Redis 没有密码且监听了所有网卡它会拒绝外部非本机连接。我们用 Docker 时通常要让其它容器或宿主机访问 Redis所以会显式设置bind 0.0.0.0加protected-mode no依靠端口映射来控制暴露范围。如果你同时设置了密码其实保持protected-mode yes也是可以的但配置判断逻辑多了以后反而容易误判我习惯直接关掉保护模式然后靠密码守护。3.3 密码、持久化和淘汰策略的取舍思路requirepass这一项建议直接启用哪怕只是在测试环境。因为 Docker 端口映射一旦发布到宿主机Redis 就处在了局域网甚至公网可达的位置如果没有密码等于把一台裸奔的服务丢在外面。Redis 官方也一直强调要通过防火墙和密码双重保护。密码设置我一般用强随机字符串避免用 123456 这类弱口令然后通过环境变量或挂载配置文件传入容器不要把密码硬编码到 docker run 的进程列表里因为那会出现在 shell history 和进程快照里。持久化方面我倾向于appendonly yes和 RDB 同时开启。AOF 对数据完整性的保障更强恢复粒度更细RDB 则更适合做快速备份和冷备归档。虽然两种都开会有一定的磁盘写放大但 Redis 5.0 的 AOF 重写逻辑已经比较成熟日常负载下影响不大。也许有人觉得只在内存里用一段时间不需要持久化那你可以关掉appendonly甚至把save规则全部注释不过这属于临时缓存用法和真正承担业务数据存储的场景是不同的运维思路。淘汰策略属于一个“先配置好、未必立刻触发”的参数。如果 Redis 被当成缓存且内存上限固定可以设maxmemory 256mb和maxmemory-policy allkeys-lru让 Redis 在内存将满时自动淘汰最久未使用的 key。如果被当成数据库或消息中间件来用默认不设上限也没问题但系统层面需要配合监控防止内存被打爆。关键是要提前想清楚这一套 Redis 的使用定位而不是盲目加参数。4. 启动容器docker run 全参数逐个拆解4.1 一条经过实践检验的启动命令配置文件准备好后启动命令如下docker run -d \ --name redis-5.0.12 \ -p 6379:6379 \ -v /opt/redis/conf/redis.conf:/etc/redis/redis.conf \ -v /opt/redis/data:/data \ -v /opt/redis/logs:/logs \ --restartalways \ redis:5.0.12 \ redis-server /etc/redis/redis.conf我拆开讲一下这些参数背后的逻辑。-d表示后台运行容器这个和daemonize no完全不冲突因为-d是让容器后台运行而 Redis 进程本身依然作为前台进程承载整个容器生命周期。--name redis-5.0.12给容器起一个有意义的名字后续docker exec、docker logs、docker stop都不用记一串随机 ID。-p 6379:6379是端口映射宿主机 6379 端口转发到容器内 6379 端口。这里左右两边的端口可以不相同比如宿主上用 6380可以写成-p 6380:6379但客户端连接地址就要改成 6380。需要提醒的是-p默认绑定0.0.0.0也就是宿主机所有网卡接口都对外开放如果只希望内网访问建议写成-p 127.0.0.1:6379:6379只绑定本机回环地址这样其它机器无法直接访问只有本机应用能连安全性会好很多。三个-v分别把宿主机上的 conf、data、logs 目录挂载进容器。这里有个顺序问题需要注意数据目录挂载时如果宿主机目录是空的Docker 会把这个目录挂载为空的卷容器里原有在/data下的数据不会被隐藏覆盖但 Redis 启动时会在空目录里创建新的 RDB 和 AOF 文件。因此如果是从旧环境迁移数据先把 dump.rdb 或 AOF 文件拷贝到/opt/redis/data下再启动容器而不是反过来。--restartalways是生产环境必备选项。它让 Docker 在宿主机重启或容器因异常退出时自动拉起 Redis。注意如果容器是被手动docker stop的restartalways也不会自动重新拉起Docker 尊重手动停止状态这是预期行为。如果容器是因为 Redis 进程崩溃退出--restart就会介入几秒内重新启动。这个重启策略对 Redis 这种核心组件尤其重要很多事故就是容器挂掉后没人及时发现服务直接不可用。最后一段redis-server /etc/redis/redis.conf是容器主进程的启动命令意思是让 Redis 加载我们挂载进去的配置文件运行。不加这个参数的话官方镜像默认会用内置命令redis-server启动它不会主动读取我们挂载的位置导致所有自定义配置全部失效进入一个无密码、无持久化的裸奔状态。4.2 为什么主进程必须用 redis-server 引导配置文件这一条我觉得值得单独强调。不少人在官方镜像默认情况下只写了docker run -d redis:5.0.12然后进去发现没有密码配置也不生效接着就开始怀疑是不是镜像的问题。实际上官方镜像的默认入口就是一个没有任何参数的redis-server它加载的是 Redis 自身默认配置。我们挂载的/etc/redis/redis.conf如果没有被命令引用就是纯粹放在那里当摆设。所以启动命令里redis-server /etc/redis/redis.conf才是真正让自定义配置生效的关键一步。如果你想临时加一个配置项而不想改动配置文件也可以写成docker run -d \ --name redis-5.0.12 \ -p 6379:6379 \ -v /opt/redis/data:/data \ redis:5.0.12 \ redis-server --appendonly yes --requirepass YourStrongPassword这种方式用来临时验证配置很顺手但缺点是不能持久化保存配置肉眼看不出来之前用过哪些参数。所以我更建议把配置固化到文件里用上面那套完整命令启动。4.3 启动后的状态检查与基础连通性验证启动完成后不要急着写业务代码先看容器状态docker ps如果docker ps里没有出现 redis-5.0.12说明容器已经退出立刻看日志docker logs redis-5.0.12docker logs是最快定位问题的入口。如果看到Cant open the log file: Permission denied说明 logs 目录权限不对解决方式是调整目录属主如果看到FATAL CONFIG FILE ERROR说明配置里有语法错误或者无效参数需要逐行检查 redis.conf如果看到Could not create server TCP listening socket *:6379: bind: Address already in use说明宿主机 6379 端口被占用要么换映射端口要么清理占用进程。容器运行正常后再做一次连接测试。官方镜像自带redis-cli通过docker exec直接进入容器环境操作是最干净的方式docker exec -it redis-5.0.12 redis-cli -a YourStrongPassword ping返回PONG就说明 Redis 核心功能正常。接着可以看一些基本运行信息docker exec -it redis-5.0.12 redis-cli -a YourStrongPassword infoinfo返回特别长但一开始只需要关注redis_version、uptime_in_seconds、connected_clients、used_memory、role。这些指标能快速确认实例版本、运行时长、内存占用和主从角色。最后通过宿主机验证端口映射是否真的畅通。如果在宿主机装了 redis-cli直接redis-cli -h 127.0.0.1 -p 6379 -a YourStrongPassword ping没装的话可以执行docker run --rm -it --network host redis:5.0.12 redis-cli -h 127.0.0.1 -p 6379 -a YourStrongPassword ping这一步确认的是网络链路而不是容器内部功能。两者都验证通过部署才算真正完成。4.4 重启、停止与删除的常用管理操作Redis 容器日常管理主要就是几个命令背下来就行但要理解每个命令的行为差异。停止容器docker stop redis-5.0.12这个命令会先给 Redis 发送 SIGTERM 信号Redis 会尝试正常关闭把内存中的数据刷到持久化文件然后进程退出容器停止。如果 10 秒内没有停止Docker 才发 SIGKILL 强杀。注意--restartalways不会打破这个逻辑手动 stop 后容器保持 stopped。启动容器docker start redis-5.0.12重启容器等价于 stop 再 startdocker restart redis-5.0.12修改了配置后我通常用docker restart加载新配置。如果只想验证一下配置语法而不实际重启服务可以用docker exec -it redis-5.0.12 redis-server /etc/redis/redis.conf --test-memory不过--test-memory是内存测试和配置检查不是一回事。真正检查配置文件有没有语法错误可以先用一个临时容器挂载配置文件启动一次docker run --rm -v /opt/redis/conf/redis.conf:/etc/redis/redis.conf redis:5.0.12 redis-server /etc/redis/redis.conf --check-config如果输出没有报错对应的语法就是好的。这个命令在生产环境换配置之前先跑一遍能避免很多自杀式重启。彻底删除容器会连同容器本身的数据卷声明一起删掉但宿主机挂载目录里的数据保留docker rm -f redis-5.0.12如果你确认不要这套数据了再把/opt/redis/data下的持久化文件清空否则再次启动时旧数据还会被加载造成“明明换配置了老 key 还在”的错觉。5. 验证、性能检查与高频问题排查5.1 用 redis-benchmark 做一次基础压测部署完成后做一次简单的压测能帮你确认实例的处理能力是否符合预期。官方镜像里带了一个redis-benchmark工具一般用法如下docker exec -it redis-5.0.12 redis-benchmark -h 127.0.0.1 -p 6379 -a YourStrongPassword -n 100000 -c 50 -t set,get,lpush参数含义不复杂-n 100000表示总共发送 10 万条请求-c 50表示用 50 个并发连接-t set,get,lpush指定只测这三种操作。一次性跑完会输出每个操作的 QPS 和平均延迟。第一次看到这份输出时别太兴奋也不要太紧张。QPS 高低取决于宿主机 CPU 和网络环境同一套 Redis 在不同机器上表现差异很大。关键是拿它作为基线之后如果服务变慢再跑一次对比才有参考价值。如果压测中出现大量超时优先怀疑资源限制检查docker stats redis-5.0.12看看容器 CPU 和内存使用率是不是已经被打满如果接近maxmemory上限且写操作触发淘汰策略也会出现延迟升高。5.2 容器内外连接失败的原因分布我在实际帮别人排查时发现容器内外连接不上 Redis 的原因通常集中在下面几个方面我整理成表格方便对照现象原因处理方式docker exec进去后 redis-cli 连不上Redis 进程未启动或启动后崩溃docker logs看错误日志宿主机 redis-cli 连接超时端口映射没生效或映射到 127.0.0.1检查docker ps的端口列宿主机 redis-cli 连接被拒绝防火墙拦截 6379临时放通或检查安全组规则(error) NOAUTH Authentication required需要密码但没带命令加-a 密码Connection refused且日志显示 bind 失败6379 被其它进程占用netstat -tlnp找到占用进程远程机器连接超时绑定网卡不对或安全组未放通确认 bind 0.0.0.0 和安全组写操作返回MISCONF错误RDB/AOF 持久化失败保护性拒绝写检查磁盘空间和目录权限其中MISCONF这个错误我需要单独展开说。Redis 的默认行为是如果后台保存 RDB 快照失败它会拒绝所有写请求以此提醒运维人员及时处理。这种情况往往是磁盘空间满了或者dir /data这个挂载点权限有问题。我见过有人用CONFIG SET stop-writes-on-bgsave-error no临时绕过这个保护让 Redis 继续写入但这是治标不治本的应急手段根因不修复数据安全始终是隐患。5.3 内存、后台保存和容器退出相关的系统级参数除了 Redis 自身配置宿主机层面还有两个参数对 Redis 运行稳定性影响很大。第一个是内核的vm.overcommit_memory。Redis 在持久化时如果内存申请模式过度保守可能出现后台保存失败日志里会看到Background save process terminated by signal。一般建议设置sysctl -w vm.overcommit_memory1这个参数的意思是允许进程申请超过物理内存和交换空间总和的内存对 Redis fork 子进程进行持久化时有明显帮助。要想永久生效写入/etc/sysctl.conf里再sysctl -p。第二个是内核参数net.core.somaxconn。Redis 默认监听套接字的 backlog 队列长度是 511如果系统默认的 somaxconn 太小高并发连接进来时会出现连接被拒绝。建议同样调大sysctl -w net.core.somaxconn1024这两个参数都是系统级调优不区分裸机还是 Docker因为容器和宿主机共享内核参数。很多东西如果你只盯着容器内部可能永远找不到问题根源。另外还有一个视觉上特别神秘的现象容器启动后过了几秒就 Exited日志里却干干净净。这种情况通常是配置里写了daemonize yes。Redis 进程自己 fork 到后台后作为容器主进程的前台进程已经退出容器自然就停了。这也再一次回到核心原则容器里的 Redis 必须前台运行daemonize no不能写成 yes。5.4 修改配置后别踩的坑我最后再分享一个很多人容易忽略的实操细节。配置文件修改后执行docker restart redis-5.0.12前最好先通过docker exec进入容器验证配置文件是否真的加载到了正确路径。你可以这样确认docker exec -it redis-5.0.12 redis-cli -a YourStrongPassword CONFIG GET maxmemory docker exec -it redis-5.0.12 redis-cli -a YourStrongPassword CONFIG GET appendonly如果返回的结果里appendonly是yes说明配置加载成功如果显示no那就是启动命令没有读取你挂载的配置文件或者读取的是另一个路径。这种排查思路比反复重启容器高效得多因为每个配置项都能精确对账不会出现“以为改了但实际没生效”。如果只是临时想改一个运行参数不打算动配置文件在线修改也能办到docker exec -it redis-5.0.12 redis-cli -a YourStrongPassword CONFIG SET maxmemory 512mb但这种修改只会保留到 Redis 重启前。如果确认这个参数要长期生效还是回归到改配置文件那一步否则重启后一切打回原形。最后想说的是Docker 部署 Redis 本身不难难的是你愿不愿意在启动之前把配置、目录、权限、网络、持久化这些细节想清楚。我最初几次部署也踩过daemonize yes导致容器秒退的坑也见过生产环境因为logs目录没挂载、日志被容器清掉后无从排查的麻烦。但只要按这套流程走一遍之后你再在任意一台机器上部署 Redis基本上十分钟内就能交付一个稳定、可维护、数据安全的实例。这套组合我已经在很多环境里验证过了你也可以放心直接照用。
阅读完成 · 觉得有帮助?