docker-compose 部署 Redis Stack Server 6.2.6-v19

用 Docker 跑一个 Redis,本该是十分钟的事。可一换成 Redis Stack,特别是要固定到某个具体版本时,端口、密码、持久化、模块参数只要有一项没配对,容器就会"起来了但连不上"。

这篇以 redis/redis-stack-server:6.2.6-v19 为例,把一份可以直接抄的 Compose 部署方案拆开讲清楚,从环境准备一直讲到健康检查。

一、为什么用 Compose,而不是一行 docker run

单个 docker run 当然能把服务跑起来,但参数一多就不好维护:端口、数据卷、环境变量、健康检查全挤在一行里,改动一次就要重敲一遍,换台机器还容易漏参数。

Compose 把这些统一写进一份声明式的 YAML,配置本身就成了文档,docker-compose up -d 一条命令即可复现整套环境,也方便随代码一起做版本管理。

对 Redis Stack 来说还有一层现实原因:它需要靠 REDIS_ARGS 这类环境变量把参数传给底层 Redis,写进 Compose 文件,比塞进冗长的命令行清晰得多。

二、先认识 redis-stack-server:6.2.6-v19

Redis Stack 并不是 Redis 的替代品,而是在 Redis 内核之上打包了五个模块,让一个实例同时具备搜索、文档、图和时间序列等能力:

  • RediSearch:全文搜索与二级索引;
  • RedisJSON:JSON 文档的存取与查询;
  • RedisGraph:图数据结构的存储与查询;
  • RedisTimeSeries:时间序列数据的写入与查询;
  • RedisBloom:布隆过滤器等概率型数据结构。

官方为 Redis Stack 提供了两个镜像,选错很常见:

  • redis/redis-stack:包含 server 和 RedisInsight,适合本地开发,会同时暴露 6379(Redis)和 8001(RedisInsight)两个端口;
  • redis/redis-stack-server:只包含 server,适合生产部署,仅暴露 6379。本文用的就是这一个。

本次的 6.2.6-v19 是 Redis Stack Server 6.2.6 的一个维护版本,内置的 Redis 内核版本为 6.2.17,所含模块版本如下:

模块 版本
RediSearch 2.6.26
RedisJSON 2.4.11
RedisGraph 2.10.12
RedisTimeSeries 1.8.16
RedisBloom 2.4.13

官方把这次更新的紧急程度标为 HIGH ,主要修复了 Redis Query Engine 中几处可能导致 FT.SEARCH 崩溃的问题。如果线上还在更早的 6.2.6 系列上,升级到这个版本会比较稳妥。

但有一条必须提前讲清楚:这个版本内置的 Redis 内核是 6.2.17 ,而 2025 年 10 月披露的 CVE-2025-49844 是一个 CVSS 8.8 级别的高危认证后远程代码执行漏洞,影响 Redis < 6.2.20 的所有版本,通过 Lua 脚本引擎的 use-after-free 缺陷触发------只要攻击者拥有合法账号能连进来、能执行 EVAL/EVALSHA,就有机会在 redis-server 进程内执行任意代码。6.2.6-v19 的内核无法通过小版本升级来修复这个问题,因此网络隔离与攻击面缩减就是部署这个版本时最重要的两件事,下文会单独展开。

客户端的最低版本建议,官方也一并给出:Java 端 Jedis 5.2.0 及以上,Python 端 redis-py 5.2.1 及以上,Node.js 端 node-redis 4.7.0 及以上,.NET 端 NRedisStack 0.13.2 及以上,Go 端 go-redis 9.7.0 及以上。

三、环境准备与目录规划

先确认 Docker 与 Compose 已就位:

yaml 复制代码
docker version
​
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false
​
Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:45:28 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
yaml 复制代码
docker-compose version 
​
1.29.1, build c34c88b2
docker-py version: 5.0.0
CPython version: 3.7.10
OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019

再规划一个专门的目录,把数据和配置都收在一处,便于备份和迁移:

bash 复制代码
mkdir -p /opt/redis-stack/{data,conf}
cd /opt/redis-stack

