ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Windows下Docker Compose部署RocketMQ完整指南与避坑实录

Windows下Docker Compose部署RocketMQ完整指南与避坑实录 最近在 Windows 上折腾 RocketMQ从装 Docker Desktop 到把 NameServer、Broker、可视化面板全部跑起来前前后后踩了不下十个坑。有些坑网上搜半天也找不到明确答案比如容器里 Broker 注册的 IP 不对导致客户端连不上再比如默认 JVM 参数直接把 4G 内存吃满导致容器反复重启。这篇就把我最终跑通的完整步骤和排查思路原原本本写出来给同样想在 Windows 下用 Docker 部署 RocketMQ 的人做个参考。1. 部署前梳理RocketMQ 组件与部署方式取舍1.1 RocketMQ 架构里的核心角色先别急着敲命令搞清楚 RocketMQ 由哪几部分组成后面出问题了才知道去哪看日志。RocketMQ 的核心其实就两个服务端进程。NameServer 负责集群成员管理和路由信息维护可以理解成一个轻量级注册中心Producer 和 Consumer 启动时先找它拿 Broker 地址。Broker 才是真正存消息、转发消息的角色所有消息数据都落在它管理的存储文件里。生产环境一般会部署多台 Broker 组成主从集群但我们本地测试只需要一个 NameServer 加一个 Broker就能完整跑通消息的发送和消费。除了这两个进程还有一个经常被一起拉起来的可视化控制台 RocketMQ Dashboard。它不是 RocketMQ 的必需品但部署完用它看一眼主题列表、消息积压量、消费者组状态比敲命令行舒服太多所以我建议直接把 Dashboard 也纳入 compose 编排。初次接触的人容易把 RocketMQ 和 Kafka 搞混。从架构上看两者确实高度相似但 RocketMQ 是 Java 写的Broker 进程默认吃内存非常猛这一点在个人电脑上部署时必须特别处理。我在后面第三节会专门讲内存参数那是新手最容易忽略、也最容易翻车的地方。1.2 为什么选择 Docker Compose 而不是其他方式在 Windows 上部署 RocketMQ按老办法是先下载二进制包、配置 JDK、修改启动脚本、手动开两个终端窗口分别跑 mqnamesrv 和 mqbroker。听起来不复杂但实际操作时会遇到一堆环境问题JDK 版本不匹配、脚本里的路径带空格导致诡异报错、改完配置想回到干净状态还得手动清理文件和进程。用 Docker 单容器跑也行docker run直接拉一个镜像就能起 Namesrv 或 Broker。但 RocketMQ 最少需要两个进程再加上控制台就是三个容器只用 docker run 管理会很散端口映射、网络互通、启动顺序全得自己记。我最终选了 Docker Compose原因是它把整个环境描述收敛到一个docker-compose.yml文件里三个服务各自用什么镜像、映射哪些端口、挂载哪些目录、依赖什么顺序启动全部声明式管理。以后想重来一条docker compose down -v把所有容器和数据卷清掉环境立刻回归初始状态完全不会污染 Windows 宿主机。这套思路对任何在开发机上做中间件本地测试的人都适用。Compose 本身不挑操作系统Windows 上装好 Docker Desktop 后自带 compose v2 插件命令写法跟 Linux 上完全一致没有额外学习成本。2. Windows 环境准备Docker Desktop 安装与镜像源2.1 安装前必须检查的两个前置条件Windows 装 Docker Desktop 最容易踩的坑是启动时报虚拟化错误错误信息长这样Docker Desktop failed to start because virtualisation support wasnt detected。这行字表面上是说虚拟化没开但根源往往不是一个地方就能解决的。先检查 CPU 虚拟化是否开启了。打开任务管理器切到性能页看 CPU 那一栏虚拟化那一项必须是“已启用”。如果显示“已禁用”需要在重启时进 BIOS/UEFI找到 Intel Virtualization Technology 或者 AMD SVM Mode 选项打开。笔记本用户如果 BIOS 里找不到这个选项还要先排查是不是被 Windows 的快速启动机制干扰了关掉快速启动再重启一次。第二个检查项是 WSL2。Docker Desktop 建议使用 WSL 2 后端这个后端稳定性和性能都比旧的 Hyper-V 后端好。在管理员 PowerShell 里执行两条命令启用相关 Windows 功能dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart然后重启电脑再执行wsl --set-default-version 2。如果之前没装过任何 Linux 发行版wsl命令可能会提示需要安装一个内核更新包按提示下载安装即可。我这里特别提醒一点这两步做完必须重启别以为 dism 命令执行完就能直接打开 Docker Desktop我见过好几个同事在没重启的情况下反复点启动按钮最后只收获了两个同样报错的弹窗。2.2 镜像选型与加速配置镜像我推荐用 Apache 官方仓库的apache/rocketmq。拉取时指定具体 tag 而不是 latest这一点很重要。我自己测试用的是4.9.7这个版本算是经典稳定版社区里大量文章都是基于这个版本写的真遇到问题搜答案也容易搜到。RocketMQ 5.x 虽然已经发布很久但 5.x 镜像对 Broker 启动参数的传递方式有细节变化新手如果一上来就追新很容易被各种“此参数已废弃”的提示搞懵。Dashboard 镜像用apacherocketmq/rocketmq-dashboard这是当前官方维护的可视化控制台镜像旧教程里常见的styletime/rocketmq-console-ng已经停止维护不要再用。国内网络环境下拉 Docker Hub 镜像偶尔会失败或者慢到让人失去耐心这个问题不只影响 RocketMQ装 MySQL 也经常遇到。解决思路是给 Docker Desktop 配置 registry-mirrors 加速地址。打开 Docker Desktop 设置进入 Docker Engine 页面在 JSON 配置里加一项{ registry-mirrors: [ https://docker.m.daocloud.io ] }具体的加速地址时效性变化很快可以在网上搜当前可用的公共镜像加速服务。配置完保存后 Docker Engine 会重启一次再拉镜像就能明显感觉到速度差异。3. 三个关键配置内存、端口、持久化3.1 内存参数为什么必须改RocketMQ 官方 bin 目录下的启动脚本默认给 NameServer 分配了 4G 堆内存给 Broker 分配了 8G 堆内存。这个配置是针对生产服务器的放到个人电脑上就是灾难。我最初没修改直接启动结果 Windows 风扇疯狂转16G 内存的机器直接卡到鼠标都不跟手容器里 Broker 进程还没跑完启动流程就被 OOM Killer 杀了。在容器环境里想覆盖默认内存参数需要借助环境变量JAVA_OPT_EXT。官方镜像里的 mqnamesrv 和 mqbroker 启动脚本都会读取这个变量把它拼进最终的 Java 启动命令。我的本机是 16G 内存给 NameServer 和 Broker 各分配 512M 堆内存加 256M 新生代已经足够本地测试-Xms512m -Xmx512m -Xmn256m如果跑的是完整的主从架构Broker 内存可以提到 1G。这个经历跟部署 Elasticsearch 很像ES 也是 Java 服务默认 jvm.options 同样是几个 G 的堆内存跑到本地不改参数必定卡死。凡是这种重量级 Java 中间件进容器第一件事就是确认内存参数是否适合当前机器。我用 compose 编排时给每个服务都设置了独立的JAVA_OPT_EXTNameServer 和 Broker 互不影响。不要图省事只给 Broker 配NameServer 默认的 4G 内存同样可能撑爆本地机器。3.2 端口映射与 brokerIP1RocketMQ 涉及三个端的端口先说清楚再配 compose 也不迟。NameServer 监听 9876 端口客户端和 Broker 都需要访问它。Broker 则监听三个端口10911 是主端口客户端实际收发消息都走这个端口10909 是快速消息端口10912 是主从同步端口单机模式下用不到但也映射出来备用。控制台默认监听 8080如果本机 8080 被其他程序占用可以在 compose 里把宿主机侧端口改成 18080。端口映射不难理解真正坑的是brokerIP1这个配置。Broker 启动后会把自身 IP 注册到 NameServer而容器默认注册的是容器内网 IP类似172.18.0.2。你想想客户端在宿主机上拿到的 Broker 地址是 172 开头的容器 IP它能连上吗连不上。解决方案是在 Broker 的配置文件里显式指定对外注册的 IP。如果只是本机测试填127.0.0.1即可如果同一局域网内其他机器也要访问就填 Windows 宿主机的局域网 IP。这个配置项极其容易踩坑很多人的 RocketMQ 客户端报“connect 172.18.0.2:10911 failed”基本都是这个原因网上搜“rocketmq 容器部署后记录 IP 不对”“客户端连不上 broker”很大概率也是同一个问题。我习惯把 Broker 的监听地址固定为宿主机 IP而不是 127.0.0.1因为一旦以后想从局域网内的另一台机器发消息测试不用再改配置重启。3.3 数据持久化挂载RocketMQ 的数据核心是 Broker 的存储目录里面是 commitlog、consumequeue、index 等文件。如果不做挂载容器一删数据就没了。虽然本地测试丢了也不心疼但每次重启都要重新建主题、重新积压数据体验很差。我做了三个目录的挂载日志目录logs、存储目录store、配置文件目录conf。conf 挂载的是宿主机上的文本配置文件这种方式叫 bind mount修改配置后重启容器即可生效。Windows 宿主机上使用相对路径时要注意compose 文件里的相对路径是相对于docker-compose.yml所在目录的不是当前终端路径。我第一次就是没注意在别的目录执行了 compose 命令结果挂载目录凭空多出一堆乱七八糟的空文件夹。再补充一个进阶提醒如果要做性能压测不要把 Broker 的 store 目录挂到 Windows 的 NTFS 磁盘路径上。Docker Desktop 的 WSL2 模式下跨文件系统做大量随机 IO 性能损失相当大更合理的做法是给 Broker 单独创建 docker volume让数据落在 WSL2 的文件系统内部。本地功能测试用 bind mount 图个一目了然压测场景务必换成 volume。4. 实操过程docker-compose 一键部署 RocketMQ4.1 编写 docker-compose.yml 和 broker.conf我建议新建一个专门的目录放 RocketMQ 的 compose 文件目录结构干净也好管理D:\docker\rocketmq ├── docker-compose.yml ├── broker │ └── broker.conf └── data ├── namesrv └── broker先在broker/broker.conf里写基础配置brokerClusterName DefaultCluster brokerName broker-a brokerId 0 deleteWhen 04 fileReservedTime 48 brokerRole ASYNC_MASTER flushDiskType ASYNC_FLUSH autoCreateTopicEnable true brokerIP1 192.168.1.100autoCreateTopicEnable在开发环境建议显式设为 true这样不用手动建主题就能直接发消息测试。Production 环境需要关闭并走审批流程但本地跑测试没必要为难自己。其中brokerIP1填写你自己 Windows 宿主机的局域网 IP我这里是192.168.1.100。如果只想本机访问填127.0.0.1也行。这个配置文件会在 Broker 容器启动时通过-c参数指定加载。接着写docker-compose.ymlversion: 3.8 services: namesrv: image: apache/rocketmq:4.9.7 container_name: rmqnamesrv ports: - 9876:9876 volumes: - ./data/namesrv/logs:/home/rocketmq/logs environment: JAVA_OPT_EXT: -Xms512m -Xmx512m -Xmn256m command: sh mqnamesrv broker: image: apache/rocketmq:4.9.7 container_name: rmqbroker ports: - 10909:10909 - 10911:10911 - 10912:10912 volumes: - ./data/broker/logs:/home/rocketmq/logs - ./data/broker/store:/home/rocketmq/store - ./broker/broker.conf:/home/rocketmq/conf/broker.conf environment: JAVA_OPT_EXT: -Xms512m -Xmx512m -Xmn256m command: sh mqbroker -n namesrv:9876 -c /home/rocketmq/conf/broker.conf depends_on: - namesrv dashboard: image: apacherocketmq/rocketmq-dashboard:latest container_name: rmqdashboard ports: - 18080:8080 environment: JAVA_OPTS: -Drocketmq.namesrv.addrnamesrv:9876 depends_on: - namesrv有几个细节值得展开说。Broker 通过-n namesrv:9876而不是localhost:9876去连接 NameServer因为这两个组件现在是两个独立容器必须走 compose 自动创建的默认网络用服务名 namesrv 才能解析到对方。新手最容易在这里写错把 localhost 当成万能地址。Dashboard 的镜像新老版本对环境变量的解析方式不完全一致。我用JAVA_OPTS传 namesrv 地址的方式在多数版本下都能生效如果你拉的新版镜像进面板后连不上 NameServer直接在 Dashboard 页面右上角手动填namesrv:9876也可以。我把 Dashboard 的宿主机端口映射改成 18080 是因为本机 8080 经常被占你如果没占用直接用 8080 映射也行。注意容器内部的监听端口始终是 8080只有左侧宿主机端口可以灵活改。4.2 启动、验证与基本测试在 Docker Desktop 已经运行的情况下进入D:\docker\rocketmq目录执行docker compose up -d第一次启动需要拉三个镜像取决于网络状况可能需要几分钟。拉完后docker ps应该能看到三个容器都在运行状态。用docker logs rmqbroker观察 Broker 启动日志看到类似boot success或者The broker[broker-a, 192.168.1.100:10911] boot success字样就说明核心环境起来了。注意日志里注册的 IP 应该是我们配置的192.168.1.100如果你看到的是 172 开头的内网 IP说明 broker.conf 没生效回去检查-c参数指向的路径是否正确。打开浏览器访问http://localhost:18080。如果看到 Dashboard 页面且在集群列表里能看到一个存活的 Broker部署就算成功了一大半。我最简单的端到端验证方式是用 Java 写一段十几行的客户端代码。RocketMQ 5.x 前的客户端库依赖还是很传统的Maven 引入即可dependency groupIdorg.apache.rocketmq/groupId artifactIdrocketmq-client/artifactId version4.9.7/version /dependency生产者发一条消息DefaultMQProducer producer new DefaultMQProducer(test-producer-group); producer.setNamesrvAddr(127.0.0.1:9876); producer.start(); Message msg new Message(TestTopic, hello from windows.getBytes(StandardCharsets.UTF_8)); producer.send(msg); producer.shutdown();消费者接一条消息DefaultMQPushConsumer consumer new DefaultMQPushConsumer(test-consumer-group); consumer.setNamesrvAddr(127.0.0.1:9876); consumer.subscribe(TestTopic, *); consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) - { msgs.forEach(m - System.out.println(new String(m.getBody()))); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start();两段代码跑通后Dashboard 的消息量、消费进度都会有对应变化。整个过程验证的都是同一件事NameServer 路由发现、Broker 存储、客户端跨网络访问三个环节是否连通。5. 常见问题排查从启动失败到连接失败5.1 问题速查表我把 Windows 下用 Docker 部署 RocketMQ 遇到的典型问题整理成一个表排查时可以直接对着看。症状可能原因解决方向Docker Desktop 无法启动报 virtualization 错误BIOS 虚拟化没开或 WSL2 功能未启用BIOS 打开 VT-x/SVM启用 WSL 和虚拟机平台重启容器起来后马上退出logs 里有 OOM 字样默认 JVM 内存超配被系统杀掉通过 JAVA_OPT_EXT 调小堆内存客户端报 connect 172.x.x.x:10911 failedBroker 注册了容器内 IPbroker.conf 显式设置 brokerIP1 为宿主机 IP客户端能连 NameServer但连不上 Broker防火墙拦截或端口映射漏配检查 Windows 防火墙确认 10909/10911/10912 已映射9876 端口被占用本机其他进程占了端口用 netstat 定位并处理占用进程或改映射端口Dashboard 连不上 NameServer环境变量未生效或地址填错在页面手动填namesrv:9876确认容器网络互通镜像拉取缓慢或失败网络到 Docker Hub 不稳定配置 registry-mirrors 加速地址5.2 几个高发问题的详细排查过程Docker Desktop 启动失败是环境层面最磨人的问题。一个比较隐蔽的原因是部分安全软件会拦截 Docker Desktop 对虚拟化组件的调用表现就是明明虚拟化和 WSL2 都开了启动还是失败。我处理过的一台机器关了第三方的主动防御功能后 Docker Desktop 立刻正常。另外确认不要在精简版 Windows 系统上折腾家庭中文版和 LTSC 版本对这些功能组件的支持情况不一样缺组件时先补组件再谈启动。容器反复重启的问题往往出现在内存小或者开了很多程序的机器上。可以先用docker stats看各容器当前内存占用再用docker inspect rmqbroker看容器的 OOMKilled 标志是否变成 true。如果确实被 OOM 杀了把JAVA_OPT_EXT里的 Xmx 从 512m 降到 256m同时把 Dashboard 先停掉给 Broker 腾出空间。客户端连接不上 Broker 的排查路径要按链路从后往前查。先看 Broker 注册的 IP 是什么不确定就看日志里boot success那行再看宿主机能否正常访问 10911 端口可以用 PowerShell 的Test-NetConnection命令Test-NetConnection -ComputerName 127.0.0.1 -Port 10911如果 TcpTestSucceeded 返回 False排查顺序是容器端口映射是否配置、Windows 防火墙是否拦截、Broker 进程是否真的在监听。如果返回 True说明问题在客户端检查客户端的 namesrvAddr 能不能路由到 Broker 的注册地址。9876 端口被占用的处理也很常规。Windows 下用命令找到占用进程netstat -ano | findstr 9876拿到 PID 后如果确认不是关键进程直接在任务管理器里结束它或者用命令行强制结束taskkill /PID 进程号 /F这个过程推荐放在部署之前就做不然 compose 启动时才发现端口冲突日志又不会直接告诉你占用方是谁排查起来多绕一圈。关于防火墙如果同一局域网的其他机器无法访问 Dashboard或者远程客户端连不上 Broker优先级最高的检查项就是 Windows Defender 防火墙是否放行了 Docker Desktop。多数情况下 Docker Desktop 安装时会自动加规则但偶尔规则只覆盖了某个网络配置文件从其他子网的机器访问还是会被拦。手动新建一条入站规则放行 10911 和 18080 端口能做到注意边界别把防火墙完全关掉。6. 写在最后一点部署心得这套环境我反复部署过不下十次最大的体会是想清楚容器网络和资源边界比背熟启动命令重要得多。很多看起来很玄学的故障本质都是容器内外 IP 不一致惹的祸。配置brokerIP1、端口映射、客户端 namesrvAddr 这三者之间如果形成了一个闭合可通的路由RocketMQ 的容器化部署其实相当稳妥。还有一个小技巧分享给你不要在生产环境照搬本地这套单机配置。单测环境可以用autoCreateTopicEnabletrue图省事但生产环境必须关闭同时主从同步、刷盘策略、文件保留时间都要单独设计。本地环境的核心价值是快速验证功能和排查客户端问题它无法替代生产环境下的完整故障演练。如果你照着这个流程跑通了再往后可以试着在 compose 里再加一个 Broker 做双主模式体会一下客户端在 NameServer 引导下自动发现多个 Broker 的过程。到那一步你对 RocketMQ 的理解就不再只是“能用”而是开始摸到它集群设计的门道了。
返回列表