欢迎光临
我们一直在努力

RocketMQ 5.5.0 Docker 部署踩坑实录

🧑 博主简介:CSDN博客专家,「历代文学网」(PC端可以访问:https://lidaiwenxue.com/#/?__c=1000,移动端可关注公众号 “ 心海云图 ” 微信小程序搜索“历代文学”)总架构师,首席架构师,也是联合创始人!16年工作经验,精通Java编程,高并发设计,分布式系统架构设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。 🤝商务合作:请搜索或扫码关注微信公众号 “ 心海云图 ”

在这里插入图片描述

在这里插入图片描述

RocketMQ 5.5.0 Docker 部署踩坑实录

前言

最近在将 RocketMQ 从 5.3.3 迁移到 5.5.0 的过程中,选择了官方 apache/rocketmq:5.5.0 镜像,并搭建了一套两主两从的 Docker 集群。本以为会是一个简单的版本升级,没想到一路踩坑无数。

本文将这些坑点逐一记录下来,希望能帮助到正在或即将部署 RocketMQ 5.5.0 的你。


环境说明

  • 操作系统:CentOS 7 / Rocky Linux 8
  • 容器运行时:Docker 20.10+
  • 镜像版本:apache/rocketmq:5.5.0(实际使用 registry.cn-hangzhou.aliyuncs.com/sinhy/rocketmq:5.5.0,基于官方镜像重命名)
  • 集群架构:两主两从(2m-2s-sync),共 4 个 Broker 节点 + 2 个 NameServer 节点

目录

  • 镜像内 UID 是 3000 而非 1000
  • JVM 环境变量名称错了
  • 直接内存为何要设大
  • JDK 8 不支持 –add-exports
  • SELinux 阻止容器写入挂载目录
  • logs/rocketmqlogs 子目录需提前创建
  • brokerName 和 brokerId 配置要唯一
  • –network host 模式下 -p 端口映射失效
  • store 目录为空(正常现象)
  • 总结:一键部署脚本

  • 一、镜像内 UID 是 3000 而非 1000

    现象

    容器启动后,日志无法写入挂载的宿主机目录。检查目录权限后,发现已经执行了 chown -R 1000:1000,但依然报错 Permission denied。

    原因

    官方 apache/rocketmq:5.5.0 镜像内部,rocketmq 用户的 UID 是 3000,而非常见的 1000。

    # 进入容器验证
    docker exec -it broker-a-10911 id
    # 输出: uid=3000(rocketmq) gid=3000(rocketmq)

    很多周边项目(如 rocketmq-operator)使用的是 UID 1000,但官方核心镜像的标准是 3000。

    解决方案

    挂载目录的所有者必须设置为 3000:

    sudo chown -R 3000:3000 /data/docker-containers/rocketmq/broker
    sudo chmod -R 755 /data/docker-containers/rocketmq/broker

    教训:永远不要假设容器内用户的 UID,直接用 docker exec 进去确认。


    二、JVM 环境变量名称错了

    现象

    明明在 docker run 中通过 -e JAVA_OPTS="…" 设置了 JVM 参数,但 ps aux | grep java 看到的值仍是脚本默认值,导致 OutOfMemoryError: Direct buffer memory 不断出现。

    原因

    RocketMQ 5.5.0 的启动脚本 runbroker.sh / runserver.sh 中,读取的是 JAVA_OPT_EXT 环境变量,而不是 JAVA_OPTS 或 ROCKETMQ_OPTS。

    脚本中关键行:

    JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"

    解决方案

    使用正确的环境变量名:

    -e "JAVA_OPT_EXT=-Xss512k -Xms512m -Xmx2048m -XX:MaxDirectMemorySize=6g -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m"

    教训:不同版本的 RocketMQ 镜像可能使用不同的环境变量名,务必查看 runbroker.sh 源码确认。


    三、直接内存为何要设大

    现象

    即使设置了 JAVA_OPT_EXT,Broker 仍报错:

    java.lang.OutOfMemoryError: Direct buffer memory
    at org.apache.rocketmq.store.TransientStorePool.init(TransientStorePool.java:50)

    原因

    RocketMQ Broker 默认开启了 TransientStorePool(堆外内存池)。该组件会初始化一组 DirectByteBuffer 作为循环复用的缓冲区,其数量由 transientStorePoolSize 参数控制,默认值为 5;每个缓冲区的大小默认为 1GB。

    注意:这 5 个缓冲区是池化复用的,并非启动时一次性全部占用。实际占用取决于 transientStorePoolSize 和 mappedFileSizeCommitLog 的配置值。

    如果 -XX:MaxDirectMemorySize 设置的值过小,无法满足 TransientStorePool 初始化时的内存申请,JVM 就会抛出 Direct buffer memory 错误。

    解决方案

    方案一(推荐):将直接内存设为 6GB 或更高:

    -e "JAVA_OPT_EXT=-XX:MaxDirectMemorySize=6g …"

    方案二:在 broker-a.properties 中调整 TransientStorePool 的缓冲区数量:

    transientStorePoolSize=4

    或者直接禁用:

    transientStorePoolEnable=false

    注意:方案二会降低写入性能,非必要不推荐。

    教训:堆外内存池是 RocketMQ 高性能的关键,但它需要足够的直接内存支撑。生产环境下建议 MaxDirectMemorySize 至少为 -Xmx 的 2-3 倍,同时根据实际负载调整 transientStorePoolSize。


    四、JDK 8 不支持 –add-exports

    现象

    NameServer 启动报错:

    Unrecognized option: –add-exports
    Error: Could not create the Java Virtual Machine.

    原因

    –add-exports 是 JDK 9+ 引入的模块化系统参数,用于跨模块导出包。如果底层的 JDK 是 8,JVM 不认识这个参数,直接拒绝启动。

    解决方案

    直接移除 JAVA_OPT_EXT 中的 –add-exports 参数,或者添加 -XX:+IgnoreUnrecognizedVMOptions 让 JVM 忽略它:

    -e "JAVA_OPT_EXT=-XX:+IgnoreUnrecognizedVMOptions …"

    教训:如果你的镜像基于 JDK 8(官方 apache/rocketmq:5.5.0 默认使用 eclipse-temurin:8),请避免使用 JDK 9+ 特有的 JVM 参数。


    五、SELinux 阻止容器写入挂载目录

    现象

    目录权限(chown + chmod)全部正确,甚至设置了 777,但容器内仍无法写入文件:

    touch: cannot touch '/home/rocketmq/logs/test.txt': Permission denied

    原因

    CentOS / RHEL 系列操作系统默认启用 SELinux。即使宿主机目录权限放开了,SELinux 策略也会阻止容器进程写入挂载的目录。

    解决方案

    方案一(临时测试):

    sudo setenforce 0

    方案二(永久关闭,生产环境慎用):

    sudo sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
    sudo reboot

    方案三(给目录打标签,推荐):

    sudo chcon -Rt svirt_sandbox_file_t /data/docker-containers/rocketmq/broker

    教训:权限问题不只有 chown 和 chmod,SELinux 是隐藏的大坑。遇到诡异权限问题时,先 setenforce 0 测试一下。


    六、logs/rocketmqlogs 子目录需提前创建

    现象

    宿主机挂载目录 logs/ 为空,即使 Broker 启动成功也没有日志文件。

    原因

    RocketMQ 的日志配置 rmq.broker.logback.xml 中,日志输出路径为 ${user.home}/logs/rocketmqlogs/。容器在启动时,如果 /home/rocketmq/logs/rocketmqlogs/ 目录不存在,可能因权限或挂载机制导致无法自动创建。

    解决方案

    在宿主机上提前创建好 rocketmqlogs 子目录:

    sudo mkdir -p /data/docker-containers/rocketmq/broker/a/logs/rocketmqlogs
    sudo chown -R 3000:3000 /data/docker-containers/rocketmq/broker/a/logs
    sudo chmod -R 755 /data/docker-containers/rocketmq/broker/a/logs

    教训:不要依赖容器内的程序自动创建多层目录,尤其是涉及 Docker 挂载时,提前创建是更保险的做法。


    七、brokerName 和 brokerId 配置要唯一

    现象

    集群中某个从节点无法启动,日志卡在 logback 配置加载后,无任何错误信息。

    原因

    主从配置中,从节点的 brokerName 与主节点重复,或 brokerId 未设置为唯一值,导致 Broker 注册到 NameServer 时发生冲突。

    解决方案

    确保每个 Broker 节点的配置文件中的关键字段正确:

    # 主节点
    brokerName=broker-a
    brokerId=0

    # 从节点
    brokerName=broker-a-s
    brokerId=1 # 从节点 > 0

    教训:两主两从集群中,4 个 Broker 的 brokerName 必须两两成对(主从相同),但主从之间用 brokerId 区分(主=0,从>0)。


    八、–network host 模式下 -p 端口映射失效

    现象

    启动容器时明明指定了 -p 10911:10911,但 ss -tlnp 看不到端口监听。

    原因

    当使用 –network host 时,容器直接使用宿主机网络栈,所有端口映射(-p)参数会被忽略,且 Docker 会输出警告:

    WARNING: Published ports are discarded when using host network mode

    解决方案

    • 如果需要端口映射,不要使用 –network host。
    • 在分布式部署中,每个 Broker 部署在不同宿主机上,使用 –network host 可简化网络配置,此时 -p 可以移除。

    教训:–network host 是一把双刃剑,简化网络配置的同时也会让端口管理更加透明(也可能更混乱)。


    九、store 目录为空(正常现象)

    现象

    启动 Broker 后,挂载的 store 目录为空,没有任何子目录。

    结论

    这是正常的。store 目录下的 commitlog、consumequeue 等子目录只有在 Broker 收到并存储消息后才会自动创建。刚启动且未收发消息时,store 目录为空是预期行为。

    发送一条测试消息后,目录结构会自动生成。

    教训:不要因为空目录就以为 Broker 没启动好,docker logs 看到 boot success 才是关键证据。


    十、总结:一键部署脚本

    以下是集成了所有踩坑修复的最终部署脚本(单节点模板):

    #!/bin/bash
    # ============================================================
    # RocketMQ 5.5.0 Broker 部署脚本(含所有坑修复)
    # 用法:在每台机器上执行,修改 BROKER_ROLE 和 LISTEN_PORT
    # ============================================================

    BROKER_ROLE="a" # 根据节点修改:a, a-s, b, b-s
    LISTEN_PORT="10911" # 根据节点修改
    NAMESRV_ADDR="172.16.10.90:9876;172.16.10.91:9876"

    # 1. 创建目录(包含 rocketmqlogs 子目录)
    mkdir -p /data/docker-containers/rocketmq/broker/${BROKER_ROLE}/logs/rocketmqlogs
    mkdir -p /data/docker-containers/rocketmq/broker/${BROKER_ROLE}/store
    mkdir -p /data/docker-containers/rocketmq/broker/conf

    # 2. 修复权限(注意:UID 是 3000!)
    sudo chown -R 3000:3000 /data/docker-containers/rocketmq/broker/${BROKER_ROLE}
    sudo chmod -R 755 /data/docker-containers/rocketmq/broker/${BROKER_ROLE}

    # 3. 关闭 SELinux(或使用 chcon 打标签)
    sudo setenforce 0

    # 4. 启动容器
    docker run -d \\
    –restart=always \\
    –privileged=true \\
    -p ${LISTEN_PORT}:${LISTEN_PORT} \\
    –volume /data/docker-containers/rocketmq/broker/${BROKER_ROLE}/logs:/home/rocketmq/logs \\
    –volume /data/docker-containers/rocketmq/broker/${BROKER_ROLE}/store:/home/rocketmq/store \\
    –volume /data/docker-containers/rocketmq/broker/conf/2m-2s-sync:/home/rocketmq/rocketmq/conf/2m-2s-sync \\
    –name broker-${BROKER_ROLE}${LISTEN_PORT} \\
    –network host \\
    -e TZ=Asia/Shanghai \\
    -e NAMESRV_ADDR="${NAMESRV_ADDR}" \\
    -e "JAVA_OPT_EXT=-Xss512k -Xms512m -Xmx2048m -XX:MaxDirectMemorySize=6g -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m" \\
    registry.cn-hangzhou.aliyuncs.com/sinhy/rocketmq:5.5.0 \\
    sh mqbroker -c /home/rocketmq/rocketmq/conf/2m-2s-sync/broker-${BROKER_ROLE}.properties


    一句话锦囊

    RocketMQ 5.5.0 官方镜像部署四件套:UID 用 3000,JVM 用 JAVA_OPT_EXT,直接内存给 6G,SELinux 关掉。


    赞(0)
    未经允许不得转载:171主机测评 » RocketMQ 5.5.0 Docker 部署踩坑实录
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址