1. 程式人生 > >HAProxy+Varnish+LNMP實現高可用負載均衡動靜分離集群部署

HAProxy+Varnish+LNMP實現高可用負載均衡動靜分離集群部署

else 應用服務器 bash == 開機啟動 多少 heal 啟用 4.0

轉自http://bbs.hfteams.com/forum.php?mod=viewthread&tid=11&extra=page%3D1

HAProxy+Varnish+LNMP實現高可用負載均衡動靜分離集群部署

HAProxy高可用負載均衡集群部署

基本信息:

系統平臺:VMware WorkStation

系統版本: CentOS Linux release 7.2.1511 (Core)

內核版本: 3.10.0-327.el7.x86_64

集群架構:

前端:HAProxy

    1、虛擬FQDN:www.simpletime.net

    2、VIP:192.168.39.1;DIP:172.16.39.50

    3、調度服務器:Varnish1、Varnish2

    4、調度算法:URL_Hash_Consistent

    5、集群統計頁:172.16.39.50:9091/simpletime?admin 

緩存服務器:Varnish

1、VarnishServer1:172.16.39.14:9527 

2、VarnishServer2:172.16.39.15:9527

3、開啟健康狀態探測,提供高可用

4、負載均衡後端Web服務器組

5、動靜分離後端服務器,並動靜都提供負載均衡效果

後端服務器:

StaticServer1:172.16.39.14:80

StaticServer2:172.16.39.15:80

DynamicServer1:172.16.39.151

DynamicServer2:172.16.39.152

Mysql服務器:

MysqlServer:172.16.39.150

思考:

1、負載均衡動靜分離後,會話如何保持?

2、負載均衡動靜分離後,存儲如何解決?

3、該方案適用於什麽樣的場景?

4、該方案缺陷有哪些?

5、如何改進?

一、部署HAProxy
1、安裝HAProxy
~]# yum install HAProxy
2、配置HAProxy
#---------------------------------------------------------------------

main frontend which proxys to the backends

#---------------------------------------------------------------------

frontend web *:80
#acl url_static path_beg -i /static /images /javascript /stylesheets
#acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
#acl url_dynamic path_begin -i .php .jsp
#default_backend static_srv if url_static
#use_backend dynamic_srv if url_dynamic
use_backend varnish_srv

#---------------------------------------------------------------------

round robin balancing between the various backends

#---------------------------------------------------------------------
backend varnish_srv
balance uri #使用基於URL的一致性哈希調度算法
hash-type consistent
server varnish1 172.16.39.14:9527 check
server varnish2 172.16.39.15:9527 check

listen stats #開啟HAProxy圖形化Web管理功能
bind :9091
stats enable
stats uri /simpletime?admin
stats hide-version
stats auth admin:abc.123
stats admin if TRUE
3、啟動服務
~]# systemctl start haproxy
~]# systemctl status haproxy #查看狀態
~]# ss -tnlp #查看80和9091端口是否啟用
~]# systemctl enable haproxy #設置開機啟動
復制代碼

二、部署Varnish,兩臺配置一致(172.16.39.14|15)

~]# yum install varnish -y
~]# vim /etc/varnish/varnish.params
VARNISH_LISTEN_PORT=9527 #更改默認端口
~]# systemctl start varnish
~]# systemctl enable varnish
~]# vim /etc/varnish/default.vcl
vcl 4.0;
##############啟用負載均衡模塊###############
import directors;
################定義Purge-ACL控制#######################
acl purgers {
"127.0.0.1";
"172.16.39.0"/16;
}

Default backend definition. Set this to point to your content server.

##############配置健康狀態探測##############
probe HE { #靜態檢測
.url = "/health.html"; #指定檢測URL
.timeout = 2s; #探測超時時長
.window = 5; #探測次數
.threshold = 2; #探測次數成功多少次才算健康
.initial = 2; #Varnish啟動探測後端主機2次健康後加入主機
.interval = 2s; #探測間隔時長
.expected_response = 200; #期望狀態響應碼
}
probe HC { #動態監測
.url = "/health.php";
.timeout = 2s;
.window = 5;
.threshold = 2;
.initial = 2;
.interval = 2s;
.expected_response = 200;
}
#############添加後端主機################
backend web1 {
.host = "172.16.39.151:80";
.port = "80";
.probe = HC;
}