启动前顺手看一眼 6379 端口有没有被占用,避免起不来才发现冲突:

perl 复制代码
ss -lntp | grep 6379

四、编写 docker-compose.yml

在 /opt/redis-stack 下新建 docker-compose.yml:

bash 复制代码
services:
  redis-stack:
    image: redis/redis-stack-server:6.2.6-v19
    container_name: redis-stack-server
    restart: unless-stopped
    ports:
      # 默认仅绑定宿主机回环地址,不暴露到公网
      - "127.0.0.1:6379:6379"
    environment:
      - REDIS_ARGS=--requirepass ${REDIS_PASSWORD} --appendonly yes --appendfsync everysec --maxmemory 768mb --maxmemory-policy allkeys-lru --rename-command CONFIG "1d7f729e3a5b_${REDIS_PASSWORD}_CONFIG" --rename-command FLUSHDB "" --rename-command FLUSHALL "" --rename-command EVAL "" --rename-command EVALSHA ""
    volumes:
      - ./data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s
    deploy:
      resources:
        limits:
          memory: 1g
        reservations:
          memory: 256m

几个关键项逐一说明:

  • image :锁定到具体版本号,不要用 latest,否则某天镜像更新可能带来不可预期的行为变化;
  • ports :写成 "127.0.0.1:6379:6379",只绑定宿主机回环地址 ,而不是默认的 0.0.0.0(所有网卡)。Docker 端口映射的默认行为是监听全部网卡------写成 "6379:6379" 就等同于把 Redis 挂到了公网 IP 上,这是最容易被忽略、也最危险的写法;
  • environment :把 Redis 参数交给 REDIS_ARGS,这里额外通过 --rename-command 做了几件事:把 EVAL/EVALSHA 重命名为空字符串直接禁用(堵住 CVE-2025-49844 的攻击入口),把 FLUSHDB/FLUSHALL 同样禁用防止误删,把 CONFIG 重命名为带密码的复杂字符串以防被低权限连接篡改配置;
  • volumes :把宿主机的 ./data 挂到容器的 /data,容器重建数据也不会丢;
  • healthcheck :带密码的连接需要显式传 -a,否则检查会一直失败;
  • deploy.resources:给容器设内存和 CPU 上限,避免单个服务耗尽宿主机资源。

五、配置与持久化:为什么必须用 REDIS_ARGS

这是 redis-stack-server 最容易踩的坑:不要用 command 去覆盖容器的启动命令。

用一个 command: redis-server --save ... 覆盖入口脚本,模块不会按预期加载,服务还可能进入 protected mode 而对外不可访问------容器看着是起来了,客户端却连不上。正确做法是保持镜像默认入口不动,把参数通过 REDIS_ARGS 环境变量传给底层 Redis。

密码等敏感值建议放到同目录的 .env 文件,Compose 会自动读取,避免把密码硬编码进 YAML:

ini 复制代码
# /opt/redis-stack/.env
REDIS_PASSWORD=ChangeMe_2026

REDIS_ARGS 里常用的参数包括:

  • --requirepass <password>:设置访问密码;
  • --appendonly yes:开启 AOF 持久化,比 RDB 更不容易丢数据;
  • --appendfsync everysec:每秒同步一次,兼顾性能与安全;
  • --maxmemory 768mb 与 --maxmemory-policy allkeys-lru:限制可用内存并指定淘汰策略,取值应略小于容器的内存上限;
  • --rename-command EVAL "" / --rename-command EVALSHA "":将 Lua 脚本相关命令重命名为空字符串,等于直接禁用------这是针对 CVE-2025-49844 的关键缓解手段;
  • --rename-command FLUSHDB "" / --rename-command FLUSHALL "":禁用清库命令,防止误操作或攻击者删数据;
  • --rename-command CONFIG "<复杂字符串>":将 CONFIG 重命名为只有运维知道的名字,避免低权限连接动态改配置。

