本文为多宿主搭建,单机搭建可参考下方文章
https://www.cnblogs.com/CaesarLinsa/p/11037613.html
坑很多,配置要求比较严苛,请注意
多物理机搭建最大的问题是,默认bridge网络模式下docker容器使用的是bridge按照DHCP协议动态分配的子网IP,容器是虚拟网络容器,相对于外部网络是隔离的,所以无法通过hosts解析到外部IP,也无从连接其他mq节点
多宿主集群当前已知的搭建模式有:
① host的网络模式(- -net host)
② 插件(Calico flannel weave Docker Overlay)
③ overlay的网络模式
④ 内网DNS服务器提供域名解析
这里提供host网络模式的集群搭建,以两台物理机为例,网络环境为内网
172.16.22.72 (主机)
172.16.22.59 (备机)
172.16.22.240(VIP)
(零)、架构图
(一)、创建MQ容器
两种方法均可,均为增加hosts映射关系进行节点寻址,最终产出相同。建议采用方法一 ,后续节点扩增更灵活
- 方法一
1.创建hosts
两台机器都创建/opt/rabbitmq
目录,然后在rabbitmq目录下创建hosts文件
mkdir /opt/rabbitmq
cd /opt/rabbitmq
vi hosts
#ip 真实主机hostname(如果主机名为localhost会有一点问题,建议更改)
172.16.22.72 support
172.16.22.59 hr02
2.运行容器
当docker发现本地没有 rabbitmq:management
的镜像时会主动从仓库拉取,management
为带有管理页面的版本
#172.16.22.72
docker run -d --net host --name rabbit1 -v /opt/rabbitmq:/var/lib/rabbitmq:z -v /opt/rabbitmq/hosts:/etc/hosts -e RABBITMQ_ERLANG_COOKIE='RABBITMQ' rabbitmq:management
#172.16.22.59
docker run -d --net host --name rabbit2 -v /opt/rabbitmq:/var/lib/rabbitmq:z -v /opt/rabbitmq/hosts:/etc/hosts -e RABBITMQ_ERLANG_COOKIE='RABBITMQ' rabbitmq:management
- 方法二
#172.16.22.72
docker run -d --net host --name rabbit1 --add-host=support:172.16.22.72 --add-host=hr02:172.16.22.59 -e RABBITMQ_ERLANG_COOKIE='RABBITMQ' rabbitmq:management
#172.16.22.59
docker run -d --net host --name rabbit2 --add-host=support:172.16.22.72 --add-host=hr02:172.16.22.59 -e RABBITMQ_ERLANG_COOKIE='RABBITMQ' rabbitmq:management
【可选】
如果要将文件映射到宿主机方便以后迁移备份,可以添加文件映射命令如下。要注意,添加映射后不会开启管理页面的插件,需要进入容器手动启用插件,原因尚未探寻
#etc存放配置,lib存放数据库,log存放日志 -v /home/rabbitmq/etc/rabbitmq:/etc/rabbitmq -v /home/rabbitmq/lib/rabbitmq:/var/lib/rabbitmq ->v /home/rabbitmq/log/rabbitmq:/var/log/rabbitmq
开启插件
docker exec -it rabbit1 /bin/bash rabbitmq-plugins enable rabbitmq_management
【解释】
#采用host的网络模式 --net host #容器命名为rabbit1 --name rabbit1 #将宿主机的/opt/rabbitmq目录映射到容器中的/var/lib/rabbitmq目录, #z是一个标记,在selinux环境下使用 -v /opt/rabbitmq:/var/lib/rabbitmq:z #设置env 环境变量。这里的cookie可以为任意值,所有节点保持一致即可 -e RABBITMQ_ERLANG_COOKIE='RABBITMQ' #增加host --add-host=support:172.16.22.72
(二)、搭建集群
将除了第一个节点外的其他节点加入第一个节点,每个节点都执行如下命令。加入节点时,
加上--ram
命令则为创建内存节点,不加为硬盘节点
#进入容器
docker exec -it rabbit2 /bin/bash
#关闭应用
rabbitmqctl stop_app
#清除所有队列
rabbitmqctl reset
#加入节点
rabbitmqctl join_cluster --ram rabbit@support
#启动应用
rabbitmqctl start_app
#查看集群状态
rabbitmqctl cluster_status
#【备用】从rabbit@support主节点上移除rabbit@hr02节点
rabbitmqctl -n rabbit@support forget_cluster_node rabbit@hr02
【补充】:
tcp4369端口用于集群邻居发现;
tcp5671,5672端口用于AMQP 0.9.1 and 1.0 clients使用;
tcp15672端口用于http api与rabbitadmin访问,后者仅限在management plugin开启时;
tcp25672端口用于erlang分布式节点/工具通信
【个人理解】:
① 节点的全称默认为 【节点名字@主机名称】
② 假定节点2(hr02)要加入的节点1的全称是【rabbit@support】,rabbitmq首先会在同一网段/桥下寻找是否存在该节点,如果不存在,会在节点2配置的hosts中寻找support对应的ip,通过4369端口进行连接。如果连接成功,节点2会告知对方,节点2想要连接的对方的节点的名称是【rabbit@support】,对方则会按照【rabbit@support】的全名在本地进行节点搜索
③ 如果节点1的主机名称为localhost,那即使在节点2的hosts配置了【support - 目标ip】的映射关系,在节点1也无法搜索到【rabbit@support】这个节点,因为节点1的真实节点名称是【rabbit@localhost】
【节点操作】:
删除集群中的节点:
①进入待删除节点容器
②停止节点 rabbitmqctl stop_app
③进入主节点容器删除待删除节点 rabbitmqctl forget_cluster_node node_name
增加集群中的节点:
①进入待增加节点容器
②清除数据 rabbitmqctl reset
③加入集群 rabbitmqctl join_cluster 主节点名称
④启动节点 rabbitmqctl start_app
(三)、部署Haproxy
第三、四步的目的是创建主备haproxy和主备对应的监测kp,重点依旧是网络IP配置及VIP创建。
ha安装没什么好说的。kp装在宿主机、单独容器或ha容器都可以,只不过如果要装在ha容器或单独容器内要用host网络模式且容器运行的时候加- -privileged参数,否则对VIP有影响;如果kp是装在宿主机上,ha用什么网络模式都可以,kp只是一个全端口转发。
建议将kp装在ha容器内,方便通过脚本监控ha的存活状态。
1.编辑haproxy配置文件如下:
mkdir /home/haproxy
vi /home/haproxy/haproxy.cfg
global
daemon
maxconn 10000
#日志输出配置,所有日志都记录在本机,以local0的日志级别(系统不可用)输出
#local0~local7
# emerg 0-系统不可用 alert 1-必须马上采取行动的事件
# crit 2-关键的事件 err 3-错误事件
# warning 4-警告事件 notice 5-普通但重要的事件
# info 6-有用的信息 debug 7-调试信息
log 127.0.0.1 local0 info
defaults
mode http
#应用全局的日志配置
log global
#超时配置
timeout connect 5000ms
timeout client 5000ms
timeout server 5000ms
timeout check 2000ms
#负载均衡的名字(自定义)
#监听5666端口并转发到rabbitmq服务
listen rabbitmq_cluster
bind 0.0.0.0:5666#对外提供的虚拟的端口
option tcplog
mode tcp
#负载均衡算法为轮询
balance roundrobin
#对后端服务器的健康状况检查间隔为2000毫秒,
#连续2次健康检查成功,则认为是有效的,连续3次健康检查失败,则认为服务器宕机
server rabbit1 172.16.22.72:5672 check inter 5000ms rise 2 fall 3
server rabbit2 172.16.22.59:5672 check inter 5000ms rise 2 fall 3
#haproxy的客户页面
listen http_front
bind 0.0.0.0:25666
stats uri /haproxy #页面地址
#页面的用户名和密码,建议主备设为不同,方便确定抢占到VIP的服务器是主机还是备机
stats auth root:ruijie
stats admin if TRUE #管理界面,成功登陆后可通过webui管理节点
#rabbit管理页面,监听15666端口转发到rabbitmq的客户端
listen rabbitmq_admin
bind 0.0.0.0:15666
server rabbit1 172.16.22.72:15672 check inter 5000ms rise 2 fall 3
server rabbit2 172.16.22.59:15672 check inter 5000ms rise 2 fall 3
2.创建haproxy容器
这里采用host模式进行创建,使用宿主机的网卡,否则KP创建的VIP是容器内VIP而不是容器外VIP
#-v 中的参数:ro表示read only,宿主文件为只读。如果不加此参数默认为rw,即允许容器对宿主文件的读写
#一定要添加--privileged参数,使用该参数,container内的root拥有真正的root权限。
#否则,container内的root只是外部的一个普通用户权限(无法创建网卡)
docker run -d --name cluster-rabbit-haproxy --privileged --net host -v /home/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro haproxy
3.在备机上以同样的配置和命令创建一台备份haproxy
(四)、安装keepalived
这里采用的方式是将kp安装在haproxy容器内
1.进入haproxy容器
docker exec -it cluster-rabbit-haproxy /bin/bash
2.更新,安装keepalived
apt-get update
apt-get install keepalived
3.安装ifconfig,安装ping,安装vim,安装ps
apt-get install net-tools
apt-get install iputils-ping
apt-get install vim
apt-get install procps
4.创建kp配置文件
vi /etc/keepalived/keepalived.conf
#keepalived配置文件
global_defs {
router_id NodeA #路由ID, 主备的ID不能相同
notification_email {
xxx@xxx.com
}
notification_email_from xxx@xxx.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
vrrp_skip_check_adv_addr
#在keepalived的服务器上配合使用nginx或haproxy时,需要把这一项注掉,否则VIP ping不通,80端口也无法正常访问
# vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
#自定义监控脚本
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 5
weight 2
}
vrrp_instance VI_1 {
state MASTER #Master为主机,备机设为BACKUP
interface ens160 #指定网卡(宿主机真实网卡,ip a查看)
virtual_router_id 1
priority 100 #优先级,BACKUP机器上的优先级要小于这个值
advert_int 1 #设置主备之间的检查时间,单位为s
authentication { #定义验证类型和密码,主备需相同
auth_type PASS
auth_pass ruijie
}
track_script {
chk_haproxy #ha存活监控脚本
}
virtual_ipaddress { #VIP地址,可为多个。如果有需要可以部署双机双VIP
172.16.22.240
}
}
【补充】关于双机双VIP(参考@dloux_zc)
A B 双机, 正常情况下A绑定vip1, B绑定vip2;顶层通过dns解析将不同的域名分别指向其中一个vip, 以充分利用服务器资源;
在发生故障时,A或B上同时绑定两个VIP。
在大流量的情况下不建议这么用,如果AB 都接近满载,一旦发生故障,其中一台的流量全部导到另一台,可能很快将另一台服务器也压崩。
仅作测试及小流量情况又不想浪费备机资源的情况下使用。
5.配置监控脚本
vi /etc/keepalived/check_haproxy.sh
#!/bin/bash
if [ $(ps -C haproxy --no-header | wc -l) -eq 0 ];then
haproxy -f /usr/local/etc/haproxy/haproxy.cfg
fi
sleep 2
if [ $(ps -C haproxy --no-header | wc -l) -eq 0 ];then
#service keepalived stop
/etc/init.d/keepalived stop
fi
6.启动
service keepalived start
7.查看配置是否成功
ip a
使用的网卡出现虚拟ip,则为配置成功
注:ha容器重启之后kp不会自动重启
至此,高可用的MQ集群已经搭建完毕,对外暴露5666端口进行MQ通信,15666端口进行MQ可视化管理,ip:25666/haproxy地址进行ha管理
(五)、配置镜像队列(可选)
增加一条策略即可
Name为自定义名称;Pattern为正则匹配,^为匹配全部;Definition为具体策略与规则,ha-mode配置高可用模式,all为全部
队列只能定义在一个节点上,普通模式队列仅存在于一个节点,镜像模式队列存在于所有节点,+1表示额外存在的队列数。如果是+2就表示在另外两个节点也存在此队列,即存在另外两个节点
(六)、测试
访问haproxy的管理页面: http://172.16.22.240:25666/haproxy
连接rabbitmq 的5666端口并发送数据,可以在此页面,对每次的请求转发进行监控
访问rabbitmq的客户端: http://172.16.22.240:15666
该页面为ha对mq集群的轮询访问
(七)、连接异常的问题处理
参考: https://www.cnblogs.com/xishuai/p/rabbitmq-and-haproxy-channel-shutdown-connection-error.html
SpringBoot连接集群可能会有 Channel shutdown: connection error
连接错误的问题,
报错信息如下:
2019-11-18 16:55:23 INFO o.s.a.r.l.SimpleMessageListenerContainer: Restarting Consumer@7925e772: tags=[[amq.ctag-NKT1PBwEVFNlR6zlAklF4A, amq.ctag-QHdxvp2TeHV-7d26AonMPA]], channel=Cached Rabbit Channel: AMQChannel(amqp://guest@172.16.22.240:5666/AS_System_Event,2), conn: Proxy@fe156f4 Shared Rabbit Connection: SimpleConnection@bb21063 [delegate=amqp://guest@172.16.22.240:5666/AS_System_Event, localPort= 61176], acknowledgeMode=MANUAL local queue size=0
2019-11-18 16:55:23 INFO o.s.a.r.c.CachingConnectionFactory: Attempting to connect to: [172.16.22.240:5666]
2019-11-18 16:55:23 INFO o.s.a.r.c.CachingConnectionFactory: Created new connection: connectionFactory#68c4a860:1/SimpleConnection@f36276b [delegate=amqp://guest@172.16.22.240:5666/AS_System_Event, localPort= 61190]
2019-11-18 16:55:27 ERROR o.s.a.r.c.CachingConnectionFactory: Channel shutdown: connection error
从红框可以看得出来,springboot一直在重复【重启消费者——连接服务端——创建新连接——连接异常】,而绿框则为轮询请求的两个服务端。参考链接文章确定问题为客户端连接超时:
为什么会出现此问题呢?因为 HAProxy 配置了客户端连接超时参数
timeout client ms
,如果客户端连接超过配置的此参数,那么 HAProxy 将会删除这个客户端连接。
RabbitMQ 客户端使用永久连接到代理,从不超时,那为什么还会出现问题?因为如果 RabbitMQ 在一段时间内处于非活动状态,那么 HAProxy 将自动关闭连接
引用原文:
说了那么多,我们该怎么解决此问题呢?
两种方案:
① 修改系统的tcp_keepalive_time
配置,间隔时间低于 HAProxy 配置的timeout client
超时时间(因为有可能影响其他系统服务,不推荐)。
② 修改 HAProxy 中的timeout client
超时时间,配置大于系统的tcp_keepalive_time
间隔时间(推荐)。因为系统tcp_keepalive_time
发送TCP keepalive
数据包间隔时间是 2 个小时,所以,我们将 HAProxy 中的timeout client
超时时间,设置为 3 个小时
配置文件完整示例:
global
daemon
...
defaults
...
listen rabbitmq_cluster
bind 0.0.0.0:5666
option tcplog
timeout client 3h
timeout server 3h
mode tcp
balance roundrobin
server rabbit1 172.16.22.72:5672 check inter 5000ms rise 2 fall 3
server rabbit2 172.16.22.59:5672 check inter 5000ms rise 2 fall 3
listen http_front
...
listen rabbitmq_admin
...
重新运行 HAProxy,系统正常运行无异常
参考:
https://blog.csdn.net/qq_21108311/article/details/82973763#commentBox
https://www.cnblogs.com/CaesarLinsa/p/11037613.html
https://blog.csdn.net/kevin3101/article/details/86579311