RocketMQ 5.5.0 Docker 部署踩坑实录

🧑 博主简介CSDN博客专家「历代文学网」(PC端可以访问:https://lidaiwenxue.com/#/?__c=1000,移动端可关注公众号 “ 心海云图 ” 微信小程序搜索“历代文学”)总架构师,首席架构师,也是联合创始人!16年工作经验,精通Java编程高并发设计分布式系统架构设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生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 节点

目录

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

一、镜像内 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_OPTSROCKETMQ_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 个缓冲区是池化复用的,并非启动时一次性全部占用。实际占用取决于 transientStorePoolSizemappedFileSizeCommitLog 的配置值。

如果 -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-exportsJDK 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

教训:权限问题不只有 chownchmod,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 挂载时,提前创建是更保险的做法。


七、brokerNamebrokerId 配置要唯一

现象

集群中某个从节点无法启动,日志卡在 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 目录下的 commitlogconsumequeue 等子目录只有在 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 关掉。


评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

越重天

你的打赏是我精心创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值