一、Nginx/keepalived/lvs的介紹
1.?nginx
1.1.??nginx簡介
Nginx是一個自由、開源、高性能及輕量級的HTTP服務器及反轉代理服務器。Nginx以其高性能、穩定、功能豐富、配置簡單及占用系統資源少而著稱。
Nginx?超越?Apache?的高性能和穩定性,使得國內使用?Nginx?作為?Web?服務器的網站也越來越多.
1.2.?基礎功能?
反向代理加速,簡單的負載均衡和容錯;
1.3.?優勢
1、Nginx專為性能優化而開發,性能是其最重要的考量,?實現上非常注重效率 。有報告表明能支持高達?50,000?個并發連接數。?
2、Nginx具有很高的穩定性。其它HTTP服務器,當遇到訪問的峰值,或者有人惡意發起慢速連接時,也很可能會導致服務器物理內存耗盡頻繁交換,失去響應,只能重啟服務器。
例如當前apache一旦上到200個以上進程,web響應速度就明顯非常緩慢了。而Nginx采取了分階段資源分配技術,使得它的CPU與內存占用率非常低。
3、nginx官方表示保持10,000個沒有活動的連接,它只占2.5M內存,就穩定性而言, nginx比其他代理服務器更勝一籌。
4、Nginx支持熱部署。它的啟動特別容易,?并且幾乎可以做到7*24不間斷運行,即使運行數個月也不需要重新啟動。你還能夠在不間斷服務的情況下,對軟件版本進行進行升級。?
5、Nginx采用C進行編寫,?不論是系統資源開銷還是CPU使用效率都高很多。
1.4.?安裝
見文檔
2.?keepalived
2.1.?簡介
Keepalived的作用是檢測web服務器的狀態,如果有一臺web服務器死機,或工作出現故障,Keepalived將檢測到,并將有故障的web服務器從系統中剔除,當web服務器工作正常后Keepalived自動將web服務器加入到服務器群中,這些工作全部自動完成,不需要人工干涉,需要人工做的只是修復故障的web服務器。
2.2.?作用
主要用作RealServer的健康狀態檢查以及LoadBalance主機和BackUP主機之間failover的實現。
3.?lvs
3.1.?LVS是什么
1、LVS的英文全稱是Linux Virtual Server,即Linux虛擬服務器。
2、它是我們國家的章文嵩博士的一個開源項目。
3.2.?LVS能干什么
1、?LVS主要用于多服務器的負載均衡。
2、它工作在網絡層,可以實現高性能,高可用的服務器集群技術。
3、它可把許多低性能的服務器組合在一起形成一個超級服務器。
4、它配置非常簡單,且有多種負載均衡的方法。
5、它穩定可靠,即使在集群的服務器中某臺服務器無法正常工作,也不影響整體效果。
6、可擴展性也非常好。
3.3.?nginx和lvs作對比的結果:
1、nginx工作在網絡的應用層,主要做反向代理;lvs工作在網絡層,主要做負載均衡。nginx也同樣能承受很高負載且穩定,但負載度和穩定度不及lvs。 ?
2、nginx對網絡的依賴較小,lvs就比較依賴于網絡環境。
3、在使用上,一般最前端所采取的策略應是lvs。?nginx可作為lvs節點機器使用。
3.4.?負載均衡機制
前面我們說了LVS是工作在網絡層。相對于其它負載均衡的解決辦法,它的效率是非常高的。LVS的通過控制IP來實現負載均衡。IPVS是其具體的實現模塊。IPVS的主要作用:安裝在Director Server上面,在Director Server虛擬一個對外訪問的IP(VIP)。用戶訪問VIP,到達Director Server,Director Server根據一定的規則選擇一個Real Server,處理完成后然后返回給客戶端數據。這些步驟產生了一些具體的問題,比如如何選擇具體的Real Server,Real Server如果返回給客戶端數據等等。IPVS為此有三種機制:
1.?VS/NAT(Virtual Server via Network Address Translation),即網絡地址翻轉技術實現虛擬服務器。
當請求來到時,Diretor server上處理的程序將數據報文中的目標地址(即虛擬IP地址)改成具體的某臺Real Server,端口也改成Real Server的端口,然后把報文發給Real Server。Real Server處理完數據后,需要返回給Diretor Server,然后Diretor server將數據包中的源地址和源端口改成VIP的地址和端口,最后把數據發送出去。由此可以看出,用戶的請求和返回都要經過Diretor Server,如果數據過多,Diretor Server肯定會不堪重負。
2.?VS/TUN(Virtual Server via IP Tunneling),即IP隧道技術實現虛擬服務器。
IP隧道(IP tunneling)是將一個IP報文封裝在另一個IP報文的技術,這可以使得目標為一個IP地址的數據報文能被封裝和轉發到另一個IP地址。IP隧道技術亦稱為IP封裝技術(IP encapsulation)。它跟VS/NAT基本一樣,但是Real server是直接返回數據給客戶端,不需要經過Diretor server,這大大降低了Diretor server的壓力。
3.?VS/DR(Virtual Server via Direct Routing),即用直接路由技術實現虛擬服務器。
跟前面兩種方式,它的報文轉發方法有所不同,VS/DR通過改寫請求報文的MAC地址,將請求發送到Real Server,而Real Server將響應直接返回給客戶,免去了VS/TUN中的IP隧道開銷。這種方式是三種負載調度機制中性能最高最好的,但是必須要求Director Server與Real Server都有一塊網卡連在同一物理網段上。
二、Nginx/keepalived/lvs安裝使用
nginx安裝文檔
1.?安裝依賴包
2.?安裝nginx
2.1.?上傳
2.2.?解壓
2.3.?重命名
2.4.?安裝nginx
3.?安裝JDK
3.1.?切換到root用戶:
3.2.?查看以前是不是安裝了openjdk:
3.3.?卸載openjdk:
(其中參數“tzdata-java-2013g-1.el6.noarch”為上面查看中顯示的結果,粘進來就行)
3.4.?安裝sunjdk
3.4.1.?上傳
3.4.2.?解壓
3.4.3.?創建快捷方式?
3.4.4.?配置環境變量
3.4.5.?重新編譯環境變量
4.?安裝tomcat
5.?重新配置nginx
1、cd /usr/local/nginx ?
2、vi /usr/local/nginx/nginx.conf
user ?nobody nobody;?#定義Nginx運行的用戶和用戶組
worker_processes ?4;?#nginx進程數,建議設置為等于CPU總核心數。
error_log ?logs/error.log?info;?#全局錯誤日志定義類型,[ debug | info | notice | warn | error | crit ]
worker_rlimit_nofile 1024;?#一個nginx進程打開的最多文件描述符數目,所以建議與ulimit -n的值保持一致。
pid?logs/nginx.pid;?#進程文件
#工作模式及連接數上限
events {
use epoll;#參考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本內核中的高性能網絡I/O模型
worker_connections ?1024;#單個進程最大連接數(最大連接數=連接數*進程數)
}
#設定http服務器,利用它的反向代理功能提供負載均衡支持
http {
include ??????mime.types;#文件擴展名與文件類型映射表
default_type ?application/octet-stream;#默認文件類型
#設定負載均衡的服務器列表
upstream ?tomcatxxxcom ?{ ?
?????server ??192.168.56.200:8080; ?
server ??192.168.56.201:8080;??
}
#設定日志格式
????log_format ?www_xy_com ?'$remote_addr - $remote_user [$time_local] "$request" '
??????????????????????'$status $body_bytes_sent "$http_referer" '
??????????????????????'"$http_user_agent" "$http_x_forwarded_for"';
sendfile ???????on;#開啟高效文件傳輸模式,sendfile指令指定nginx是否調用sendfile函數來輸出文件,對于普通應用設為?on,如果用來進行下載等應用磁盤IO重負載應用,可設置為off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。注意:如果圖片顯示不正常把這個改成off。
keepalive_timeout ?65; #長連接超時時間,單位是秒
????#gzip ?on;
#設定虛擬主機,默認為監聽80端口
????server {
????????listen ??????80;
server_name ?tomcat.xxx.com;#域名可以有多個,用空格隔開
????????#charset koi8-r;
#設定本虛擬主機的訪問日志
????????access_log ?/data/logs/access.log ?www_xy_com;
#對?"/"?啟用反向代理
???location / {
???proxy_pass ???????http://tomcatxxxcom; ?
???????????????proxy_set_header ??Host ????????????$host; ?
???????????????proxy_set_header ??X-Real-IP ???????$remote_addr; ?
???????????????proxy_set_header ??X-Forwarded-For ?$proxy_add_x_forwarded_for;
????????}
????????#error_page ??500 502 503 504 ?/50x.html;
????????location = /50x.html {
????????????root ??html;
????????}
????}
}
3、創建logs所需要的文件夾/data /logs/
cd /
mkdir –m 755 data
cd data
mkdir –m 755 logs
4、啟動tomcat、nginx。
/usr/local/tomcat/bin/startup.sh
/usr/local/nginx/sbin/nginx
5、修改hosts,加入
192.168.56.99 tomcat.xxx.com
6、訪問http:// tomcat.xxx.com
keepalived安裝文檔
1.?安裝依賴
su - root
yum -y install kernel-devel*
yum -y install openssl-*
yum -y install popt-devel
yum -y install lrzsz
yum -y install openssh-clients
2.?安裝keepalived
2.1.?上傳
1、cd /usr/local
2、rz?–y
3、選擇keepalived安裝文件
2.2.?解壓
tar –zxvf keepalived-1.2.2.tar.gz
2.3.?重命名
mv keepalived-1.2.2 keepalived
2.4.?安裝keepalived
1、cd keepalived
2、執行命令
./configure --prefix=/usr/local/keepalived -enable-lvs-syncd --enable-lvs --with-kernel-dir=/lib/modules/2.6.32-431.el6.x86_64/build
3、編譯
make
4、安裝
make install
2.5.?配置服務和加入開機啟動
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
mkdir -p /etc/keepalived
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
ln -s /usr/local/keepalived/sbin/keepalived /sbin/
chkconfig keepalived on
2.6.?修改配置文件
1、?vi /etc/keepalived/keepalived.conf
2、詳解:
global_defs {
notification_email {#指定keepalived在發生切換時需要發送email到的對象,一行一個
?????#acassen@firewall.loc
?????#failover@firewall.loc
?????#sysadmin@firewall.loc
???}
notification_email_from Alexandre.Cassen@firewall.loc#指定發件人
#smtp_server 192.168.200.1#指定smtp服務器地址
#smtp_connect_timeout 30 #指定smtp連接超時時間
router_id LVS_DEVEL#運行keepalived機器的一個標識
}
vrrp_instance VI_1 {
state?BACKUP#指定那個為master,那個為backup
interface?eth1#設置實例綁定的網卡
virtual_router_id 51#同一實例下virtual_router_id必須相同
????priority?100#定義優先級,數字越大,優先級越高,備機要小于主
advert_int 1#MASTER與BACKUP負載均衡器之間同步檢查的時間間隔,單位是秒
nopreempt#設置為不搶占,從啟動后主不會自動切換回來,?注:這個配置只能設置在backup主機上,而且這個主機優先級要比另外一臺高
authentication {#設置認證
????????auth_type PASS
????????auth_pass 1111
????}
????virtual_ipaddress{#設置vip
192.168.56.70#虛擬IP
????}
}
virtual_server 192.168.56.70 8080?{
delay_loop 6#健康檢查時間間隔
lb_algo rr #調度算法rr|wrr|lc|wlc|lblc|sh|dh
lb_kind?DR?#負載均衡轉發規則NAT|DR|RUN
????#nat_mask 255.255.255.0#需要驗證
persistence_timeout 1#會話保持時間
protocol TCP#使用的協議
????real_server 192.168.56.201 8080 {
weight 10 #默認為1,0為失效
????????SSL_GET {
url { #檢查url,可以指定多個
??????????????path /
digest ff20ad2481f97b1754ef3e12ecd3a9cc #檢查后的摘要信息
????????????}
????????????url {
??????????????path /mrtg/
??????????????digest 9b3a0c85a887a256d6939da88aabd8cd
????????????}
connect_timeout 3#連接超時時間
nb_get_retry 3#重連次數
delay_before_retry 3#重連間隔時間
????????}
????}
}??
3.?按照上面步驟安裝備機器
注意:備的配置文件不相同。
4.?兩臺機器啟動keepalived:
service keepalived start
5.?驗證
ip a
6.?監控
因為keepalive只能監控機器的死活,所以當軟件死掉后,keepalived仍然不會切換;
所以需要寫一個腳本,監控軟件的死活。
運行wangsf.sh,監控軟件
lvs安裝文檔
1.?安裝lvs應用模塊
1、安裝依賴包:
yum -y install ipvs*
2、驗證本機ip_vs模塊是否加載
[root@client lvs]# grep -i 'ip_vs' /boot/config-2.6.32-431.el6.x86_64
CONFIG_IP_VS=m
CONFIG_IP_VS_IPV6=y
# CONFIG_IP_VS_DEBUG is not set
CONFIG_IP_VS_TAB_BITS=12
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_PROTO_AH_ESP=y
CONFIG_IP_VS_PROTO_ESP=y
CONFIG_IP_VS_PROTO_AH=y
CONFIG_IP_VS_PROTO_SCTP=y
CONFIG_IP_VS_RR=m
CONFIG_IP_VS_WRR=m
CONFIG_IP_VS_LC=m
CONFIG_IP_VS_WLC=m
CONFIG_IP_VS_LBLC=m
CONFIG_IP_VS_LBLCR=m
CONFIG_IP_VS_DH=m
CONFIG_IP_VS_SH=m
CONFIG_IP_VS_SED=m
CONFIG_IP_VS_NQ=m
CONFIG_IP_VS_FTP=m
CONFIG_IP_VS_PE_SIP=m
2.?安裝lvs
2.1.?編寫lvs drsrever腳本:
2.1.1.?修改functions權限:
(functions這個腳本是給/etc/init.d里邊的文件使用的(可理解為全局文件)。)
chmod 755 /etc/rc.d/init.d/functions
2.1.2.?創建lvs文件夾
cd /usr/local
mkdir –m 755 lvs
cd /lvs
2.1.3.?編寫腳本
vi ?lvs_dr.sh
#!/bin/bash
#description:start lvs server
echo "1" >/proc/sys/net/ipv4/ip_forward ??#開啟ip轉發
WEB1=192.168.56.200?#真實的webip
WEB2=192.168.56.201?#真實的webip
VIP1=192.168.56.80?#虛擬lvs的ip
/etc/rc.d/init.d/functions??#初始化function
case "$1" in?#第一個參數
start)??#第一個參數是start
echo "start LVS of directorServer"?#打印
/sbin/ifconfig eth0:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up?#設置虛擬網絡
/sbin/ipvsadm?–C??#清除內核虛擬服務器表中的所有記錄,清除lvs設置
/sbin/ipvsadm -A -t $VIP1:8080 -s rr?#設置rr模式,輪詢模式
/sbin/ipvsadm -a -t $VIP1:8080 -r $WEB1:8080 –g?#輪詢的機器,-g采用DR模式
/sbin/ipvsadm -a -t $VIP1:8080 -r $WEB2:8080 –g
/sbin/ipvsadm?#啟動lvs
;;
stop)??#如果第一個參數是stop
echo "close LVS directorserver"?#打印
echo "0" >/proc/sys/net/ipv4/ip_forward?#關閉ip轉發
/sbin/ipvsadm?–C??#清除內核虛擬服務器表中的所有記錄
/sbin/ipvsadm?–Z??#虛擬服務表計數器清零(清空當前的連接數量等)
;;
*)?#如果第一個參數是其他任何值
echo "usage:$0 {start|stop}"??#打印:提示輸入start或者stop
exit 1??#退出
esac?#循環結束
2.1.4.?執行腳本
chmod 755 lvs_dr.sh
./lvs-dr.sh ?start
2.1.5.?查看:
ipvsadm –ln
看到上面信息說明ipvsadm啟動成功。
2.2.??編寫lvs realserver腳本
2.2.1.?在web1?和web2機器上修改functions權限:
(functions這個腳本是給/etc/init.d里邊的文件使用的(可理解為全局文件)。)
chmod 755 /etc/rc.d/init.d/functions
2.2.2.?在分別在web1?和web2服務器上創建lvs文件夾:
cd /usr/local
mkdir –m 755 lvs
cd lvs
rz –y
2.2.3.?編寫監本?
vi ?lvs-rs.sh
#!/bin/sh
VIP1=192.168.56.80??#虛擬ip
/etc/rc.d/init.d/functions?#初始化function
case "$1" in??#第一個參數
start)?#如果第一個參數是start
echo "start LVS of realserver"?#打印
/sbin/ifconfig lo:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up?#設置虛擬網絡
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore?#定義接收到ARP請求時的響應級別
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce?#定義將自己的地址向外通告時的級別
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
stop)?#如果第一個參數是stop
/sbin/ifconfig lo:0 down??#停止網卡
echo "close lvs dirctorserver"?#打印
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore?#定義接收到ARP請求時的響應級別
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce?#定義將自己的地址向外通告時的級別
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
*)
echo "usage:$0{start|stop}"
exit 1
esac
2.2.4.?啟動在web1?和web2機器上lvs:
chmod 755 lvs-rs.sh
./lvs-rs.sh start
3.?設置dr機器上設置連接超時值(秒)?
ipvsadm --set 1 1 1
4.?關閉
./lvs-rs.sh stop
./lvs-dr.sh stop
三、Nginx/keepalived/lvs集群安裝
1.?安裝tomcat
2.?安裝nginx
配置文件和之前的一樣
user ?nobody nobody;?#定義Nginx運行的用戶和用戶組
worker_processes ?4;?#nginx進程數,建議設置為等于CPU總核心數。
error_log ?logs/error.log?info;?#全局錯誤日志定義類型,[ debug | info | notice | warn | error | crit ]
worker_rlimit_nofile 1024;?#一個nginx進程打開的最多文件描述符數目,所以建議與ulimit -n的值保持一致。
pid?logs/nginx.pid;?#進程文件
#工作模式及連接數上限
events {
use epoll;#參考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本內核中的高性能網絡I/O模型
worker_connections ?1024;#單個進程最大連接數(最大連接數=連接數*進程數)
}
#設定http服務器,利用它的反向代理功能提供負載均衡支持
http {
include ??????mime.types;#文件擴展名與文件類型映射表
default_type ?application/octet-stream;#默認文件類型
#設定負載均衡的服務器列表
upstream ?tomcatxxxcom ?{ ?
?????server ??192.168.56.200:8080; ?
server ??192.168.56.201:8080;??
}
#設定日志格式
????log_format ?www_xy_com ?'$remote_addr - $remote_user [$time_local] "$request" '
??????????????????????'$status $body_bytes_sent "$http_referer" '
??????????????????????'"$http_user_agent" "$http_x_forwarded_for"';
sendfile ???????on;#開啟高效文件傳輸模式,sendfile指令指定nginx是否調用sendfile函數來輸出文件,對于普通應用設為?on,如果用來進行下載等應用磁盤IO重負載應用,可設置為off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。注意:如果圖片顯示不正常把這個改成off。
keepalive_timeout ?65; #長連接超時時間,單位是秒
????#gzip ?on;
#設定虛擬主機,默認為監聽80端口
????server {
????????listen ??????80;
server_name ?tomcat.xxx.com;#域名可以有多個,用空格隔開
????????#charset koi8-r;
#設定本虛擬主機的訪問日志
????????access_log ?/data/logs/access.log ?www_xy_com;
#對?"/"?啟用反向代理
???location / {
???proxy_pass ???????http://tomcatxxxcom; ?
???????????????proxy_set_header ??Host ????????????$host; ?
???????????????proxy_set_header ??X-Real-IP ???????$remote_addr; ?
???????????????proxy_set_header ??X-Forwarded-For ?$proxy_add_x_forwarded_for;
????????}
????????#error_page ??500 502 503 504 ?/50x.html;
????????location = /50x.html {
????????????root ??html;
????????}
????}
}
3.?安裝lvs
lvs-dr.sh:和之前對比,變化之處就是vip和轉發的端口。
#!/bin/bash
#description:start lvs server
echo "1" >/proc/sys/net/ipv4/ip_forward
WEB1=192.168.56.200
WEB2=192.168.56.201
VIP1=192.168.56.90
/etc/rc.d/init.d/functions
case "$1" in
start)
echo "start LVS of directorServer"
#set the Virtual address and sysctl parameter
/sbin/ifconfig eth1:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up
#clear ipvs table
/sbin/ipvsadm -C
#set LVS
#web apache or tomcat
/sbin/ipvsadm -A -t $VIP1:80 -s rr
/sbin/ipvsadm -a -t $VIP1:80 -r $WEB1:80 ?-g
/sbin/ipvsadm -a -t $VIP1:80 -r $WEB2:80 ?-g
#run LVS
/sbin/ipvsadm
;;
stop)
echo "close LVS directorserver"
echo "0" >/proc/sys/net/ipv4/ip_forward
/sbin/ipvsadm -C
/sbin/ipvsadm -Z
;;
*)
echo "usage:$0 {start|stop}"
exit 1
esac
lvs-rs.sh:與之前的不同在于修改了vip
#!/bin/sh
#description start realserver
#chkconfig 235 26 26
VIP1=192.168.56.90
/etc/rc.d/init.d/functions
case "$1" in
start)
echo "start LVS of realserver"
/sbin/ifconfig lo:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
stop)
/sbin/ifconfig lo:0 down
echo "close lvs dirctorserver"
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
*)
echo "usage:$0{start|stop}"
exit 1
esac
4.?安裝keepalived
注意:在用keepalived做tomcat和nginx的熱備時,需要加入realserver的配置。但是做lvs的熱備則不需要配置realserver,因為keepalived有lvs的配置參數。
backup
! Configuration File for keepalived
global_defs {
???notification_email {
?????#acassen@firewall.loc
?????#failover@firewall.loc
?????#sysadmin@firewall.loc
???}
???notification_email_from Alexandre.Cassen@firewall.loc
???#smtp_server 192.168.200.1
???#smtp_connect_timeout 30
???router_id LVS_DEVEL
}
vrrp_instance VI_1 {
????state BACKUP
????interface eth1
lvs_sync_daemon_inteface eth1
????virtual_router_id 51
????priority 100
nopreempt
????advert_int 1
????authentication {
????????auth_type PASS
????????auth_pass 1111
????}
????virtual_ipaddress {
????????192.168.56.90
????}
}
virtual_server 192.168.56.90 80 {
????delay_loop 6
????lb_algo rr
????lb_kind DR
????#nat_mask 255.255.255.0
????persistence_timeout 1
????protocol TCP
}
master
! Configuration File for keepalived
global_defs {
???notification_email {
?????#acassen@firewall.loc
?????#failover@firewall.loc
?????#sysadmin@firewall.loc
???}
???notification_email_from Alexandre.Cassen@firewall.loc
???#smtp_server 192.168.200.1
???#smtp_connect_timeout 30
???router_id LVS_DEVEL
}
vrrp_instance VI_1 {
????state MASTER
????interface eth1
lvs_sync_daemon_inteface eth1
????virtual_router_id 51
????priority 200
????advert_int 1
????authentication {
????????auth_type PASS
????????auth_pass 1111
????}
????virtual_ipaddress {
????????192.168.56.90
????}
}
virtual_server 192.168.56.90 80 {
????delay_loop 6
????lb_algo rr
????lb_kind DR
????#nat_mask 255.255.255.0
????persistence_timeout 1
????protocol TCP
?}
四、Zookeeper
1.?Zookeeper概念簡介:
Zookeeper是一個分布式協調服務;就是為用戶的分布式應用程序提供協調服務
A、zookeeper是為別的分布式程序服務的
B、Zookeeper本身就是一個分布式程序(只要有半數以上節點存活,zk就能正常服務)
C、Zookeeper所提供的服務涵蓋:主從協調、服務器節點動態上下線、統一配置管理、分布式共享鎖、統一名稱服務……
D、雖然說可以提供各種服務,但是zookeeper在底層其實只提供了兩個功能:
管理(存儲,讀取)用戶程序提交的數據;
并為用戶程序提供數據節點監聽服務;
Zookeeper常用應用場景:
Zookeeper集群的角色: ?Leader?和 ?follower ?(Observer)
只要集群中有半數以上節點存活,集群就能提供服務
2.?zookeeper集群機制
半數機制:集群中半數以上機器存活,集群可用。
zookeeper適合裝在奇數臺機器上!!!
3.?安裝
3.1.安裝
3.1.1.?機器部署
安裝到3臺虛擬機上
安裝好JDK
3.1.2.?上傳
上傳用工具。
3.1.3.?解壓
su –?hadoop(切換到hadoop用戶)
tar -zxvf zookeeper-3.4.5.tar.gz(解壓)
3.1.4.?重命名
mv zookeeper-3.4.5 zookeeper(重命名文件夾zookeeper-3.4.5為zookeeper)
3.1.5.?修改環境變量
1、su?–?root(切換用戶到root)
2、vi /etc/profile(修改文件)
3、添加內容:
export ZOOKEEPER_HOME=/home/hadoop/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
4、重新編譯文件:
source /etc/profile
5、注意:3臺zookeeper都需要修改
6、修改完成后切換回hadoop用戶:
su - hadoop
3.1.6.?修改配置文件
1、用hadoop用戶操作
cd zookeeper/conf
cp zoo_sample.cfg?zoo.cfg
2、vi zoo.cfg
3、添加內容:
dataDir=/home/hadoop/zookeeper/data
dataLogDir=/home/hadoop/zookeeper/log
server.1=slave1:2888:3888 (主機名,?心跳端口、數據端口)
server.2=slave2:2888:3888
server.3=slave3:2888:3888
4、創建文件夾:
cd?/home/hadoop/zookeeper/
mkdir -m 755 data
mkdir -m 755 log
5、在data文件夾下新建myid文件,myid的文件內容為:
cd data
vi myid
添加內容:略
3.1.7.?將集群下發到其他機器上
scp -r /home/hadoop/zookeeper hadoop@slave2:/home/hadoop/
scp -r /home/hadoop/zookeeper hadoop@slave3:/home/hadoop/
3.1.8.?修改其他機器的配置文件
到slave2上:修改myid為:2
到slave3上:修改myid為:3
3.1.9.?啟動(每臺機器)
zkServer.sh start
3.1.10.?查看集群狀態
1、?jps(查看進程)
2、?zkServer.sh status(查看集群狀態,主從信息)
4.?zookeeper結構和命令
4.1.?zookeeper特性
1、Zookeeper:一個leader,多個follower組成的集群
2、全局數據一致:每個server保存一份相同的數據副本,client無論連接到哪個server,數據都是一致的
3、分布式讀寫,更新請求轉發,由leader實施
4、更新請求順序進行,來自同一個client的更新請求按其發送順序依次執行
5、數據更新原子性,一次數據更新要么成功,要么失敗
6、實時性,在一定時間范圍內,client能讀到最新數據
4.2.?zookeeper數據結構
1、層次化的目錄結構,命名符合常規文件系統規范(見下圖)
2、每個節點在zookeeper中叫做znode,并且其有一個唯一的路徑標識
3、節點Znode可以包含數據和子節點(但是EPHEMERAL類型的節點不能有子節點,下一頁詳細講解)
4、客戶端應用可以在節點上設置監視器(后續詳細講解)
4.3.?數據結構的圖
?4.4.?節點類型
1、Znode有兩種類型:
短暫(ephemeral)(斷開連接自己刪除)
持久(persistent)(斷開連接不刪除)
2、Znode有四種形式的目錄節點(默認是persistent?)
PERSISTENT
PERSISTENT_SEQUENTIAL(持久序列/test0000000019?)
EPHEMERAL
EPHEMERAL_SEQUENTIAL
3、創建znode時設置順序標識,znode名稱后會附加一個值,順序號是一個單調遞增的計數器,由父節點維護
4、在分布式系統中,順序號可以被用于為所有的事件進行全局排序,這樣客戶端可以通過順序號推斷事件的順序
4.5.?zookeeper命令行操作
運行zkCli.sh–server?進入命令行工具
1、使用?ls?命令來查看當前?ZooKeeper?中所包含的內容:
[zk:?202.115.36.251:2181(CONNECTED)?1]?ls?/
2、創建一個新的?znode?,使用?create?/zk?myData?。這個命令創建了一個新的?znode?節點“?zk?”以及與它關聯的字符串:
[zk:?202.115.36.251:2181(CONNECTED)?2]?create?/zk?"myData“
3、我們運行?get?命令來確認?znode?是否包含我們所創建的字符串:
[zk:?202.115.36.251:2181(CONNECTED)?3]?get?/zk
#監聽這個節點的變化,當另外一個客戶端改變/zk時,它會打出下面的
#WATCHER::
#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk
[zk: localhost:2181(CONNECTED) 4] get /zk watch
4、下面我們通過?set?命令來對?zk?所關聯的字符串進行設置:
[zk:?202.115.36.251:2181(CONNECTED)?4]?set?/zk?"zsl“
5、下面我們將剛才創建的?znode?刪除:
[zk:?202.115.36.251:2181(CONNECTED)?5]?delete?/zk
6、刪除節點:rmr
[zk:?202.115.36.251:2181(CONNECTED)?5]?rmr?/zk
4.6.?zookeeper-api應用
4.6.1.?基本使用
?org.apache.zookeeper.Zookeeper是客戶端入口主類,負責建立與server的會話
它提供了表1?所示幾類主要方法:
功能描述
create在本地目錄樹中創建一個節點
delete刪除一個節點
exists測試本地是否存在目標節點
get/set data從目標節點上讀取?/?寫數據
get/set ACL獲取?/?設置目標節點訪問控制列表信息
get children檢索一個子節點上的列表
sync等待要被傳送的數據
表?1?:?ZooKeeper API?描述
4.6.2.?demo增刪改查
public class SimpleDemo {
// 會話超時時間,設置為與系統默認時間一致
private static final int SESSION_TIMEOUT = 30000;
// 創建 ZooKeeper 實例
ZooKeeper zk;
// 創建 Watcher 實例
Watcher wh = new Watcher() {
public void process(org.apache.zookeeper.WatchedEvent event)
{
System.out.println(event.toString());
}
};
// 初始化 ZooKeeper 實例
private void createZKInstance() throws IOException
{
zk = new ZooKeeper("weekend01:2181", SimpleDemo.SESSION_TIMEOUT, this.wh);
}
private void ZKOperations() throws IOException, InterruptedException, KeeperException
{
System.out.println("/n1. 創建 ZooKeeper 節點 (znode : zoo2, 數據: myData2 ,權限: OPEN_ACL_UNSAFE ,節點類型: Persistent");
zk.create("/zoo2", "myData2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("/n2. 查看是否創建成功: ");
System.out.println(new String(zk.getData("/zoo2", false, null)));
System.out.println("/n3. 修改節點數據 ");
zk.setData("/zoo2", "shenlan211314".getBytes(), -1);
System.out.println("/n4. 查看是否修改成功: ");
System.out.println(new String(zk.getData("/zoo2", false, null)));
System.out.println("/n5. 刪除節點 ");
zk.delete("/zoo2", -1);
System.out.println("/n6. 查看節點是否被刪除: ");
System.out.println(" 節點狀態: [" + zk.exists("/zoo2", false) + "]");
}
private void ZKClose() throws InterruptedException
{
zk.close();
}
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
SimpleDemo dm = new SimpleDemo();
dm.createZKInstance();
dm.ZKOperations();
dm.ZKClose();
}
}
Zookeeper的監聽器工作機制
監聽器是一個接口,我們的代碼中可以實現Wather這個接口,實現其中的process方法,方法中即我們自己的業務邏輯
監聽器的注冊是在獲取數據的操作中實現:
getData(path,watch?)監聽的事件是:節點數據變化事件
getChildren(path,watch?)監聽的事件是:節點下的子節點增減變化事件
4.7.?zookeeper應用案例(分布式應用HA||分布式鎖)
4.7.1?實現分布式應用的(主節點HA)及客戶端動態更新主節點狀態
某分布式系統中,主節點可以有多臺,可以動態上下線
任意一臺客戶端都能實時感知到主節點服務器的上下線
A、客戶端實現
public class AppClient {
private String groupNode = "sgroup";
private ZooKeeper zk;
private Stat stat = new Stat();
private volatile List serverList;
/**
?* 連接zookeeper
?*/
public void connectZookeeper() throws Exception {
zk
= new ZooKeeper("localhost:4180,localhost:4181,localhost:4182", 5000, new Watcher() {
public void process(WatchedEvent event) {
// 如果發生了"/sgroup"節點下的子節點變化事件, 更新server列表, 并重新注冊監聽
if (event.getType() == EventType.NodeChildrenChanged
&& ("/" + groupNode).equals(event.getPath())) {
try {
updateServerList();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
updateServerList();
}
/**
?* 更新server列表
?*/
private void updateServerList() throws Exception {
List newServerList = new ArrayList();
// 獲取并監聽groupNode的子節點變化
// watch參數為true, 表示監聽子節點變化事件.
// 每次都需要重新注冊監聽, 因為一次注冊, 只能監聽一次事件, 如果還想繼續保持監聽, 必須重新注冊
List subList = zk.getChildren("/" + groupNode, true);
for (String subNode : subList) {
// 獲取每個子節點下關聯的server地址
byte[] data = zk.getData("/" + groupNode + "/" + subNode, false, stat);
newServerList.add(new String(data, "utf-8"));
}
// 替換server列表
serverList = newServerList;
System.out.println("server list updated: " + serverList);
}
/**
?* client的工作邏輯寫在這個方法中
?* 此處不做任何處理, 只讓client sleep
?*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
AppClient ac = new AppClient();
ac.connectZookeeper();
ac.handle();
}
}
B、服務器端實現
public class AppServer {
private String groupNode = "sgroup";
private String subNode = "sub";
/**
?* 連接zookeeper
?* @param address server的地址
?*/
public void connectZookeeper(String address) throws Exception {
ZooKeeper?zk = new ZooKeeper(
"localhost:4180,localhost:4181,localhost:4182",
5000, new Watcher() {
public void process(WatchedEvent event) {
// 不做處理
}
});
// 在"/sgroup"下創建子節點
// 子節點的類型設置為EPHEMERAL_SEQUENTIAL, 表明這是一個臨時節點, 且在子節點的名稱后面加上一串數字后綴
// 將server的地址數據關聯到新創建的子節點上
String createdPath = zk.create("/" + groupNode + "/" + subNode, address.getBytes("utf-8"),
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("create: " + createdPath);
}
/**
?* server的工作邏輯寫在這個方法中
?* 此處不做任何處理, 只讓server sleep
?*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
// 在參數中指定server的地址
if (args.length == 0) {
System.err.println("The first argument must be server address");
System.exit(1);
}
AppServer as = new AppServer();
as.connectZookeeper(args[0]);
as.handle();
}
}
4.7.2分布式共享鎖的簡單實現
客戶端A
public class DistributedClient {
????// 超時時間
????private static final int SESSION_TIMEOUT = 5000;
????// zookeeper server列表
????private String hosts = "localhost:4180,localhost:4181,localhost:4182";
????private String groupNode = "locks";
????private String subNode = "sub";
????private ZooKeeper zk;
????// 當前client創建的子節點
????private String thisPath;
????// 當前client等待的子節點
????private String waitPath;
????private CountDownLatch latch = new CountDownLatch(1);
????/**
?????* 連接zookeeper
?????*/
????public void connectZookeeper() throws Exception {
????????zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {
????????????public void process(WatchedEvent event) {
????????????????try {
????????????????????// 連接建立時, 打開latch, 喚醒wait在該latch上的線程
????????????????????if (event.getState() == KeeperState.SyncConnected) {
????????????????????????latch.countDown();
????????????????????}
????????????????????// 發生了waitPath的刪除事件
????????????????????if (event.getType() == EventType.NodeDeleted && event.getPath().equals(waitPath)) {
????????????????????????doSomething();
????????????????????}
????????????????} catch (Exception e) {
????????????????????e.printStackTrace();
????????????????}
????????????}
????????});
????????// 等待連接建立
????????latch.await();
????????// 創建子節點
????????thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
????????????????CreateMode.EPHEMERAL_SEQUENTIAL);
????????// wait一小會, 讓結果更清晰一些
????????Thread.sleep(10);
????????// 注意, 沒有必要監聽"/locks"的子節點的變化情況
????????List childrenNodes = zk.getChildren("/" + groupNode, false);
????????// 列表中只有一個子節點, 那肯定就是thisPath, 說明client獲得鎖
????????if (childrenNodes.size() == 1) {
????????????doSomething();
????????} else {
????????????String thisNode = thisPath.substring(("/" + groupNode + "/").length());
????????????// 排序
????????????Collections.sort(childrenNodes);
????????????int index = childrenNodes.indexOf(thisNode);
????????????if (index == -1) {
????????????????// never happened
????????????} else if (index == 0) {
????????????????// inddx == 0, 說明thisNode在列表中最小, 當前client獲得鎖
????????????????doSomething();
????????????} else {
????????????????// 獲得排名比thisPath前1位的節點
????????????????this.waitPath = "/" + groupNode + "/" + childrenNodes.get(index - 1);
????????????????// 在waitPath上注冊監聽器, 當waitPath被刪除時, zookeeper會回調監聽器的process方法
????????????????zk.getData(waitPath, true, new Stat());
????????????}
????????}
????}
????private void doSomething() throws Exception {
????????try {
????????????System.out.println("gain lock: " + thisPath);
????????????Thread.sleep(2000);
????????????// do something
????????} finally {
????????????System.out.println("finished: " + thisPath);
????????????// 將thisPath刪除, 監聽thisPath的client將獲得通知
????????????// 相當于釋放鎖
????????????zk.delete(this.thisPath, -1);
????????}
????}
????public static void main(String[] args) throws Exception {
????????for (int i = 0; i < 10; i++) {
????????????new Thread() {
????????????????public void run() {
????????????????????try {
????????????????????????DistributedClient dl = new DistributedClient();
????????????????????????dl.connectZookeeper();
????????????????????} catch (Exception e) {
????????????????????????e.printStackTrace();
????????????????????}
????????????????}
????????????}.start();
????????}
????????Thread.sleep(Long.MAX_VALUE);
????}
}
?分布式多進程模式實現:
public class DistributedClientMy {
// 超時時間
private static final int SESSION_TIMEOUT = 5000;
// zookeeper server列表
private String hosts = "spark01:2181,spark02:2181,spark03:2181";
private String groupNode = "locks";
private String subNode = "sub";
private boolean haveLock = false;
private ZooKeeper zk;
// 當前client創建的子節點
private volatile String thisPath;
/**
?* 連接zookeeper
?*/
public void connectZookeeper() throws Exception {
zk = new ZooKeeper("spark01:2181", SESSION_TIMEOUT, new Watcher() {
public void process(WatchedEvent event) {
try {
// 子節點發生變化
if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) {
// thisPath是否是列表中的最小節點
List childrenNodes = zk.getChildren("/" + groupNode, true);
String thisNode = thisPath.substring(("/" + groupNode + "/").length());
// 排序
Collections.sort(childrenNodes);
if (childrenNodes.indexOf(thisNode) == 0) {
doSomething();
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 創建子節點
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// wait一小會, 讓結果更清晰一些
Thread.sleep(new Random().nextInt(1000));
// 監聽子節點的變化
List childrenNodes = zk.getChildren("/" + groupNode, true);
// 列表中只有一個子節點, 那肯定就是thisPath, 說明client獲得鎖
if (childrenNodes.size() == 1) {
doSomething();
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
/**
?* 共享資源的訪問邏輯寫在這個方法中
?*/
private void doSomething() throws Exception {
try {
System.out.println("gain lock: " + thisPath);
Thread.sleep(2000);
// do something
} finally {
System.out.println("finished: " + thisPath);
// 將thisPath刪除, 監聽thisPath的client將獲得通知
// 相當于釋放鎖
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws Exception {
DistributedClientMy dl = new DistributedClientMy();
dl.connectZookeeper();
Thread.sleep(Long.MAX_VALUE);
}
}
5.?zookeeper原理
Zookeeper雖然在配置文件中并沒有指定master和slave
但是,zookeeper工作時,是有一個節點為leader,其他則為follower
Leader是通過內部的選舉機制臨時產生的
?5.1.?zookeeper的選舉機制(全新集群paxos)
以一個簡單的例子來說明整個選舉的過程.
假設有五臺服務器組成的zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的.假設這些服務器依序啟動,來看看會發生什么.
1)?服務器1啟動,此時只有它一臺服務器啟動了,它發出去的報沒有任何響應,所以它的選舉狀態一直是LOOKING狀態
2)?服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由于兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由于沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1,2還是繼續保持LOOKING狀態.
3)?服務器3啟動,根據前面的理論分析,服務器3成為服務器1,2,3中的老大,而與上面不同的是,此時有三臺服務器選舉了它,所以它成為了這次選舉的leader.
4)?服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1,2,3,4中最大的,但是由于前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了.
5)?服務器5啟動,同4一樣,當小弟.
5.2.?非全新集群的選舉機制(數據恢復)
那么,初始化的時候,是按照上述的說明進行選舉的,但是當zookeeper運行了一段時間之后,有機器down掉,重新選舉時,選舉過程就相對復雜了。
需要加入數據id、leader id和邏輯時鐘。
數據id:數據新的id就大,數據每次更新都會更新id。
Leader id:就是我們配置的myid中的值,每個機器一個。
邏輯時鐘:這個值從0開始遞增,每次選舉對應一個值,也就是說: ?如果在同一次選舉中,那么這個值應該是一致的?; ?邏輯時鐘值越大,說明這一次選舉leader的進程更新.
選舉的標準就變成:
1、邏輯時鐘小的選舉結果被忽略,重新投票
2、統一邏輯時鐘后,數據id大的勝出
3、數據id相同的情況下,leader id大的勝出
根據這個規則選出leader。