业务依赖 EVAL 的例外处理:个别框架(如某些 Lua 缓存层、限流组件的脚本模式、Spring Cache 的部分 Lua 实现)会用到 EVAL/EVALSHA。启用前先在测试环境确认应用日志里是否有 EVAL 相关调用,如确需使用,请:①删除 YAML 中对 EVAL/EVALSHA 的 rename-command 行;②确保 Redis 密码强度足够(16位以上混合字符);③禁止任何未经认证的网络可达性;④应用账号通过 ACL 限制只能执行必要的命令前缀。

除了 REDIS_ARGS,镜像还提供针对各模块的环境变量:REDISEARCH_ARGS、REDISJSON_ARGS、REDISTIMESERIES_ARGS、REDISBLOOM_ARGS。例如给时序数据设置保留策略,可以写成 REDISTIMESERIES_ARGS=RETENTION_POLICY=20。

如果你更习惯一整份配置文件,也可以把本地的 redis-stack.conf 挂载到容器内的 /redis-stack.conf,由它接管配置。

至于持久化本身,数据落在容器的 /data 目录下。配合前面挂载的 ./data,即使执行 docker-compose down 再重建,数据依然还在。

六、启动、验证与连接

启动并在后台运行:

复制代码
docker-compose up -d
​
docker-compose ps

当状态显示为 Up (healthy),说明容器与健康检查都通过了。

接着进入容器验证服务是否正常应答:

perl 复制代码
docker exec -it redis-stack-server redis-cli -a ChangeMe_2026 --no-auth-warning ping
# 返回 PONG

确认五个模块都已加载:

css 复制代码
docker exec -it redis-stack-server redis-cli -a ChangeMe_2026 --no-auth-warning MODULE LIST

写入一条数据试试手:

css 复制代码
docker exec -it redis-stack-server redis-cli -a ChangeMe_2026 --no-auth-warning SET hello world

如果宿主机上装有 redis-cli,也可以直接连 127.0.0.1:6379。因为我们默认只绑定了回环地址 ,外部机器是无法通过网络直接访问这个端口的------这正是我们想要的效果。需要图形化界面的话,可以在本机运行 RedisInsight 容器通过 --network host 或连 127.0.0.1:6379。

七、安全加固:本版本必须做的三件事

这个版本的内置内核停在 6.2.17,而 CVE-2025-49844 修复于 6.2.20------我们没法靠升级内核来修它,所以下面三件事不是"建议",是必须做到,否则这个实例一旦暴露到不可信网络就是高危。

1. 端口只绑定 127.0.0.1,绝不直接放公网

Docker 的端口映射 "6379:6379" 默认行为是监听 0.0.0.0:6379,也就是宿主机的所有网卡------只要你的服务器有公网 IP,任何人都能尝试连这个端口。我们在配置里已经写成 "127.0.0.1:6379:6379",这是第一道也是最重要的防线。

如果同机部署的应用(比如本机的 Node/Python/Java 服务)需要访问 Redis,127.0.0.1:6379 就足够了。

如果需要从其他机器访问(包括本地电脑远程管理服务器上的 Redis),走 SSH 隧道或 VPN,不要放开端口。最简单的 SSH 隧道用法:

perl 复制代码
# 在你本地机器执行,把远程 6379 映射到本地 6379
ssh -L 6379:127.0.0.1:6379 your_user@your_server_ip -N

执行后本地程序连 127.0.0.1:6379 就相当于连到了服务器上的 Redis,整条链路走 SSH 加密,且服务器端不需要在防火墙开放 6379。

如果是多台服务器之间的内网访问,至少要做到三层限制:云安全组/防火墙只放行业务服务器内网 IP;Docker 网络使用自定义 bridge 网络而非 host 模式;Redis 侧配合 ACL 做权限分级。

2. 禁用 Lua 脚本(EVAL/EVALSHA),堵住 CVE-2025-49844 的攻击入口

CVE-2025-49844 是 Redis Lua 脚本引擎中的 use-after-free 内存破坏漏洞。已认证的攻击者通过构造恶意 Lua 脚本,可以在 redis-server 进程内执行任意代码------一旦成功,进程里所有缓存数据、会话令牌、业务状态全部泄露,Redis 通常又以较高权限运行,可能被用来横向移动。