backend web2 {
.host = "172.16.39.152:80";
.port = "80";
.probe = HC;
}

backend app1 {
.host = "172.16.39.14:80";
.port = "80";
.probe = HE;
}

backend app2 {
.host = "172.16.39.15:80";
.port = "80";
.probe = HE;
}

#############定義負載均衡及算法###############
sub vcl_init {
new webcluster = directors.round_robin();
webcluster.add_backend(web1);
webcluster.add_backend(web2);

new appcluster = directors.round_robin();
appcluster.add_backend(app1);
appcluster.add_backend(app2);

}
################定義vcl_recv函數段######################
sub vcl_recv {
#####ACL未授權,不允許PURGE,並返回405#####
if (req.method == "PURGE") {
if(!client.ip ~ purgers){
return(synth(405,"Purging not allowed for" + client.ip));
}
return (purge);
}
#####添加首部信息,使後端服務記錄訪問者的真實IP

if (req.restarts == 0) {

set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;

} else {

set req.http.X-Forwarded-For = client.ip;

}

set req.backend_hint = webcluster.backend();

set req.backend_hint = appcluster.backend();

#註:因為Varnish不是一級代理,配置forward只能取到上級代理IP,而上級代理IP,本身就包含在HAProxy發送過來的Forward裏,所以沒必要配置,而後端服務器只要日誌格式有啟用記錄Forward信息,並且上級代理沒有做限制,那麽,就能獲取到客戶端真實IP;
#####動靜分離#####
if (req.url ~ "(?i).(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
set req.backend_hint = appcluster.backend();
}
#####不正常的請求不緩存#####
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PATCH" &&
req.method != "DELETE") {
return (pipe);
}
#####如果請求不是GET或者HEAD,不緩存#####
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
#####如果請求包含Authorization授權或Cookie認證,不緩存#####
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
#####啟用壓縮,但排除一些流文件壓縮#####
if (req.http.Accept-Encoding) {
if (req.url ~ ".(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)[ DISCUZ_CODE_1 ]quot;) {
unset req.http.Accept-Encoding;
} elseif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elseif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
return (hash);
}
####################定義vcl_pipe函數段#################
sub vcl_pipe {
return (pipe);
}
sub vcl_miss {
return (fetch);
}
####################定義vcl_hash函數段#################
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
if (req.http.Accept-Encoding ~ "gzip") {
hash_data ("gzip");
} elseif (req.http.Accept-Encoding ~ "deflate") {
hash_data ("deflate");
}
}
##############設置資源緩存時長#################
sub vcl_backend_response {
if (beresp.http.cache-control !~ "s-maxage") {
if (bereq.url ~ "(?i).(jpg|jpeg|png|gif|css|js|html|htm)[ DISCUZ_CODE_1 ]quot;) {
unset beresp.http.Set-Cookie;
set beresp.ttl = 3600s;
}
}
}
################啟用Purge#####################
sub vcl_purge {
return(synth(200,"Purged"));
}
###############記錄緩存命中狀態##############
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from " + req.http.host;
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS from " + req.http.host;
}
unset resp.http.X-Powered-By;
unset resp.http.Server;
unset resp.http.Via;
unset resp.http.X-Varnish;
unset resp.http.Age;
}
2、加載配置,因為還沒有配置後端應用服務器,可以看到後端主機健康檢測全部處於Sick狀態
~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> vcl.load conf1 default.vcl
200
VCL compiled.
varnish> vcl.use conf1
200
VCL ‘conf1‘ now active
varnish> backend.list
200
Backend name Refs Admin Probe
web1(172.16.39.151,,80) 15 probe Sick 0/5
web2(172.16.39.152,,80) 15 probe Sick 0/5
app1(172.16.39.14,,80) 15 probe Sick 0/5
app2(172.16.39.15,,80) 15 probe Sick 0/5
復制代碼
三、部署Mysql(172.16.39.150)

