- name
- deploy-rocketmq
- description
- 部署 RocketMQ Studio 与开源 RocketMQ。当用户说 部署 studio、把 studio 部署到远程机器/测试机、更新远程 studio、单机部署开源 rocketmq、远程部署 rocketmq 集群、单机部署 rocketmq 测试客户端、部署 studio 测试集群、部署负载挂具、给 K8s 集群上消费负载、制造消费延迟 inflight 时触发。
# 部署 RocketMQ Studio / 开源 RocketMQ
四种部署模式,全部遵循同一条主线:
> **本地把源码打成 tar.gz → 复制到目标机器 → 在目标机器上构建镜像并启动。**
> 构建统一在目标机执行,复用目标机本地缓存(Maven `~/.m2`、docker 层缓存),
> 不做镜像中转。
| 模式 | 场景 |
|------|------|
| A. Studio 部署到目标机器 | 把本项目(server + web + mysql)部署到一台远程 Linux 机器,浏览器访问 |
| B. 单机部署开源 RocketMQ | 在目标机器部署纯净开源集群(nameserver + 双 broker + proxy),不带测试挂具 |
| C. 单机部署 RocketMQ 测试客户端 | 在目标机器部署模式 B 集群 + producer/consumer 1 TPS 轨迹挂具,供 Studio 调试 |
| D. K8s 集群负载挂具 | 对 K8s 上的 RocketMQ 集群,用 docker compose 在同 VPC 机器跑单容器负载(4 种 topic 类型 × remoting/grpc 双协议消费 + 可控消费延迟),供 Studio 观测消费延迟/inflight |
> ⚠️ 模式 A 可用 `deploy/deploy.sh` 一键执行;模式 B/C/D 无脚本,按手动步骤执行。
## 通用前置条件(目标机器)
- 已安装 `docker`(含 `docker compose`)。国内机器安装要点:
- 用阿里源:`dnf config-manager --add-repo=https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo`;
Alibaba Cloud Linux 3 需 `sed -i 's#$releasever#8#g' /etc/yum.repos.d/docker-ce.repo`(releasever=3 无对应目录)。
- 配 registry-mirrors(国内拉不动 Docker Hub,mysql:8.0 / dragonwell 等基础镜像必需),
写入 `/etc/docker/daemon.json` 的 `registry-mirrors`(如 `https://docker.m.daocloud.io` 等可用镜像站),
然后 `systemctl enable --now docker`。
- 用户已配置好到目标机的 SSH 密钥/证书访问(可免密登录;用
`ssh -o BatchMode=yes <user>@<host> whoami` 验证)。未配置时先与用户确认登录方式。
- 目标机器语言必须为 `en_US.UTF-8`:`export LANG=en_US.UTF-8`。SSH 会话内临时生效;
需持久化时追加到 `~/.bashrc` 或 `/etc/profile.d/`。用 `ssh <user>@<host> 'echo $LANG'` 验证。
- 放行所需端口:模式 A 放行 web 端口(默认 6789);模式 B/C 如需跨机访问放行
9876、10909、10911、10912、20909、20911、20912、8080、8081。
### 测试集群可直接操作(用户授权 2026-10-14)
为验证 studio 功能,**测试环境(ECS + ACK `rocketmq1` 命名空间;地址一律从本地 `export` 的
`SSH_TARGET` / `STUDIO_URL` 环境变量取,仓库不存真实 IP)可以直接操作,无需先问**:
含改 broker 配置、重启 broker / Pod、重启 ECS 上的 `rmq-loadgen` 容器、开 ACL、建演示 topic/group 等
破坏性动作。前提是操作后**恢复原状**,或在 `rocketmq-studio-docs/progress/` 当日文档里记录留存状态。
生产/预发集群不在此授权内。
操作前必须知道的三个坑(2026-10-14 实测踩过):
- **`rocketmq1` 的 broker store 是 emptyDir**(`broker-storage` volume 没有 PVC):**重启 broker Pod 会清空
全部消息与 topic / 订阅组配置**。重启后按 studio 元数据库的记录重建:`POST /api/topics/create`、
`POST /api/groups/create` 会把配置写回 broker;再重发演示消息、`docker restart rmq-loadgen`(ECS 上)、
重建集群内的演示消费者 Pod(裸 Pod 删了不会自愈,要重新 apply YAML)。
- **开 ACL 2.0(apache/rocketmq:5.5.0)的完整配方**:broker.conf 除 `authenticationEnabled=true`、
`authorizationEnabled=true` 外,还必须显式配 `authenticationProvider` / `authenticationMetadataProvider` /
`authorizationProvider` / `authorizationMetadataProvider` 四个类名(该镜像默认为空,只开两个开关会报
`The authenticationMetadataProvider is not configured`,连 mqadmin 都建不了首个用户);并用
`initAuthenticationUser={"username":...,"password":...,"userType":"Super"}`(**User 对象的 JSON**)播种
首个 Super 账号。该账号要用 studio 的 admin 凭据同一对 AK/SK(在 ECS `/opt/rocketmq-studio/.env` 的
`SPRING_APPLICATION_JSON` → `studio.cluster.admin.credentials.<adminCredentialRef>`),否则开关一开
studio 自己的 admin 通道先被拒。验完把这几行删掉再重启即恢复。
- **broker ACL 2.0 的 resource 必须带类型前缀**:`Topic:<name>` / `Group:<name>`,集群域用 `*`。
裸名字会被解析成 null resource,整条账号写入报 `CODE: 16 The resource is null`。
---
## 模式 A:部署 Studio 到目标机器
> 🚀 **一键脚本**:`deploy/deploy.sh [all|server|web]` 已实现本模式全流程
> (打包 → 传输 → 目标机构建 → compose 启动 → actuator/health 验证),
> `deploy/.env` 配置 `REMOTE_HOST` 等。后续部署优先用脚本;以下手动步骤用于首次初始化与排障。
### 核心原则
- **后端编译在目标机器上做**:复用目标机宿主机 `~/.m2/repository` 缓存,增量构建快。
- **国内目标机必须先检查 Maven 镜像源**(见步骤 2),否则依赖下载极慢或失败。
- 默认 `STUDIO_AUTH_LOGIN_REQUIRED=false` 免登录,方便直接查看效果;如需开启登录,
改为 `true` 并配好 admin 账号密码后重建 server。
### 1. 初始化部署目录与 .env(仅首次)
```bash
SSH="ssh <user>@<host>"
$SSH bash -s <<'EOF'
mkdir -p /opt/rocketmq-studio
docker network inspect rocketmq_net >/dev/null 2>&1 || docker network create rocketmq_net
cat > /opt/rocketmq-studio/.env <<'ENV'
TZ=Asia/Shanghai
MYSQL_ROOT_PASSWORD=rocketmq
STUDIO_AUTH_LOGIN_REQUIRED=false
STUDIO_AUTH_ADMIN_USERNAME=admin
STUDIO_AUTH_ADMIN_PASSWORD=rocketmq
RMQ_LLM_TOKEN=
RMQ_ANTHROPIC_BASE_URL=
ENV
chmod 600 /opt/rocketmq-studio/.env
EOF
```
- `MYSQL_ROOT_PASSWORD` 同时被 compose 用于 mysql 和 server 的 `SPRING_DATASOURCE_PASSWORD`,务必一致。
- compose 中 `rocketmq` 网络声明为 external(名 `rocketmq_net`),不存在时 `up` 会失败,必须先创建。
### 2. 检查并配置 Maven 阿里源(国内目标机必做)
后端 JAR 用 maven 容器挂载宿主机 `~/.m2` 编译,镜像源配置来自宿主机 `~/.m2/settings.xml`。
先检查:
```bash
$SSH 'grep -q "maven.aliyun.com" ~/.m2/settings.xml 2>/dev/null && echo OK || echo MISSING'
```
输出 `MISSING` 时帮目标机写入(已有 settings.xml 则把 mirror 合并进 `<mirrors>`,不要整文件覆盖):
```bash
$SSH 'mkdir -p ~/.m2 && cat > ~/.m2/settings.xml <<'"'"'XML'"'"'
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
<mirrors>
<mirror>
<id>aliyun</id>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>
XML'
```
### 3. 本地打包源码并复制到目标机
```bash
cd <项目根目录> # rocketmq-studio 仓库根
# rmqctl, LICENSE and NOTICE have to travel with the package: the server image is built with the
# repository root as its context, and the rmqctl stage of server/Dockerfile copies all three.
# deploy/.env is per-environment and rmqctl/bin is a local build artifact, so both stay out.
tar czf /tmp/src.tar.gz \
--exclude='web/node_modules' --exclude='web/dist' --exclude='server/target' \
--exclude='deploy/.env' --exclude='rmqctl/bin' \
LICENSE NOTICE server web deploy rmqctl
scp /tmp/src.tar.gz <user>@<host>:/opt/rocketmq-studio/
$SSH 'cd /opt/rocketmq-studio && rm -rf server web deploy rmqctl LICENSE NOTICE && tar xzf src.tar.gz && rm src.tar.gz'
```
### 4. 在目标机上编译后端 + 构建镜像
复用宿主机 `~/.m2` 缓存编译 JAR,再用 Dockerfile 的 `runtime-prebuilt` target 打镜像
(不要走默认多阶段 build target,那会在 Docker 内从零下载依赖、用不上宿主机缓存)。
**构建上下文是仓库根目录而不是 `server/`**:Dockerfile 的 rmqctl 阶段要 `COPY rmqctl/` 与根目录的
`LICENSE NOTICE`,镜像才会自带 `/usr/local/bin/rmqctl`(AI 后端以 `rmqctl mcp stdio` 拉起 MCP server):
```bash
$SSH 'cd /opt/rocketmq-studio && \
docker run --rm -e HOME=/tmp \
-v $PWD/server:/app -v $HOME/.m2:/maven-cache -w /app \
maven:3.9.16-eclipse-temurin-21 \
mvn -B -ntp -s /maven-cache/settings.xml -Dmaven.repo.local=/maven-cache/repository package -DskipTests && \
docker build --target runtime-prebuilt -f server/Dockerfile -t rocketmq-server:latest .'
```
- 若目标机 `~/.m2/settings.xml` 不存在(海外机器通常不需要镜像源),去掉 `-s /maven-cache/settings.xml`。
- maven 基础镜像本身也要拉取,国内依赖通用前置条件里的 registry-mirrors。
构建 web(页脚「构建 <短commit>」由 `VITE_GIT_COMMIT` 注入,先取当前 git 短 commit):
```bash
$SSH 'cd /opt/rocketmq-studio && \
docker build --build-arg VITE_GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo dev) -t rocketmq-web:latest web/'
```
### 5. 启动与验证
```bash
$SSH 'cd /opt/rocketmq-studio && docker compose --env-file .env -f deploy/docker-compose.yml up -d'
# 三容器均 healthy 即成功
$SSH 'docker ps'
$SSH 'docker exec rocketmq-server curl -fsS http://localhost:8888/actuator/health'
curl -s -o /dev/null -w 'web %{http_code}\n' http://<host>:6789/
```
### 6. 更新代码后重新部署
重复步骤 3 → 5 即可。注意:
- mysql 数据在独立数据卷 `mysql-data`,重建 server/web 不影响数据;**不要 `down -v`**。
- 仅改 server 时只需重编 server(步骤 4 前半段)并 `docker compose ... up -d rocketmq-server`。
### 可选:注入演示数据
`deploy/mysql/upgrade-demo-instance.sql` / `upgrade-demo-acl.sql` 可注入演示实例/ACL。
导入后必须补 vendor,否则 `/api/instances` 报 500(demo SQL 早于 vendor 功能):
```bash
$SSH 'docker exec rocketmq-studio-mysql mysql -uroot -procketmq rocketmq \
-e "UPDATE rmq_instance SET vendor=\"APACHE\" WHERE vendor IS NULL OR vendor=\"\";"'
```
---
## 模式 B:单机部署开源 RocketMQ(纯净集群)
在目标机器部署一套开源 RocketMQ 集群(NameServer + 双 Broker + Proxy,集群名
`rocketmq-studio`,Broker 开启消息轨迹),不带 producer/consumer 测试挂具,
供任意客户端或 Studio 接入。部署产物在仓库 `deploy/rocketmq/` 目录。
### 1. 本地打包并复制到目标机
```bash
SSH="ssh <user>@<host>"
tar czf /tmp/rocketmq-deploy.tar.gz -C deploy rocketmq
scp /tmp/rocketmq-deploy.tar.gz <user>@<host>:/tmp/
$SSH 'mkdir -p /opt/rocketmq && rm -rf /opt/rocketmq/rocketmq && \
tar xzf /tmp/rocketmq-deploy.tar.gz -C /opt/rocketmq && rm /tmp/rocketmq-deploy.tar.gz'
```
### 2. 在目标机上构建镜像
```bash
$SSH 'cd /opt/rocketmq/rocketmq && ./build.sh'
```
- `build.sh` 自动判断地理位置:国内切阿里 apt/Maven 源,国外用默认源。
- Dockerfile 为多阶段构建:builder 阶段 `git clone --depth 1 -b rocketmq-all-5.5.0
https://github.com/apache/rocketmq.git` 后执行
`mvn -Prelease-all -DskipTests clean install`,运行阶段仅保留
`bin/ conf/ lib/` 产物,基础镜像 `alibabadragonwell/dragonwell:17-anolis`(yum 系,国内拉取快)。
- 构建阶段需访问 github.com,国内机器若 clone 慢或失败,用
`--build-arg ROCKETMQ_REPO=<镜像仓库地址>` 换源。
- 首次构建需 10-20 分钟;默认钉已发布 tag(与 `server/pom.xml` 的 `rocketmq.version` 对齐),
换版本用 `./build.sh --build-arg ROCKETMQ_BRANCH=rocketmq-all-5.5.1` 并同步改
`docker-compose.yml` 的 image tag;跟未发布分支用 `ROCKETMQ_BRANCH=develop`(仅限本地排查,
不要把 develop 钉回仓库)。
### 3. 启动(仅核心服务)
```bash
$SSH 'docker network inspect rocketmq_net >/dev/null 2>&1 || docker network create rocketmq_net'
$SSH 'cd /opt/rocketmq/rocketmq && docker compose up -d nameserver broker-0 broker-1 proxy'
```
- broker-0 监听 10909/10911/10912,broker-1 在 broker-1.conf 里通过 `listenPort=20911`
直接监听 20909/20911/20912(fast/ha 端口自动 -2/+1),容器与宿主机端口 1:1 映射。
- 堆内存通过 `JAVA_OPT_EXT` 环境变量收敛(开源启动脚本会将其追加到 JVM 参数末尾,
覆盖脚本内置的大堆默认值),适合小内存环境。
### 4. 验证
```bash
$SSH 'cd /opt/rocketmq/rocketmq && docker compose ps'
$SSH 'cd /opt/rocketmq/rocketmq && \
docker compose exec -T nameserver sh bin/mqadmin clusterList -n nameserver:9876'
```
通过标准:clusterList 能看到 `rocketmq-studio` 集群下 `rocketmq-studio-0`、
`rocketmq-studio-1` 两个 broker,且 `#ACTIVATED=true`。
### 5. 网络接入说明
- **与 Studio 同机部署**:两边共用 `rocketmq_net` 网络,Studio server 容器直接用
`nameserver:9876` 接入(compose 默认 `STUDIO_ROCKETMQ_NAMESRV_ADDR=nameserver:9876`)。
- **跨机访问**:`brokerIP1=broker-{0,1}` 仅容器网络内可达,宿主机/其他机器直连需改
`conf/broker-{0,1}.conf` 为 `brokerIP1=<目标机器 IP>` 并重启 broker,同时放行
9876、10909、10911、10912、20909、20911、20912、8080、8081。
- proxy 对外提供 remoting 8080 / gRPC 8081 接入点,已关闭 topic 消息类型校验
(`enableTopicMessageTypeCheck=false`,避免自动建 topic 类型为 UNSPECIFIED 时发送被拒)。
---
## 模式 C:单机部署 RocketMQ 测试客户端
在目标机器部署模式 B 的集群,并额外拉起 Producer / Consumer 容器以 1 TPS 持续
收发消息并上报轨迹,供 RocketMQ Studio 调试使用。
### 1. 准备与构建
同模式 B 步骤 1 → 3,但启动全部服务(含 producer/consumer):
```bash
$SSH 'cd /opt/rocketmq/rocketmq && docker compose up -d'
```
producer / consumer 服务说明(容器名均带 `rmq-` 前缀):
| 服务 | 说明 |
|------|------|
| producer | 编译并运行 `clients/TraceProducer.java`,1 TPS 发送到 `StudioTest`(带 Key `studio-key-<n>`),`enableMsgTrace=true`,直连 `nameserver:9876` |
| consumer | 编译并运行 `clients/TraceConsumer.java`,Push 消费 `StudioTest`,`enableMsgTrace=true`,直连 `nameserver:9876` |
producer / consumer 直接用运行镜像自带 JDK + `lib/*` 依赖现场编译,无需额外构建。
### 2. 验证
```bash
# 收发日志:producer 每秒一条 SEND_OK,consumer 对应 consume
$SSH 'docker logs -f --tail 50 rmq-producer'
# 消息轨迹已落盘(RMQ_SYS_TRACE_TOPIC 有数据即轨迹链路打通)
$SSH 'cd /opt/rocketmq/rocketmq && docker compose exec -T broker-0 \
sh bin/mqadmin consumeMessage -n nameserver:9876 -t RMQ_SYS_TRACE_TOPIC -c 5'
```
验证通过标准:模式 B 的 clusterList 标准之外,producer 日志持续输出 `SEND_OK`;
`RMQ_SYS_TRACE_TOPIC` 能消费出轨迹数据。
### 3. 清理
```bash
$SSH 'cd /opt/rocketmq/rocketmq && docker compose down -v'
```
---
## 模式 D:K8s 集群负载挂具(4 种 topic 类型 × 双协议消费)
对部署在 K8s 上的 RocketMQ 集群(如社区 Helm chart 部署的 rocketmq1/rocketmq2),
在与集群**同 VPC** 的机器上用 docker compose 跑一个负载容器:
- producer 每秒向 4 种类型 topic(NORMAL / FIFO / DELAY / TRANSACTION)各发 1 条
- 每个 topic 挂 1 个 remoting push 消费者 + 1 个 gRPC simple 消费者,共 **8 个消费者**
- 每条消息消费时固定 sleep `CONSUME_DELAY_MS`(默认 15s)才返回 success,
使管控(Studio)能看到稳定的消费延迟与 inflight(稳态积压 ≈ TPS × 延迟秒数/组)
源码在 terrances 仓库 `project/rocketmq-loadgen/`(不放进 apache 仓库),
本地 `mvn package` 出 fat jar 后传到目标机器构建镜像。
### 1. 为 proxy 创建内网 SLB(关键知识点)
社区 Helm chart 的 Service 全部硬编码 headless(`clusterIP: None`),集群外机器
只能直连 Pod IP,而 **Pod IP 在 pod 重建后会变**。稳定做法是给 proxy 建一个
**内网 SLB**(LoadBalancer Service):
```yaml
apiVersion: v1
kind: Service
metadata:
name: <release>-proxy-slb # 如 rocketmq1-proxy-slb
namespace: <namespace>
annotations:
# 强制内网 SLB,禁止公网暴露(测试集群纪律:No public SLB)
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
spec:
type: LoadBalancer
selector: # 与 chart proxy Service 的 selector 一致
app.kubernetes.io/instance: <release>
app.kubernetes.io/name: proxy
ports:
- { name: remoting, port: 8080, targetPort: 8080, protocol: TCP }
- { name: grpc, port: 8081, targetPort: 8081, protocol: TCP }
```
```bash
kubectl apply -f proxy-slb.yaml
# 等待 EXTERNAL-IP 分配(约 10~30s)
kubectl -n <ns> get svc <release>-proxy-slb -w
Auf GitHub ansehen