一、簡介
Haproxy一個高性能的負載均衡服務軟體,它可基于四層和七層之間進行排程,而且對各個節點具有健康狀态檢測的功能,當後端伺服器故障時,會自動标記為不可用狀态,當伺服器上線時還會自動将後端主機上線。比起lvs其配置簡單,且引入了frontend,backend,listen等功能,frontend可添加acl規則,可根據HTTP請求頭做規則比對,然後把請求定向到相關的backend。
二、配置相關參數詳解
haproxy主要分為global、defaults、front、backend、listen幾段,配置檔案詳細說明如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<code>#---------------------------------------------------------------------</code>
<code># Global settings #全局配置段</code>
<code>global </code><code>#全局配置段</code>
<code> </code><code># to have these messages end up in /var/log/haproxy.log you will</code>
<code> </code><code># need to:</code>
<code> </code><code>#</code>
<code> </code><code># 1) configure syslog to accept network log events. This is done</code>
<code> </code><code># by adding the '-r' option to the SYSLOGD_OPTIONS in</code>
<code> </code><code># /etc/sysconfig/syslog</code>
<code> </code><code># 2) configure local2 events to go to the /var/log/haproxy.log</code>
<code> </code><code># file. A line like the following can be added to</code>
<code> </code><code># /etc/sysconfig/syslog</code>
<code> </code><code># local2.* /var/log/haproxy.log #如需儲存日志檔案需修改/etc/rsyslog.cfg添加此項至配置問檔案中重新開機rsyslog</code>
<code> </code><code>log 127.0.0.1 local2 </code><code>#日志将通過rsyslog進行記錄</code>
<code> </code><code>chroot </code><code>/var/lib/haproxy</code> <code>#運作的安裝路徑</code>
<code> </code><code>pidfile </code><code>/var/run/haproxy</code><code>.pid </code><code>#運作時的pid程序檔案</code>
<code> </code><code>maxconn 4000 </code><code>#最大連接配接數</code>
<code> </code><code>user haproxy </code><code>#運作以haproxy使用者</code>
<code> </code><code>group haproxy </code><code>#運作以haproxy使用者</code>
<code> </code><code>daemon </code><code>#以守護程序的方式運作haproxy </code>
<code> </code><code># turn on stats unix socket</code>
<code> </code><code>stats socket </code><code>/var/lib/haproxy/stats</code>
<code># common defaults that all the 'listen' and 'backend' sections will</code>
<code># use if not designated in their block</code>
<code>defaults</code>
<code> </code><code>mode http </code><code>#工作模式</code>
<code> </code><code>log global </code><code>#記錄日志級别為全局</code>
<code> </code><code>option httplog </code><code>#詳細的http日志</code>
<code> </code><code>option dontlognull</code>
<code> </code><code>option http-server-close </code>
<code> </code><code>option forwardfor except 127.0.0.0</code><code>/8</code> <code>#傳遞客服端IP </code>
<code> </code><code>option redispatch</code>
<code> </code><code>retries 3 </code><code>#失敗後重試次數 </code>
<code> </code><code>timeout http-request 10s </code><code>#http請求逾時時長 </code>
<code> </code><code>timeout queue 1m</code>
<code> </code><code>timeout connect 10s </code>
<code> </code><code>timeout client 1m</code>
<code> </code><code>timeout server 1m</code>
<code> </code><code>timeout http-keep-alive 10s</code>
<code> </code><code>timeout check 10s </code><code>#心跳資訊檢測逾時時長 </code>
<code> </code><code>maxconn 3000</code>
<code># main frontend which proxys to the backends</code>
<code>listen stats</code>
<code> </code><code>mode http</code>
<code> </code><code>bind *:1080</code>
<code> </code><code>stats </code><code>enable</code>
<code> </code><code>stats hide-version</code>
<code> </code><code>stats uri </code><code>/admin</code>
<code> </code><code>stats realm Haproxy\ Statistics</code>
<code> </code><code>stats auth admin:admin</code>
<code> </code><code>stats admin </code><code>if</code> <code>TRUE</code>
<code>frontend main </code>
<code> </code><code>bind *:80</code>
<code> </code><code>#定義acl規則</code>
<code> </code><code>acl url_static path_beg -i </code><code>/static</code> <code>/images</code> <code>/javascript</code> <code>/stylesheets</code> <code>#請求封包中以此類開頭的都定義為uri_static</code>
<code> </code><code>acl url_static path_end -i .jpg .gif .png .css .js .html .ico </code><code>#不區分大小寫一此類.*結尾的都定義為url_static</code>
<code> </code><code>acl url_dynamic path_end -i .php .jsp .asp </code><code>#不區分大小寫以此類開頭的定義為動态資源組</code>
<code> </code><code>use_backend static </code><code>if</code> <code>url_static </code><code>#調用後端伺服器并檢測規則</code>
<code> </code><code>use_backend bynamic </code><code>if</code> <code>url_dynamic </code><code>#調用後端伺服器并檢查規則</code>
<code> </code><code>default_backend static </code><code>#使用預設規則</code>
<code># static backend for serving up images, stylesheets and such</code>
<code>backend static </code><code>#後端排程</code>
<code> </code><code>balance roundrobin </code><code>#排程算法,除此外還有static-rr,leaseconn,first,source,uri等</code>
<code> </code><code>server static 192.168.10.125:80 inter 1500 rise 2 fall 3 check </code>
<code> </code><code>rspadd X-Via:static </code><code>#啟用響應封包首部标志,以便觀察是靜态伺服器回報的</code>
<code># round robin balancing between the various backends</code>
<code>backend dynamic</code>
<code> </code><code>balance </code><code>source</code>
<code> </code><code>server s2 172.16.10.12:80 check inter 1500 rise 2 fall 3 </code>
<code>#check inter 1500是檢測心跳頻率</code>
<code>#rise2 2次正确認為伺服器可用</code>
<code>#fall3 3次失敗認為伺服器不可用</code>
<code># round robin balancing listen option</code>
<code>listen statistics</code>
<code> </code><code>mode http </code><code>#http 7 層模式</code>
<code> </code><code>bind *:9988 </code><code>#監聽位址</code>
<code> </code><code>stats </code><code>enable</code> <code>#啟用狀态監控</code>
<code> </code><code>stats auth admin:admin </code><code>#驗證的使用者與密碼</code>
<code> </code><code>stats uri </code><code>/admin</code><code>?stats </code><code>#通路路徑</code>
<code> </code><code>stats hide-version </code><code>#隐藏狀态頁面版本号</code>
<code> </code><code>stats admin </code><code>if</code> <code>TRUE </code><code>#如果驗證通過了就允許登入</code>
<code> </code><code>stats refresh 3s </code><code>#每3秒重新整理一次</code>
<code> </code><code>acl allow src 192.168.18.0</code><code>/24</code> <code>#允許的通路的IP位址</code>
<code> </code><code>tcp-request content accept </code><code>if</code> <code>allow </code><code>#允許的位址段就允許通路</code>
<code> </code><code>tcp-request content reject </code><code>#拒絕非法連接配接</code>
三、haproxy+varnish實作動靜分離小案例
實驗架構拓撲圖:
<a href="http://s1.51cto.com/wyfs02/M01/8A/1B/wKioL1gnKs3xbL7fAAEnlR56EfQ239.png" target="_blank"></a>
架構說明:使用者請求到達前端,通過haproxy排程到varnish緩存伺服器上,當緩存伺服器上的資源命中且未過期時直接叫資源響應改使用者,當未命中時通過在兩台varnish伺服器上設定比對規則将其轉發至後端響應的動态和靜态伺服器上,後端動态或靜态伺服器均使用nfs網絡檔案共享使用同一個資源目錄,同時将使用同一台厚分離出來的mysql伺服器作為存儲資料使用,考慮單點故障的瓶頸,mysql伺服器将使用主從兩台實作高可用主負責讀寫均可,但從伺服器為隻讀,同理路由需要也可在前端haproxy代理提供備援。
實驗環境如下:
前端:HAProxy
1、排程伺服器:Varnish1、Varnish2
2、排程算法一緻性URL哈希:URL_Hash_Consistent
3、叢集統計頁:ipaddr/haproxy?admin
緩存伺服器:Varnish
1、VarnishServer01
2、VarnishServer02
3、開啟健康狀态探測,提供高可用
4、負載均衡後端Web伺服器組
5、動靜分離後端伺服器,并動靜都提供負載均衡效果
後端伺服器:
StaticServer01
StaticServer02
DynamicServer01
DynamicServer02
Mysql伺服器:
MysqlServer-master
MysqlServer-slave
haproxy配置如下:
<code># Global settings</code>
<code>global</code>
<code> </code><code># local2.* /var/log/haproxy.log</code>
<code> </code><code>log 127.0.0.1 local2</code>
<code> </code><code>chroot </code><code>/var/lib/haproxy</code>
<code> </code><code>pidfile </code><code>/var/run/haproxy</code><code>.pid</code>
<code> </code><code>maxconn 4000</code>
<code> </code><code>user haproxy</code>
<code> </code><code>group haproxy</code>
<code> </code><code>daemon</code>
<code> </code><code>mode http</code>
<code> </code><code>log global</code>
<code> </code><code>option httplog</code>
<code> </code><code>option http-server-close</code>
<code> </code><code>option forwardfor except 127.0.0.0</code><code>/8</code>
<code> </code><code>retries 3</code>
<code> </code><code>timeout http-request 10s</code>
<code> </code><code>timeout connect 10s</code>
<code> </code><code>timeout check 10s</code>
<code>frontend web *:80</code>
<code>#acl url_static path_beg-i /static /images /javascript /sytlesheets</code>
<code>#acl url_static path_end -i .jpg .gif .png .css .js</code>
<code>#use_backend staticif url_static</code>
<code>use_backendvarnish_srv</code>
<code># vanrnish server balance method</code>
<code>backend varnish_srv </code><code>#定義varnish後端主機組</code>
<code>balance uri </code><code>#一緻性hash </code>
<code>hash</code><code>-</code><code>type</code> <code>consistent </code><code>#一緻性hash url </code>
<code>servervarnish1 10.1.10.6:9988 check </code><code>#varnish伺服器1,并添加健康狀态檢測</code>
<code>servervarnish2 10.1.10.7:9988 check </code><code>#varnish伺服器02,并添加健康狀态檢測</code>
<code>listen stats </code><code>#定義狀态監控管理頁</code>
<code>bind:9002</code>
<code>stats uri </code><code>/alren</code><code>?admin </code><code>#頁面URL</code>
<code>stats hide-version </code><code>#影藏版文本資訊</code>
<code>stats authadmin:alren </code><code>#提供認證頁面</code>
<code>stats admin </code><code>if</code> <code>TRUE </code><code>#認證通過則條狀到相應頁面</code>
varnish配置如下:
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<code># This is an example VCL file for Varnish.</code>
<code>#</code>
<code># It does not do anything by default, delegating control to the</code>
<code># builtin VCL. The builtin VCL is called when there is no explicit</code>
<code># return statement.</code>
<code># See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/</code>
<code># and http://varnish-cache.org/trac/wiki/VCLExamples for more examples.</code>
<code># Marker to tell the VCL compiler that this VCL has been adapted to the</code>
<code># new 4.0 format.</code>
<code>vcl 4.0; </code><code>#版本資訊</code>
<code>import</code> <code>directors; </code><code>#導入子產品</code>
<code>acl purges { </code><code>#定義修剪規則</code>
<code> </code><code>"127.0.0.1"</code><code>; </code>
<code> </code><code>"10.1.10.0"</code><code>;</code>
<code>}</code>
<code>backend web1 {</code>
<code> </code><code>.host = </code><code>"10.1.10.68:80"</code><code>;</code>
<code> </code><code>.port = </code><code>"80"</code><code>;</code>
<code> </code><code>.url =</code><code>"/heath.php"</code><code>;</code>
<code> </code><code>.timeout = 2s;</code>
<code> </code><code>.interval = 1s;</code>
<code> </code><code>.window = 6; </code>
<code> </code><code>.threshold = 3; </code>
<code> </code><code>.expected_response = 200;</code>
<code> </code><code>.initial = 2;</code>
<code>backend web2 {</code>
<code> </code><code>.host = </code><code>"10.1.10.69:80"</code><code>;</code>
<code>backend app1 {</code>
<code> </code><code>.host = </code><code>"10.1.10.70:80"</code><code>;</code>
<code> </code><code>.url =</code><code>"/heath.html"</code><code>;</code>
<code>backend app2 {</code>
<code> </code><code>.host = </code><code>"10.1.10.71:80"</code><code>;</code>
<code>sub vcl_init {</code>
<code> </code><code>new webcluster = directors.round_robin();</code>
<code> </code><code>webcluster.add_backend(web1);</code>
<code> </code><code>webcluster.add_backend(web2);</code>
<code> </code>
<code> </code><code>new appcluster = directors.round_robin();</code>
<code> </code><code>appcluster.add_backend(app1);</code>
<code> </code><code>appcluster.add_backend(app2);</code>
<code>sub vcl_recv { </code>
<code> </code><code>if</code> <code>(req.method == </code><code>"PURGE"</code><code>){</code>
<code> </code><code>if</code><code>(!client.ip ~ purges){</code>
<code> </code><code>return</code><code>(synth(408,</code><code>"you don't have permission purge "</code> <code>+ client.ip));</code>
<code> </code><code>}</code>
<code>return</code> <code>(purge);</code>
<code> </code><code>}</code>
<code> </code><code>if</code> <code>(req.url ~ </code><code>"(?i)\.(php|asp|aspx|jsp)($|\?)"</code><code>) {</code>
<code>set</code> <code>req.backend_hint = appcluster.backend();</code>
<code> </code><code>if</code> <code>(req.method != </code><code>"GET"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"HEAD"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"PUT"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"POST"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"TRACE"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"OPTIONS"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"PATCH"</code> <code>&&</code>
<code> </code><code>req.method != </code><code>"DELETE"</code><code>) {</code>
<code> </code><code>return</code> <code>(pipe);</code>
<code> </code><code>} </code>
<code> </code><code>if</code> <code>(req.method != </code><code>"GET"</code> <code>&& req.method != </code><code>"HEAD"</code><code>) {</code>
<code> </code><code>return</code> <code>(pass);</code>
<code> </code><code>if</code> <code>(req.http.Authorization || req.http.Cookie) {</code>
<code> </code><code>if</code> <code>(req.http.Accept-Encoding) {</code>
<code> </code><code>if</code> <code>(req.url ~ </code><code>"\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$"</code><code>) {</code>
<code> </code><code>unset</code> <code>req.http.Accept-Encoding;</code>
<code> </code><code>} elseif (req.http.Accept-Encoding ~ </code><code>"gzip"</code><code>) {</code>
<code> </code><code>set</code> <code>req.http.Accept-Encoding = </code><code>"gzip"</code><code>;</code>
<code> </code><code>} elseif (req.http.Accept-Encoding ~ </code><code>"deflate"</code><code>) {</code>
<code> </code><code>set</code> <code>req.http.Accept-Encoding = </code><code>"deflate"</code><code>;</code>
<code> </code><code>} </code><code>else</code> <code>{</code>
<code> </code><code>}</code>
<code> </code><code>return</code> <code>(</code><code>hash</code><code>);</code>
<code>sub vcl_pipe {</code>
<code>return</code> <code>(pipe);</code>
<code>sub vcl_miss {</code>
<code>return</code><code>(fetch);</code>
<code>sub vcl_hash {</code>
<code> </code><code>hash_data(req.url);</code>
<code> </code><code>if</code> <code>(req.http.host) {</code>
<code> </code><code>hash_data(req.http.host);</code>
<code> </code><code>} </code><code>else</code> <code>{</code>
<code> </code><code>hash_data(server.ip);</code>
<code> </code><code>if</code> <code>(req.http.Accept-Encoding ~ </code><code>"gzip"</code><code>) {</code>
<code> </code><code>hash_data (</code><code>"gzip"</code><code>);</code>
<code> </code><code>} elseif (req.http.Accept-Encoding ~ </code><code>"deflate"</code><code>) {</code>
<code> </code><code>hash_data (</code><code>"deflate"</code><code>);</code>
<code>sub vcl_backend_response {</code>
<code> </code><code>if</code> <code>(beresp.http.cache-control !~ </code><code>"s-maxage"</code><code>) {</code>
<code> </code><code>if</code> <code>(bereq.url ~ </code><code>"(?i)\.(jpg|jpeg|png|gif|css|js|html|htm)$"</code><code>) {</code>
<code> </code><code>unset</code> <code>beresp.http.Set-Cookie;</code>
<code> </code><code>set</code> <code>beresp.ttl = 3600s;</code>
<code>sub vcl_purge {</code>
<code>return</code><code>(synth(200,</code><code>"Purged"</code><code>));</code>
<code>sub vcl_deliver {</code>
<code> </code><code>if</code> <code>(obj.hits > 0) {</code>
<code> </code><code>set</code> <code>resp.http.X-Cache = </code><code>"HIT via "</code> <code>+ req.http.host;</code>
<code> </code><code>set</code> <code>resp.http.X-Cache-Hits = obj.hits;</code>
<code> </code><code>set</code> <code>resp.http.X-Cache = </code><code>"MISS via "</code> <code>+ req.http.host;</code>
上訴完成後,配置web服務,使用nfs網絡檔案系統并且提供實時資料同步(rsync+inotify),啟動mysql進行授權使用者和建立資料庫,搭建WordPress或其他應用程式,此過程簡單就不啰嗦,此架構存在一定的不足之處,即單點故障會導緻使用者請求失敗。
實作效果如下所示:
<a href="http://s2.51cto.com/wyfs02/M02/8A/20/wKiom1gn1ubDoW7eAABUER_K1es647.png" target="_blank"></a>
将nfs服務端停止後實驗圖:
<a href="http://s5.51cto.com/wyfs02/M01/8A/20/wKiom1gn2LDB6KgvAABwl9T_1WQ558.png" target="_blank"></a>
本文轉自chengong1013 51CTO部落格,原文連結:http://blog.51cto.com/purify/1872279,如需轉載請自行聯系原作者