前言
如有錯誤,隨時斧正,非常感謝!
為什麼要使用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 協議》,轉載必須註明作者和本文連結