前言
在上一篇文章基于RocketMQ实现分布式事务我们完成基于消息队列实现分布式事务,为了方便后续的开发和环境统一,我们决定将RocketMQ
容器化部署到服务器上。所以这篇文章就来演示一下笔者基于docker-compose
完成RocketMQ
容器化的过程。
本篇文章为了保证连贯性,所以在部署过程的介绍中不会涉及笔者遇到的问题和排查思路,所以当读者根据本文进行部署遇到问题的时候,可以直接到达问题清单一栏中查阅是否有相同情况,然后根据笔者思路进行排查修复。
容器化操作步骤
编写docker-compose
需要基于docker-compose
开发就需要编写yml
文件,查阅网上资料笔者找到了这样一份yml
文件,内容比较长,笔者将配置参数含义都加以注释,读者可以根据需要进行修改,完成后将这份配置文件上传到服务器上即可。
# docker-compose 语法版本
version: '3'
services:# NameServer配置rocketmq-namesrv:# 镜像版本image: foxiswho/rocketmq:4.8.0# 容器名称container_name: rocketmq-namesrv# 重启restart: always# 端口映射ports:- 9876:9876volumes:# 日志文件、存储文件内外部容器卷映射- /app/cloud/rocketmq/namesrv/logs:/home/rocketmq/logs- /app/cloud/rocketmq/namesrv/store:/home/rocketmq/storeenvironment:# jvm参数配置,调整rocket-mq空间JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms64M -Xmx64M -Xmn64m"# 启动命令command: ["sh","mqnamesrv"]# 网络连接配置networks:rocketmq_net:aliases:- rocketmq-namesrv# broker配置rocketmq-broker:image: foxiswho/rocketmq:4.8.0container_name: rocketmq-brokerrestart: alwaysports:- 10909:10909- 10911:10911volumes:# 日志、存储、broker配置文件宿主和容器文件映射- /app/cloud/rocketmq/broker/logs:/home/rocketmq/logs- /app/cloud/rocketmq/broker/store:/home/rocketmq/store- /app/cloud/rocketmq/broker/conf/broker.conf:/home/rocketmq/broker.confenvironment:JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms64M -Xmx64M -Xmn64m"# 启动命令配置,指定broker配置地址,指定mq NameServer地址,设置topic自动创建为truecommand: ["sh","mqbroker","-c","/home/rocketmq/broker.conf","-n","rocketmq-namesrv:9876","autoCreateTopicEnable=true"]# 确保等NameServer启动后,在启动brokerdepends_on:- rocketmq-namesrvnetworks:rocketmq_net:aliases:- rocketmq-broker# mq控制台配置rocketmq-console:image: styletang/rocketmq-console-ngcontainer_name: rocketmq-consolerestart: alwaysports:- 8180:8080environment:JAVA_OPTS: "-Drocketmq.namesrv.addr=rocketmq-namesrv:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false"depends_on:- rocketmq-namesrvnetworks:rocketmq_net:aliases:- rocketmq-console# 自定义rocketmq容器网络配置为桥接模式确保当前容器和其他桥接容器、宿主机都是互通
networks:rocketmq_net:name: rocketmq_netdriver: bridge
上文我们指定了broker.conf
对应宿主机的位置为/app/cloud/rocketmq/broker/conf/broker.conf
,所以我们就需要在宿主机上创建这个文件,并添加相应配置,内容如下,读者可以根据提示自行修改:
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 所属集群名字
brokerClusterName=DefaultCluster
# broker 名字,注意此处不同的配置文件填写的不一样,如果在 broker-a.properties 使用: broker-a,
# 在 broker-b.properties 使用: broker-b
brokerName=broker-a
# 0 表示 Master,> 0 表示 Slave
brokerId=0
# nameServer地址,分号分割
# namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
# 启动IP,如果 docker 报 com.alibaba.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.0.120:10909> failed
# 解决方式1 加上一句 producer.setVipChannelEnabled(false);,解决方式2 brokerIP1 设置宿主机IP,不要使用docker 内部IP
brokerIP1=服务器ip地址
# 在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
# 是否允许 Broker 自动创建 Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
# 是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
# Broker 对外服务的监听端口
listenPort=10911
# 删除文件时间点,默认凌晨4点
deleteWhen=04
# 文件保留时间,默认48小时
fileReservedTime=120
# commitLog 每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
# ConsumeQueue 每个文件默认存 30W 条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
# destroyMapedFileIntervalForcibly=120000
# redeleteHangedFileInterval=120000
# 检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
# 存储路径
# storePathRootDir=/home/ztztdata/rocketmq-all-4.1.0-incubating/store
# commitLog 存储路径
# storePathCommitLog=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/commitlog
# 消费队列存储
# storePathConsumeQueue=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/consumequeue
# 消息索引存储路径
# storePathIndex=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/index
# checkpoint 文件存储路径
# storeCheckpoint=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/checkpoint
# abort 文件存储路径
# abortFile=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/abort
# 限制的消息大小
maxMessageSize=65536
# flushCommitLogLeastPages=4
# flushConsumeQueueLeastPages=2
# flushCommitLogThoroughInterval=10000
# flushConsumeQueueThoroughInterval=60000
# Broker 的角色
# - ASYNC_MASTER 异步复制Master
# - SYNC_MASTER 同步双写Master
# - SLAVE
brokerRole=ASYNC_MASTER
# 刷盘方式
# - ASYNC_FLUSH 异步刷盘
# - SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
# 发消息线程池数量
# sendMessageThreadPoolNums=128
# 拉消息线程池数量
# pullMessageThreadPoolNums=128
笔者在安装过程中,遇到broker
启动失败,错误码为253
原因后文会详述,解决方式是修改我们宿主机关于RocketMQ
的所属者和所属组
# 进入宿主机配置mq配置的文件夹,运行这条命令
chown -R 3000:3000 rocketmq/
前台启动观察日志
笔者将上文配置文件命名为mq.yml
,到mq.yml
文件所在位置,运行启动命令,注意由于本次部署为第一次部署,我们建议在前台运行方便观察日志确定服务运行情况。
docker-compose -f mq.yml up
当我们看到这段输出,就说明mq
的nameserver
启动成功了。
按照启动顺序,我们再来看看broker
,当我们在控制台看到boot success. serializeType=JSON and name server is rocketmq-namesrv:9876
就说明broker
也启动成功了。
而mq-console
则是一个控制台管理应用,看到spring boot
项目启动成功的文本也就说明启动成功了。
此时我们可以查看docker
进程就会发现这些项目端口映射是否和yml
配置的一致。
[root@xxx ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e9851c9bdd48 foxiswho/rocketmq:4.8.0 "sh mqbroker -c /hom…" 48 minutes ago Up 40 minutes 0.0.0.0:10909->10909/tcp, :::10909->10909/tcp, 9876/tcp, 10912/tcp, 0.0.0.0:10911->10911/tcp, :::10911->10911/tcp rocketmq-broker
2120e73fafd1 styletang/rocketmq-console-ng "sh -c 'java $JAVA_O…" 48 minutes ago Up 40 minutes 0.0.0.0:8180->8080/tcp, :::8180->8080/tcp rocketmq-console
6e8d0c48a533 foxiswho/rocketmq:4.8.0 "sh mqnamesrv" 48 minutes ago Up 40 minutes 10909/tcp, 0.0.0.0:9876->9876/tcp, :::9876->9876/tcp, 10911-10912/tcp rocketmq-namesrv
尝试打开RocketMQ控制台
确定完所有进程都正常启动,我们服务器必须先放行8180
端口,然后就可以访问mq-console
了,我们对着浏览器键入mq控制台的地址:
http://ip:8180/#/
这时我们就会看到控制台主界面,注意初次进入时界面为英文,读者可以右上角点击更换语言修改为中文,最终效果如下所示:
我们可以在集群一栏查看broker
是否和broker.conf
配置名字一致,同时也能监控查看近期消费情况,其余的主题、生产者、消费者等控制台信息笔者就不一一演示了,读者可以自行点击查看了解。
启动本地服务测试可用性
为了实现本地服务可以连接mq进行正常消费测试,我们的服务器必须放行10911
、10909
、9876
这几个端口,完成后修改各个spring boot
应用关于mq
的配置。
rocketmq:name-server: 服务器ip:9876producer:group: cloud-group
我们尝试将项目启动,并运行我们的测试代码,以笔者为例,运行的就是上一篇文章实现分布式事务的案例,完成后核服务控台、数据库数据是否正常修改。
确认上述步骤无误,我们再去观察mq控制台
可以发现此时消费主题也多了一个我们自定义的消费主题:
我们配置的两个Java进程服务消费者
也出现在控制台上,自此我们的应用消费
、mq控制台监控
容器化部署配置就完成了
确保服务部署无误之后,将docker
容器改为后台启动即可
docker-compose -f mq.yml up -d
问题清单
The compose file ‘./docker-compose.yaml‘ is invalid because:networks.rocketmq_net
在笔者启动docker-compose
时,服务器输出了这样一段错误,查阅网上资料发现是docker-compose
版本过低,导致无法识别这段语法,遇到这个问题我们升级docker-compose
版本就好了,以笔者为例,参考StackOverflow
的回答将其升级为1.28.5
就解决了这个问题。
curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
broker文件不存在
在启动过程中笔者控制台输出了这样一段错误,原因是笔者第一版mq
的yml配置
指定容器内部的broker
文件在etc目录
下,这个文件夹下并不存在这个文件,最后我们将broker.conf
改到home目录
下,配置内容为/app/cloud/rocketmq/broker/conf/broker.conf:/home/rocketmq/broker.conf
问题得以解决。
rocketmq-broker | java.io.FileNotFoundException: /etc/rocketmq/broker.conf (No such file or directory)
rocketmq-broker | at java.io.FileInputStream.open0(Native Method)
rocketmq-broker | at java.io.FileInputStream.open(FileInputStream.java:195)
rocketmq-broker | at java.io.FileInputStream.<init>(FileInputStream.java:138)
rocketmq-broker | at java.io.FileInputStream.<init>(FileInputStream.java:93)
rocketmq-broker | at org.apache.rocketmq.broker.BrokerStartup.createBrokerController(BrokerStartup.java:128)
rocketmq-broker | at org.apache.rocketmq.broker.BrokerStartup.main(BrokerStartup.java:58)
broker启动时输出exited with code 253
这个问题比较棘手,一开始笔者在控制台没有发现任何不正常的地方,排查半天网络问题都得不到解决,最终从网上得知是权限问题,最终在宿主机下所有关于mq的文件夹下
修改所属者和所属组问题得以解决
chown -R 3000:3000 rocketmq/
本着求知的欲望笔者开始推断这个命令的作用,查阅宿主机下没有这个uid
和gid为3000的用户,猜测有没有可能是在容器中存在一个需要操作宿主机权限的用户呢?对此笔者定位到mq容器id进入内部查看果然和猜测一致,在容器中确实存在一个名为rocketmq
且uid
和gid
都为3000
的用户:
# 进入容器
[root@xxxx ~]# docker exec -it e9851c9bdd48 bash
# 查看容器中是否存在uid和gid为3000的用户
[rocketmq@e9851c9bdd48 bin]$ cat /etc/passwd |grep 3000
rocketmq:x:3000:3000::/home/rocketmq:/bin/bash
本地服务无法注册到mq上
这个问题较好定位,我们本机运行mq进行测试时没有问题,而部署到生产就连接不上,很大概率是网络连接不通,确定我们mq进程的所有端口号尝试按需放行即可。所以我们平时遇到这类问题优先和之前运行成功的样本进行比对猜测错误的原因加以调试。
小结
本次mq容器化
部署整体过程相较之前遇到的问题会更多一些,从笔者的排查思路中看到,排查过程总是利用控制台输出关键字结合Google
(这也是笔者部署之初采用前台启动的原因),注意是Google搜索引擎
,该引擎定位问题速度相比百度和bing会更准确一些。
结合网上的种种方案推断解决思路加以调试,对于不懂的解决方式我们一定要吃透,就例如chown
这条命令,网上都没有解释问题的原因,对此笔者查阅了这条命令的作用查阅服务器关于用户信息反推到问题的源头,确保下次遇到类似问题解决速度会更快一些。
参考文献
docker-compose安装RocketMQ
关于 The compose file ‘./docker-compose.yaml‘ is invalid because:networks.fabric_network的解决方案
Deploy broker with docker-compose returns 253 error #17
Linux chown命令:修改文件和目录的所有者和所属组
Docker 搭建部署 RocketMQ 遇到broker 253问题
本地连接服务器上rocketmq 出现closeChannel: close the connection to remote address[] result: true
Docker 搭建部署 RocketMQ 遇到broker 253问题
docker run and get exited (253)
Unable to give network name in docker-compose