Docker搭建多机多节点haproxy+keepalived负载均衡的高可用RabbitMQ集群

本文为多宿主搭建,单机搭建可参考下方文章
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)

(零)、架构图

image

(一)、创建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

image

  【补充】:

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,则为配置成功


image

  注:ha容器重启之后kp不会自动重启

  至此,高可用的MQ集群已经搭建完毕,对外暴露5666端口进行MQ通信,15666端口进行MQ可视化管理,ip:25666/haproxy地址进行ha管理

(五)、配置镜像队列(可选)

  增加一条策略即可

image

  Name为自定义名称;Pattern为正则匹配,^为匹配全部;Definition为具体策略与规则,ha-mode配置高可用模式,all为全部
image

  队列只能定义在一个节点上,普通模式队列仅存在于一个节点,镜像模式队列存在于所有节点,+1表示额外存在的队列数。如果是+2就表示在另外两个节点也存在此队列,即存在另外两个节点
image

(六)、测试

  访问haproxy的管理页面: http://172.16.22.240:25666/haproxy
  连接rabbitmq 的5666端口并发送数据,可以在此页面,对每次的请求转发进行监控

image

  访问rabbitmq的客户端: http://172.16.22.240:15666
  该页面为ha对mq集群的轮询访问
image

(七)、连接异常的问题处理

参考: 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 将自动关闭连接

image

引用原文:

image

说了那么多,我们该怎么解决此问题呢?
两种方案:
① 修改系统的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343