RabbitMQ+haproxy+keepalived使用
前言
如有错误,随时斧正,非常感谢!
为什么要使用haproxy+keepalived呢?
为了AMQP服务的稳定性
首先先说下为什么要使用haproxy。
我在两台服务器上建了AMQP集群,分别是10.71.13.24和10.71.13.25,以后简称为24、25服务器。假设AMQP客户端直接连接24或25。如果24|25服务器宕机了,那么连接24|25的AMQP客户端就无法工作,消费者也无法进行正常消费(以下以24举例代表24|25单机的情况)。再者单机连接可能造成单机负载过高,而其他服务器空闲的状态。此时则可以通过haproxy实现负载均衡。
在本地的配置如:
global
#定义haproxy 日志级别[error warring info debug]日志输出配置。
#所有日志都记录在本机,通过local1输出。具体可以查看本机的`/etc/rsyslog.conf`
log 127.0.0.1 local1 notice
daemon #以后台形式运行harpoxy
nbproc 2 #设置进程数量
maxconn 4096 #默认最大连接数,需考虑ulimit-n限制
#user haproxy #运行haproxy的用户
pidfile /var/run/haproxy.pid #haproxy 进程PID文件
#group www #运行haproxy的用户所在的组
#ulimit-n 65535 #ulimit 的数量限制
#chroot /usr/share/haproxy #chroot运行路径
#debug #haproxy 调试级别,建议只在开启单进程的时候调试
#quiet
########默认配置############
defaults
log global
log 127.0.0.1 local0 info
mode tcp #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
option tcplog #日志类别,采用httplog
option dontlognull #不记录健康检查日志信息
retries 3 #两次连接失败就认为是服务器不可用,也可以通过后面设置
#option forwardfor #如果后端服务器需要获得客户端真实ip需要配置的参数,可以从Http Header中获得客户端ip
#option httpclose #每次请求完毕后主动关闭http通道,haproxy不支持keep-alive,只能模拟这种模式的实现
option redispatch #当serverId对应的服务器挂掉后,强制定向到其他健康的服务器,以后将不支持
option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
maxconn 4096 #默认的最大连接数
#timeout http-keep-alive10s #默认持久连接超时时间
#timeout http-request 10s #默认http请求超时时间
#timeout queue 1m #默认队列超时时间
balance roundrobin #设置默认负载均衡方式,轮询方式
#balance source #设置默认负载均衡方式,类似于nginx的ip_hash
#balnace leastconn #设置默认负载均衡方式,最小连接数
timeout connect 5s #连接超时
timeout client 120s # 客户端超时
timeout server 120s #服务端超时
timeout check 2000 #心跳检测超时ms
#绑定配置
listen rabbitmq_cluster
bind 0.0.0.0:5678
#配置TCP模式,一定得配置tcp模式,因为RabbitMQ的连接是TCP。之前大意配错了没发现导致报错如下。
#`Invalid frame type 72`
mode tcp
balance roundrobin
#RabbitMQ集群节点配置
server rmq_node_13_24 10.71.13.24:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node_13_25 10.71.13.25:5672 check inter 5000 rise 2 fall 3 weight 1
# server <name> <ip>:<port> check inter <value> rise <value> fall <value> weight <value>
# check inter <value>:每隔5000ms检测AMQP服务是否可用
# rise <value>:检测到2次可用则可被确认再次可用。
# fall <value>:检测到3次无法连接则认为服务不可用
# weight <value>:权重
配置完haproxy.cfg之后,启动haproxy:sudo /etc/init.d/haproxy start
。(haproxy启动脚本见最后)
配置解析:之前连接的socket是10.71.13.24:5672
,现在连接的socket是10.71.13.24:5678
。在25也可以按24的配置COPY一份,因此无论连接的是哪个服务器的5678端口最终都能实现负载均衡。然后还是有一个问题没有解决,如果某个服务器宕机了怎么办?这就是为什么要引入keepalived了。
再谈keepalived的使用
关于keepalived的原理我不熟,也就不过多的去解读。目前我只是想要解决我碰到的问题。为了解决AMQP最终能够稳定服务,于是申请了一个虚拟IP-VIP:10.71.13.254。通过这个VIP则可以开始配置keepalived.conf了。
! Configuration File for keepalived
global_defs {
router_id Node_Master # 路由ID,主备的不能相同
}
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 5
weight 2
}
vrrp_instance VI_1 {
state MASTER #keepalived的角色。Master表示主服务器,从服务器设置为BACKUP
interface eth1 #指定检测网卡,配置成eth0之后我无法连接到AMQP服务
virtual_router_id 1
priority 100 #优先级,BACKUP机器上的优先级至少小于50
advert_int 1 #设置主备之间的检测时间,单位为s
authentication {
auth_type PASS
auth_pass root123
}
track_script {
chk_haproxy
}
virtual_ipaddress { #VIP地址,可以设置多个
10.71.13.254
}
}
virtual_server 10.71.13.254 5672 { # 设置虚拟服务器
delay_loop 6 #设置运行情况检查时间,单位s
lb_algo wrr #设置负载调度算法,共有rr、wrr、lc、wlc、lblc、lblcr、dh、sh 这8种
lb_kind DR #设置LVS实现的负载均衡机制方式 VS/DR
persistence_timeout 50 #指定在一定时间内来自同一IP的连接将会被转发到同一RealServer中
protocol TCP
# 这个real_server 即LVS的三大部分之一的RealServer,这里特指RabbitMQ服务
real_server 10.71.13.24 5678 { #配置服务节点
weight 1 #配置权重
TCP_CHECK {
nb_get retry 3
connect_timeout 3
delay_before_retry 3
connect_port 5672
}
}
real_server 10.71.13.25 5678 {
weight 1
TCP_CHECK {
nb_get retry 3
connect_timeout 3
delay_before_retry 3
connect_port 5672
}
}
}
virtual_server 10.71.13.254 15672 {
delay_loop 6
lb_algo wrr
lb_kind DR
persistence_timeout 50
protocol TCP
real_server 10.71.13.24 15672 {
weight 1
TCP_CHECK {
nb_get retry 3
connect_timeout 3
delay_before_retry 3
connect_port 15672
}
}
real_server 10.71.13.25 15672 {
weight 1
TCP_CHECK {
nb_get retry 3
connect_timeout 3
delay_before_retry 3
connect_port 15672
}
}
}
check_haproxy.sh
脚本的目的是为了防止haProxy服务已经不可用而keepalived可用的情况,该脚本会尝试重新启动haProxy,如果启动不成功则关闭keepalived。这样keepalived的VIP就会漂移到BACKUP备份机,从而继续保证服务。脚本的配置如下:
#! /bin/bash
if [ $(ps -C haproxy --no-header | wc -l) -eq 0 ]; then
sudo service haproxy restart
fi
sleep 2
if [ $(ps -C haproxy --no-header | wc -l) -eq 0 ]; then
sudo service keepalived stop
fi
在25服务器上的配置和24的服务器基本一致,注意修改属性:route_id、state、priority。
然后启动keepalived:sudo /etc/init.d/keepalived start
。(启动脚本见最后)
启动完keepalived之后,我们再次连接AMQP服务就变成了:10.71.13.254:5762
,因为master是24服务器,所以实际真实响应的服务是10.71.13.24:5678
和10.71.13.25:5678
。因为采用的是加权轮询且权重都是1,24和25会轮流响应VIP的请求。在haProxy中监听了5678端口,并且采用了轮询机制分发到24和25的5672端口。会发现keepalived的和haProxy都使用了轮询机制,能不能把其中的某一处去掉呢?增加了VIP之后如果master宕机之后,就会选举一个从机成为主机,接管原主机的服务。当人工修复好了原主机之后,从机会把服务重新还给主机去接管,自己还原成原来的状态。
haproxy和keepalived的启动脚本
haProxy的启动脚本
#! /bin/sh
set -e
PATH=/sbin:/bin:/usr/sbin:/usr/bin
PROGDIR=/opt/haproxy-1.7.11
PROGNAME=haproxy
DAEMON=$PROGDIR/$PROGNAME
CONFIG=$PROGDIR/conf/$PROGNAME.cfg
# PIDFILE=$PROGDIR/conf/$PROGNAME.pid
PIDFILE=/var/run/haproxy.pid
DESC="HAProxy daemon"
SCRIPTNAME=/etc/init.d/$PROGNAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
start()
{
echo -e "Starting $DESC: $PROGNAME\n"
$DAEMON -f $CONFIG
echo "."
}
stop()
{
echo -e "Stopping $DESC: $PROGNAME\n"
haproxy_pid="$(cat $PIDFILE)"
kill $haproxy_pid
echo "."
}
restart()
{
echo -e "Restarting $DESC: $PROGNAME\n"
$DAEMON -f $CONFIG -p $PIDFILE -sf $(cat $PIDFILE)
echo "."
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2
exit 1
;;
esac
exit 0
keepalived启动脚本:
#!/bin/sh
#
# Startup script for the Keepalived daemon
#
# processname: keepalived
# pidfile: /var/run/keepalived.pid
# config: /etc/keepalived/keepalived.conf
# chkconfig: - 21 79
# description: Start and stop Keepalived
# Source function library
. /etc/rc.d/init.d/functions
# Source configuration file (we set KEEPALIVED_OPTIONS there)
. /etc/sysconfig/keepalived
RETVAL=0
prog="keepalived"
start() {
echo -n $"Starting $prog: "
daemon keepalived ${KEEPALIVED_OPTIONS}
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
}
stop() {
echo -n $"Stopping $prog: "
killproc keepalived
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
}
reload() {
echo -n $"Reloading $prog: "
killproc keepalived -1
RETVAL=$?
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
restart)
stop
start
;;
condrestart)
if [ -f /var/lock/subsys/$prog ]; then
stop
start
fi
;;
status)
status keepalived
RETVAL=$?
;;
*)
echo "Usage: $0 {start|stop|reload|restart|condrestart|status}"
RETVAL=1
esac
exit $RETVAL
参考
《RabbitMQ实战指南》
本作品采用《CC 协议》,转载必须注明作者和本文链接