我们在 REDIS_ARGS 中已通过 --rename-command EVAL "" 和 --rename-command EVALSHA "" 将这两个命令彻底禁用。Redis 6.2 不支持 7.0+ 的 disable-commands 指令,rename-command 设为空字符串是官方文档中该版本唯一可靠的禁用方式。

如果业务框架确实依赖 EVAL(例如某些 Lua 限流脚本、Spring Data Redis 的脚本执行模式),请先在测试环境禁用后观察应用日志是否报错。确需保留的话:①移除 YAML 中对 EVAL/EVALSHA 的重命名;②密码必须 16 位以上、包含大小写+数字+特殊字符;③通过 Redis ACL 创建仅具有必要命令权限的应用账号,不要所有服务共用默认账号;④端口仍然不可以对公网开放。

3. 密码强认证 + 危险命令重命名 + 内存上限

  • 密码不要用示例中的 ChangeMe_2026,上线前替换成自己生成的强密码;
  • FLUSHDB/FLUSHALL 已经被重命名为空,防止任何连接一键清空数据;
  • CONFIG 被重命名为带有密码片段的复杂字符串,运维需要时用 redis-cli 执行重命名后的完整命令名;
  • maxmemory 配了硬上限,Redis 不会无限吃掉宿主机内存。

关于 Redis Functions 的补充说明 :Redis Functions 是 7.0 才引入的功能,6.2.x 内核不支持,因此我们不需要额外处理 fcall 等命令,只需要禁用 EVAL/EVALSHA 即可覆盖本版本全部 Lua 执行路径。

八、其他常见坑

  • 用 command 覆盖入口 :模块未加载、protected mode 拦截访问,改用 REDIS_ARGS;
  • 设了密码却没给 healthcheck 传密码 :容器会长期停在 unhealthy,记得加 -a;
  • 端口冲突 :启动前用 ss -lntp | grep 6379 确认端口空闲,或改映射端口;
  • maxmemory 与容器内存不匹配 :maxmemory 要略小于容器内存上限,避免被 OOM 杀掉;
  • 图省事用 latest:版本漂移带来的问题最难排查,务必锁定版本号。

结语

Redis Stack 把搜索、JSON、图、时序、概率结构这几件事收进了一个镜像,而 Compose 让整套部署变成一份可复制的 YAML。真正需要留心的地方不只是"把服务跑起来":锁死版本号、始终用 REDIS_ARGS 而不是 command 传配置是基本功;对于 6.2.6-v19 这个内核停在 6.2.17 的版本,端口只绑回环、禁用 Lua 脚本、强密码认证这三件事是不可省略的安全底线。

把这些都做到,docker-compose up -d 之后,你得到的就是一个带着完整模块能力、开着持久化、配了健康检查,且在已知高危漏洞面前收缩了攻击面的 Redis 实例。

相关推荐
方便面不加香菜1 小时前
Docker Container -- 容器命令详解
运维·docker·容器
小南家的青蛙2 小时前
Redis监控与告警体系化实践
redis·监控方案·告警方案
我不会起名字3222 小时前
Redis 从 0 到 1(二十七):JSON 体积是 Protobuf 的 3.7 倍?3 种序列化的 Go 基准测试
redis·go·序列化·protobuf·messagepack
我不会起名字3222 小时前
Redis 从 0 到 1(二十六):INFO memory 里 8 个关键数字——内存构成与容量规划
redis·容量规划·内存·info·listpack
我不会起名字3222 小时前
Redis 从 0 到 1(二十五):连接池又打满了——PoolSize 公式、泄漏排查与 PoolStats
redis·连接池·go-redis·排查·poolstats
我不会起名字3222 小时前
Redis 从 0 到 1(二十三):一个热 key 打到 10 万 QPS——hotkey 与本地缓存的 Go 实现
redis·缓存·go·qps·hotkey
IT规划师4 小时前
Redis 数据类型全解 - Bitmap 生产实战:把位视图讲准、用对、守住红线(六)
redis·.net