~]# yum install mariadb.server
~]# rpm -qe mariadb-server
mariadb-server-5.5.44-2.el7.centos.x86_64
~]# vim /etc/my.cnf #數據庫基本優化
[mysqld]
innodb_file_per_table = ON
skip_name_resolve = ON
~]# mysql #創建wordpress數據庫並授權該數據庫用戶

create database wwwdb;
grant all on wwwdb.* to www@‘172.16.39.%‘ identified by "abc.123";
exit
復制代碼
四、部署NFS文件系統

1、後端所有主機安裝服務
~]# yum install nfs-utils
2、動態資源主機172.16.39.152設為動態web數據共享服務器
DynamicServer2 ~]# vim /etc/exports
/data/web/ 172.16.39.151/16(rw,sync) #rw=可讀寫,sync=內存及硬盤同步寫入數據
3、靜態主機172.16.39.15設為靜態web數據共享服務器
StaticServer2 ~]# vim /etc/exports
/data/web/ 172.16.39.14/16(rw,sync) #rw=可讀寫,sync=內存及硬盤同步寫入數據
~]# systemctl start nfs-server #啟動服務
DynamicServer2 ~]# exportfs -avr #重載配置
exporting 172.16.39.151/16:/data/web
StaticServer2 ~]# exportfs -avr #重載配置
exporting 172.16.39.14/16:/data/web
4、兩臺服務端設為開機啟動
~]# systemctl enable nfs-server
5、客戶端同步,動態主機掛載動態服務器共享,靜態主機掛載靜態服務器共享
~]# showmount -e 172.16.39.152
Export list for 172.16.39.152:
/data/web 172.16.39.151/16
~]# mount -t nfs 172.16.39.15:/data/web /data/web
復制代碼
五、部署後端主機(註意:已經部署了NFS文件系統)

1、安裝及配置(DynamicServer2:172.16.39.152)
~]# yum install nginx php-fpm php-mysql -y
~]# mkdir /data/web/www -pv
~]# vim /etc/nginx/conf.d/www.simple.com.conf
server {
listen 80;
root /data/web/www;
server_name www.simple.com;
index index.html index.htm index.php;
location ~ [^/].php(/|$) {
try_files $uri = 404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
#access_log_bypass_if ($uri = ‘/health.php‘);
}
}

備註:access_log_bypass_if 需添加日誌過濾模塊,本文主要實現過濾健康狀態檢測信息;

~]# systemctl start nginx php-fpm
2、部署wordpress應用
~]# unzip wordpress-4.3.1-zh_CN.zip
~]# mv wordpress/* /data/web/www/
www]# cp wp-config{-sample,}.php
www]# vim wp-config.php
define(‘DB_NAME‘, ‘wwwdb‘);
define(‘DB_USER‘, ‘www‘);
define(‘DB_PASSWORD‘, ‘abc.123‘);
define(‘DB_HOST‘, ‘172.16.39.150‘);
3、設置facl權限
~]# id apache
~]# setfacl -m u:apache:rwx /data/web/www
4、拷貝web數據至StaticServer2,另兩臺後端主機掛載的是兩臺NFS服務端的數據文件,web數據數完成
~]# tar -jcvf web.tar.gz /data/web/www
~]# scp web.tar.gz 172.16.39.15:
~]# setfacl -m u:apache:rwx /data/web/www
StaticServer2 ~]# tar -xf web.tar.gz -C /data/web
5、創建動靜資源主機組Varnish健康狀態探測頁面
DynamicServer2~]# echo "<h1>DynamicServer is Health.</h1> > /data/web/www/health.php
StaticServer2~]# echo "<h1>StaticServer is Health.</h1>" > /data/web/www/health.html
6、在Varnish主機上查看健康狀態(172.16.39.14|15,也就是StaticServer主機)
StaticServer2~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> backend.list #後端Web主機正常
200
Backend name Refs Admin Probe
web1(172.16.39.151,,80) 15 probe Healthy 5/5
web2(172.16.39.152,,80) 15 probe Healthy 5/5
app1(172.16.39.14,,80) 15 probe Healthy 5/5
app2(172.16.39.15,,80) 15 probe Healthy 5/5
復制代碼
7、web訪問172.16.39.50完成wordpress配置

8、創建圖文測試,發現動靜分離下,圖片上傳後無法顯示

9、分析原因,動靜分離後,用戶可以上傳圖片至動態資源主機上,但靜態資源卻沒有該圖片,初步解決方案:使用文件同步軟件,實時同步圖片等流格式資源。

六、部署Rsync+inodify,實現靜態資源主機同步上傳資源圖片目錄,解決圖片不能顯示問題
1、將StaticServer2配置為備份端,同步動態資源資源DynamicServer2下的web圖片上傳目錄文件
~]# rpm -qa rsync
~]# vim /etc/rsyncd.conf #尾行追加
uid = root #指定執行備份的用戶及組權限為root用戶
gid = root
user chroot = no #關閉chroot
max connections = 200 #最大連接數
timeout = 600
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsyncd.lock
log file = /var/log/rsyncd.log
[web]
path=/data/web/www/wp-content/uploads
ignore errors
read only = no
list = no
hosts allow = 172.16.39.0/255.255.0.0 #只允許39網段進行同步
auth users = rsync #認證的用戶名
secrets file = /etc/rsyncd.password #指定密碼文件
2、創建rsync同步用戶密碼文件
~]# echo “rsync:abc.123” > /etc/rsyncd.password
~]# chmod 600 /etc/rsyncd.password
3、啟動服務
~]# systemctl start rsyncd
~]# ss -tnlp |grep rsync #查看是否開啟873端口
~]# systemctl enable rsyncd #設置rsync開機啟動
4、部署Rsync同步端DynamicServer2,實時同步本機web圖片數據至StaticServer2,並保持以DynamicServer2為主兩臺服務器web圖片資源一致性。
~]# yum install rsync inodify-tools inodify-tools-devel
如果yum源沒有找到inodify,就直接下載源碼包進行編譯
~]# tar -xf inodify-tools-3.13.tar.gz
~]# ./configure --prefix=/usr/local/inotify
~]# ]# make -j 2 && make install
~]# ln -sv /usr/local/inotify/bin/inotifywait /usr/bin/
~]# echo “abc.123” > /etc/rsyncd.password
~]# chmod 600 /etc/rsyncd.password
#創建實時同步腳本
~]# vim /etc/rsync.sh
#!/bin/bash
host=172.16.39.15 #數據備份主機
src=/data/web/www/wp-content/uploads/ #想要備份的目錄
des=web
user=rsync
/usr/bin/rsync -vzrtopg --delete --progress --password-file=/etc/rsyncd.password $src $user@$host::$des
/usr/bin/inotifywait -mrq --timefmt ‘%d/%m/%y %H:%M‘ --format ‘%T %w%f%e‘ -e modify,delete,create,attrib $src \
| while read files
do
/usr/bin/rsync -vzrtopg --delete --progress --password-file=/etc/rsyncd.password $src $user@$host::$des
echo "${files} was rsynced" >>/tmp/rsync.log 2>&1
done
5、添加開機自動運行腳本
~]# chmod 764 /etc/rsync.sh
~]# echo "/usr/bin/bash /etc/rsync.sh &" >> /etc/rc.local #開機後臺自動運行該腳本
~]# chmod 750 /etc/rc.d/rc.local #Centos7,rc.local沒有執行權限,要給予權限才能執行
~]# sh /etc/rsync.sh & #執行同步腳本
復制代碼

七、測試
1、圖文博客發布

2、PURGE清空Varnish緩存測試
~]# curl -X PURGE www.simpletime.net/
<!DOCTYPE html>
<html>
<head>
<title>200 Purged</title>
</head>
<body>
<h1>Error 200 Purged</h1>
<p>Purged</p>
<h3>Guru Meditation:</h3>
<p>XID: 100925448</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
3、壓力測試 #存在緩存服務器,單機測試只做參考
~]# ab -c 20000 -n 20000 -r www.simpletime.net/
Requests per second: 670.86 [#/sec] (mean)
Time per request: 29812.421 [ms] (mean)
Time per request: 1.491 [ms] (mean, across all concurrent requests)
~]# ab -c 20000 -n 20000 -r www.simpletime.net/
Requests per second: 521.78 [#/sec] (mean)
Time per request: 38330.212 [ms] (mean)
Time per request: 1.917 [ms] (mean, across all concurrent requests)
~]# ab -c 20000 -n 20000 -r www.simpletime.net/
Requests per second: 521.78 [#/sec] (mean)
Time per request: 38330.212 [ms] (mean)
Time per request: 1.917 [ms] (mean, across all concurrent requests)
4、HAProxy調度器掛了,整個架構崩潰...
5、Varnish掛一臺,沒事,有冗余,高可用實現,另一臺Varnish壓力增大,盡快修復故障機器即可。
6、後端主機,理論上動靜態群集組能冗余各組一臺服務器故障,可因為NFS文件系統懶人配置,NFS客戶端掛了沒事,NFS服務端掛了,GameOver...
7、文件服務器NFS服務端掛了,(NFS服務器沒有做冗余)GameOver...
8、Rsync+Inodify文件實時同步系統掛了,服務器還能訪問,只是圖片等流格式文件不顯示了...
9、MysqlServer掛了,無冗余,(GameOver)...
10、後端應用服務器Nginx日誌示例:
~]# tail /var/log/nginx/access.log
172.16.39.15(VarnishIP) - - [26/Aug/2016:17:09:56 +0800] "POST /wp-admin/admin-ajax.php HTTP/1.1" 200 32
"http://172.16.39.50/wp-admin/post-new.php" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36" "172.16.39.5(客戶端IP), 172.16.39.50(HAProxyIP)"
復制代碼

八、總結
1、架構優點:
(1)HAProxy為Varnish緩存服務器提供了高可用的負載均衡,其使用的算法,也為Varnish提升了緩存命中率;
(2)HAProxy提供Web圖形化管理界面,節約了學習成本;
(3)Varnish的高效緩存機制極大的提升了Web應用的性能,降低了Web服務器的壓力;
(4)Varnish給後端Web服務器提供了高可用負載均衡,並使用了動態分離技術,保障了後端主機的冗余,顯著提升了其性能;
(5)NFS+Rsync+inodify基本解決了該架構存儲的問題,該架構文件存儲因服務器有限,並沒有設計好,儲存服務器因有單獨的網絡存儲服務器來提供;
2、冗余不足:
(1)Nginx負載均衡調度器,沒有冗余能力,易出現單點故障。
解決方案:增加服務器,使用Keepalive做高可用。
(2)MysqlServer,沒有冗余能力。
解決方案:增加服務器,使用Keepalive做高可用。
(3)NFS文件服務,沒有冗余能力。
解決方案:可以增加服務器,使用rsync+inodify實現數據實時同步,再用keepalive做高可用。
3、性能瓶頸:
(1)NFS文件系統,因網絡IO能力與磁盤本身性能,多臺主機同時掛載執行讀取讀寫操作,勢必帶來性能下降,根據木桶原理,此短板已成為該群集方案性能瓶頸。
(2)在大量的讀寫訪問下,數據庫的壓力會非常大,從而影響性能。
解決方案:a、主從復制,主要提供高可用冗余能力,對性能有所提升,對數據壓力不算太大的企業可選方案;
b、高可用負載均衡,擴展性強,同時提供高可用及負載均衡,性能有極大提升;
4、會話保持
(1)會話保持機制因添加memcache服務器,使用其sesion功能;

友情鏈接:

    Varnish4.0官方幫助指南

    http://www.varnish-cache.org/docs/4.0/users-guide/

以上均為個人觀點,本架構還有許多不足之處,僅作學習交流之用;

HAProxy+Varnish+LNMP實現高可用負載均衡動靜分離集群部署