HAProxy
HAProxy介紹
HAProxy: 是法國人Willy Tarreau開發的一個開源軟件,是一款應對客戶端10000以上的同時連接的高性能的TCP和 HTTP負載均衡器。其功能是用來提供基於cookie的持久性, 基於內容的交換,過載保護的高級流量管制,自動故障切換 ,以正則表達式為基礎的標題控制運行時間,基於Web的報表,高級日志記錄以幫助排除故障的應用或網絡及其他功能。
相關概念
代理的作用
- 正向代理,反向代理
- 代理服務器,可以提供緩存功能加速客戶端訪問,同時可以對緩存數據進行有效性檢查
- 內容路由:根據流量以及內容類型將請求轉發至特定的服務器
- 轉碼器:支持壓縮功能,將數據以壓縮形式發送給客戶端
緩存的作用
- 減少訪冗余內容傳輸
- 節省帶寬,緩解網絡瓶頸
- 降低了對原始服務器的請求壓力
- 降低了傳輸延遲
負載均衡集群:
四層:
lvs, nginx(stream),haproxy(mode tcp)
七層:
http: nginx(http, ngx_http_upstream_module), haproxy(mode http), httpd, ats, perlbal, pound...
HAProxy功能
HAProxy是TCP / HTTP反向代理服務器,尤其適合於高可用性環境
可以針對HTTP請求添加cookie,進行路由后端服務器
可平衡負載至后端服務器,並支持持久連接
支持基於cookie進行調度
支持所有主服務器故障切換至備用服務器
支持專用端口實現監控服務
支持不影響現有連接情況下停止接受新連接請求
可以在雙向添加,修改或刪除HTTP報文首部
支持基於pattern實現連接請求的訪問控制
通過特定的URI為授權用戶提供詳細的狀態信息
版本:1.4 1.5 1.6 1.7 1.8
支持http反向代理
支持動態程序的反向代理
支持基於數據庫的反向代理
HAproxy組成
包名:haproxy
程序環境
主程序:/usr/sbin/haproxy
配置文件:/etc/haproxy/haproxy.cfg
Unit file:/usr/lib/systemd/system/haproxy.service
配置文件
haproxy.cfg主要有兩部分組成:global,和proxies配置段
global:全局配置段
進程及安全配置相關的參數
性能調整相關參數
Debug參數
proxies:代理配置段
defaults:為frontend, backend, listen提供默認配置
fronted:前端,相當於nginx, server {}
backend:后端,相當於nginx, upstream {}
listen:同時擁有前端和后端,適用於一對一環境
簡單前端調度實現
利用四台虛擬機實現簡單的前端輪詢調度。
一台客戶端,一台haproxy調度器,兩台RS
- 首先在后端部署兩台http服務
- 編輯haproxy配置文件/etc/haproxy/haproxy.cfg
默認設置不做修改
[root@CentOS6 ~]# vim /etc/haproxy/haproxy.cfg
frontend main *:80 #設置監聽ip:端口
default_backend websrvs #調用后端RS組名
backend websrvs
balance roundrobin #輪詢算法
server web1 192.168.45.11:80 check
server web2 192.168.45.12:80 check
配置
global配置參數:
global # 全局參數的設置
log 127.0.0.1 local2
# log語法:log <address_1>[max_level_1]
# 全局的日志配置,使用log關鍵字,指定使用127.0.0.1上的syslog服務中的local0日志設備,記錄日志等級為info的日志
chroot /var/lib/haproxy #改變當前工作目錄
pidfile /var/run/haproxy.pid #當前進程id文件
maxconn 4000 #最大連接數
user haproxy #所屬用戶
group haproxy #所屬組
daemon #以守護進程方式運行haproxy
stats socket /var/lib/haproxy/stats #基於本地的文件傳輸
實現日志記錄:
haproxy配置文件中默認定義了log 127.0.0.1 local2 說明日志將被記錄在本機的local2設施中。
編輯rsyslog配置文件:
[root@CentOS6 ~]#vim /etc/rsyslog.conf
# Provides UDP syslog reception
$ModLoad imudp #取消注釋
$UDPServerRun 514 #取消注釋
local2.* /var/log/haproxy.log
#指定設備local2日志存放位置
haproxy的日志信息可以設置存放在專門的日志服務器中
proxies配置參數:
代理配置段:
-
defaults <name>
-
frontend <name>
-
backend <name>
-
listen <name>
Frontend段:指定接收客戶端連接偵聽套接字設置
Backend段:指定將連接請求轉發至后端服務器的相關設置
Listen段:指定完整的前后端設置,只對 TCP 有效
proxy 名稱:使用字母 數字 - _ . : 並區分字符大小寫
mode http
#默認的模式mode { tcp|http|health },tcp是4層,http是7層,health只會返回OK
log global
#應用全局的日志配置
option httplog
# 啟用日志記錄HTTP請求,默認haproxy日志記錄是不記錄HTTP請求日志
option dontlognull
# 啟用該項,日志中將不會記錄空連接。所謂空連接就是在上游的負載均衡器或者監控系統為了探測該服務是否存活可用時,需要定期的連接或者獲取某一固定的組件或頁面,或者探測掃描端口是否在監聽或開放等動作被稱為空連接;官方文檔中標注,如果該服務上游沒有其他的負載均衡器的話,建議不要使用該參數,因為互聯網上的惡意掃描或其他動作就不會被記錄下來
option http-server-close
#每次請求完畢后主動關閉http通道
option forwardfor except 127.0.0.0/8
#如果服務器上的應用程序想記錄發起請求的客戶端的IP地址,需要在HAProxy上配置此選項, 這樣 HAProxy會把客戶端的IP信息發送給服務器,在HTTP請求中添加"X-Forwarded-For"字段。啟用X-Forwarded-For,在requests頭部插入客戶端IP發送給后端的server,使后端server獲取到客戶端的真實IP。
option redispatch
#當使用了cookie時,haproxy將會將其請求的后端服務器的serverID插入到cookie中,以保證會話的SESSION持久性;而此時,如果后端的服務器宕掉了, 但是客戶端的cookie是不會刷新的,如果設置此參數,將會將客戶的請求強制定向到另外一個后端server上,以保證服務的正常。
retries 3
# 定義連接后端服務器的失敗重連次數,連接失敗次數超過此值后將會將對應后端服務器標記為不可用
timeout http-request 10s #http請求超時時間
timeout queue 1m #一個請求在隊列里的超時時間
timeout connect 10s #連接超時
timeout client 1m #客戶端超時
timeout server 1m #服務器端超時
timeout http-keep-alive 10s #設置http-keep-alive的超時時間
timeout check 10s #檢測超時
maxconn 3000 #每個進程可用的最大連接數
frontend main *:80 #監聽地址為80
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js
use_backend static if url_static
default_backend my_webserver
#定義一個名為my_app前端部分。此處將對應的請求轉發給后端
backend static
#使用了靜態動態分離(如果url_path匹配 .jpg .gif .png .css .js靜態文件則訪問此后端)
balance roundrobin
#負載均衡算法(#banlance roundrobin 輪詢,balance source 保存session值,支持static-rr,leastconn,first,uri等參數)
server static 127.0.0.1:80 check
#靜態文件部署在本機(也可以部署在其他機器或者squid緩存服務器)
backend my_webserver
#定義一個名為my_webserver后端部分。PS:此處my_webserver只是一個自定義名字而已,但是需要與frontend里面配置項default_backend 值相一致
balance roundrobin #負載均衡算法
server web01 172.31.2.33:80 check inter 2000 fall 3 weight 30 #定義的多個后端
server web02 172.31.2.34:80 check inter 2000 fall 3 weight 30 #定義的多個后端
server web03 172.31.2.35:80 check inter 2000 fall 3 weight 30 #定義的多個后端
Balance配置
balance:后端服務器組內的服務器調度算法
balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post]
haproxy中調度算法同樣分為動態調度算法和靜態調度算法,與nginx調度算法中區分動靜態調度算法的概念不同,nginx用能不能根據后端服務器的負載狀況進行調度來區分動靜態調度算法的差別,而haproxy中則根據該算法支不支持運行時即時生效來區分動靜態算法。
調度算法:
roundrobin:基於權重輪詢,動態算法,支持權重的運行時調整,支持慢啟動;每個后端backend中最多支持4095個
server server options: weight #
static-rr:基於權重輪詢,靜態算法,不支持權重的運行時調整及慢啟動;后端主機數量無上限
leastconn:加權最少連接,動態算法,最少連接的后端服務器優先分配接收新連接,相同連接時輪詢,適用於長連接場景,例如 MySQL、LDAP等,不適合http
first:根據服務器在列表中的位置,自上而下進行調度;前面服務器的連接數達到上限,新請求才會分配給下一台服務
source:源地址hash,新連接先按權重分配,后續連接按source分配請求
動靜態取決於hash type
hash-type:哈希算法
hash-type <method> <function> <modifier>
method:
map-based:除權取余法,哈希數據結構是靜態數組(不支持權重動態調整)
consistent:一致性哈希,哈希數據結構是一棵樹 (支持權重動態調整)
<function> : 哈希函數
sdbm djb2 wt6
uri:
對URI的左半部分或整個uri做hash計算,並除以服務器總權重取模,以后派發至某挑出的服務器,適用於后端緩存服務器
動靜態取決於hash type
hash-type
map-based
consistent
<scheme>://<user>:<password>@<host>:<port>/<path> ;<params>?<query>#<frag>
左半部分:/<path>;<params>
整個uri:/<path>;<params>?<query>#<frag>
url_param:
對用戶請求的uri聽<params>部分中的參數的值作hash計算, 並由服務器總權重相除以后派發至某挑出的服務器;通常用於追蹤用戶,以確保來自同一個用戶的請求始終發往同一個Backend Server
動靜態取決於hash type
hash-type
map-based
consistent
hdr(<name>):根據請求報文中指定的header(如use_agent,referer,hostname)將該hesder做hash計算進行調度
動靜態取決於hash type
hash-type
map-based
consistent
hdr(Cookie)
rdp-cookie 遠程桌面相關
rdp-cookie(<name>)
default_backend <backend>
無use_backend 匹配時,使用默認的backend,用於 frontend中
server
server <name> <address>[:[port]] [param*]
定義后端主機的各服務器及其選項 server <name> <address>[:port] [settings ...] default-server [settings ...]
<name>:服務器在haproxy上的內部名稱;出現在日志及警告信息
<address>:服務器地址,支持使用主機名
[:[port]]:端口映射;省略時,表示同bind中綁定的端口
[param*]:參數
check:對當前server做健康狀態檢測,只用於四層檢測
注意:httpchk,“smtpchk”, “mysql-check”, “pgsql-check” and “sslhello-chk” 用於定義應用層檢測方法
addr :檢測時使用的IP地址
port :針對此端口進行檢測
inter <delay>:連續兩次檢測之間的時間間隔,默認為2000ms
rise <count>:連續多少次檢測結果為“成功”才標記服務器為可用 ;默認為2
fall <count>:連續多少次檢測結果為“失敗”才標記服務器為不可 用;默認為3
cookie <value>:為當前server指定cookie值,實現基於cookie的會話黏性
disabled:標記為不可用
redir <prefix>:將發往此server的所有GET和HEAD類的請求重定向至指 定的URL
weight <weight>:權重,默認為1
maxconn <maxconn>:當前server的最大並發連接數
backlog <backlog>:當server的連接數達到上限后的后援隊列長度
backup:設定當前server為備用服務器
default-server [param*] 為backend中的各server設定默認選項
bind配置
bind:指定一個或多個前端偵聽地址和端口
只用於frountend配置段和listen配置段
bind [<address>]:<port_range> [, ...] [param*]
示例:
listen http_proxy
bind :80,:443
bind 10.0.0.1:10080,10.0.0.1:10443
bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy
maxconn
maxconn <conns>:為指定的frontend定義其最大並發連接數;默認為2000
mode { tcp|http|health }
定義haproxy的工作模式
tcp:基於layer4實現代理;可代理mysql, pgsql, ssh, ssl等協議,https時使用此模式,默認模式
http:僅當代理協議為http時使用,centos實際默認模式
health:工作為健康狀態檢查的響應模式,當連接請求到達時回應“OK”后即斷開連接,較少使用
基於cookie的會話綁定
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
<name>:cookie名稱,用於實現持久連接
rewrite:重寫
insert:插入
prefix:前綴
配置示例:
backend websrvs
balance roundrobin
cookie WEBSRV insert nocache indirect
server web1 192.168.45.11:80 check cookie srv1
server web2 192.168.45.12:80 check cookie srv2
#每個server有自己的唯一的cookie標識
#在backend中為用戶請求調度完成后操縱其cookie
統計接口啟用相關的參數
stats enable
啟用統計頁;基於默認的參數啟用stats page
- stats uri : /haproxy?stats uri默認值
- stats realm : HAProxy\ Statistics
- stats auth : no authentication
stats uri <prefix> 自定義stats page uri
stats auth <user>:<passwd> 認證時的賬號和密碼,可使用多次
stats realm <realm> 認證時的realm
stats hide-version 隱藏版本
stats refresh <delay> 設定自動刷新時間間隔
stats admin { if | unless } <cond> 啟用stats page中的管理功能
配置示例:
listen stats
bind :9099
stats enable
stats realm HAPorxy\ Stats\ Page
stats auth 用戶名:密碼
stats admin if TRUE
#在frountend中單獨定義一個stats服務,監聽9099端口
#如果認證成功就開啟管理功能
forwardfor配置
option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
在由haproxy發往后端主機的請求報文中添加“X-ForwardedFor”首部,其值為前端客戶端的地址;用於向后端主發送真實的客戶端IP
[ except <network> ]:請求報請來自此處指定的網絡時不予添加此首部,如haproxy自身所在網絡
[ header <name> ]:使用自定義的首部名稱,而非“XForwarded-For”
[ if-none ] 如果沒有首部才添加首部,如果有使用默認值
為指定的MIME類型啟用壓縮傳輸功能
compression algo <algorithm> ...:啟用http協議的壓縮機制,指明壓縮算法gzip, deflate
compression type <mime type> ...:指明壓縮的MIMI類型
錯誤頁配置
errorfile <code> <file> 自定義錯誤頁
<code>:HTTP status code.
支持200, 400, 403, 408, 500, 502, 503, 504.
<file>:錯誤頁文件路徑
示例:
使用haproxy主機本地的文件進行響應
errorfile 400 /etc/haproxy/errorfiles/400badreq.http
errorfile 408 /dev/null # workaround Chrome preconnect bug
errorfile 403 /etc/haproxy/errorfiles/403forbid.http
errorfile 503 /etc/haproxy/errorfiles/503sorry.http
使用url進行響應,響應狀態碼為302,不適用於GET以外的其他請求方法:
errorloc <code> <url> 相當於errorloc302 <code> <url>,利用302重定向至指URL
errorloc 503 http://www.a.com/error_pages/503.html
修改報文首部
reqadd <string> [{if | unless} <cond>]
在請求報文尾部添加指定首部
rspadd <string> [{if | unless} <cond>]
在響應報文尾部添加指定首部
示例:
rspadd X-Via:\ HAPorxy #字符串中的空格要轉義
reqdel <search> [{if | unless} <cond>]
reqidel <search> [{if | unless} <cond>] (ignore case) 不分大小寫
從請求報文中刪除匹配正則表達式的首部
rspdel <search> [{if | unless} <cond>]
rspidel <search> [{if | unless} <cond>] (ignore case) 不分大小寫從響應報文中刪除匹配正則表達式的首部 示例: rspidel Server.*
連接超時
timeout client <timeout> 客戶端最長空閑連接超時時長 默認單位是毫秒
timeout server <timeout> 后端服務器最長空閑連接超時時長
timeout http-keep-alive <timeout> 持久連接的持久時長
timeout http-request <timeout> 一次完整的HTTP請求的最大等待時長
timeout connect <timeout> 成功連接后端服務器的最大等待時長
timeout client-fin <timeout> 客戶端半連接的空閑時長
timeout server-fin <timeout> 后端服務器半連接的空閑時長
ACL
acl:haproxy的ACL用於實現基於請求報文的首部、響應報文的內容或其他的環境狀態信息來做出轉發決策,這大大增加了其配置彈性。其配置法則一般分為兩部,首先定義ACL,既定義一個測試條件,而后在條件得到滿足時執行某特定動作,如阻止訪問或者轉發至某特定的后端,
定義ACL的語法格式如下:
acl <aclname> <criterion> [flags] [operator] [<value>] ...
<aclname>:ACL名稱,可使用字母 數字 : . - _ 區分字符大小寫
<criterion>: 指明檢查條件
各種條件 :
dst 目標IP
dst_port 目標PORT
src 源IP
src_port 源PORT
示例:
acl invalid_src src 172.16.100.200
<value>的類型:
- boolean
- integer or integer range
- IP address / network
- string (exact, substring, suffix, prefix, subdir, domain)
- regular expression
- hex block
<flags>
-i 不區分大小寫
-m 使用指定的pattern匹配方法
-n 不做DNS解析
-u 強制每個ACL必須唯一ID,否則多個同名ACL或關系
-- 強制flag結束. 當字符串和某個flag相似時使用
[operator]
匹配整數值:eq、ge、gt、le、lt
匹配字符串:
- exact match (-m str) :字符串必須完全匹配模式
- substring match (-m sub) :在提取的字符串中查找模式, 如果其中任何一個被發現,ACL將匹配
- prefix match (-m beg) :在提取的字符串首部中查找模式 ,如果其中任何一個被發現,ACL將匹配
- suffix match (-m end) :將模式與提取字符串的尾部進行比較,如果其中任何一個匹配,則ACL進行匹配
- subdir match (-m dir) :查看提取出來的用斜線分隔( “/”)的字符串,如果其中任何一個匹配,則ACL進行匹配
- domain match (-m dom) :查找提取的用點(“.”)分隔 字符串,如果其中任何一個匹配,則ACL進行匹配
acl作為條件時的邏輯關系:
-
與:隱式(默認)使用
-
或:使用“or” 或 “||”表示
-
否定:使用“!“ 表示
示例:
if invalid_src invalid_port 與關系
if invalid_src || invalid_port 或
if ! invalid_src 非
base : string
返回第一個主機頭和請求的路徑部分的連接,該請求從第一個斜杠開始,並在問號之前結束,對虛擬主機有用
<scheme>://<user>:<password>@<host>:<port>/<path>;< params>?<query>#<frag>
base : exact string match
base_beg : prefix match
base_dir : subdir match
base_dom : domain match
base_end : suffix match
base_len : length match
base_reg : regex match
base_sub : substring match
path : string
提取請求的URL路徑,該路徑從第一個斜杠開始,並在問號之 前結束(無主機部分)
<scheme>://<user>:<password>@<host>:<port>/<path>;< params>?<query>#<frag>
path : exact string match
path_beg : prefix match 匹配路徑開頭
path_dir : subdir match
path_dom : domain match
path_end : suffix match 匹配路徑結尾
path_len : length match
path_reg : regex match 正則表達式匹配一類PATH
path_sub : substring match
url : string
提取請求中的URL。一個典型的應用是具有預取能力的緩存, 以及需要從數據庫聚合多個信息並將它們保存在緩存中的網頁門戶入口
url : exact string match
url_beg : prefix match URL開頭,匹配協議
url_dir : subdir match
url_dom : domain match
url_end : suffix match URL結尾
url_len : length match
url_reg : regex match 正則表達式匹配一類url
url_sub : substring match
req.hdr([
[,
]]) : string
提取在一個HTTP請求報文的首部
hdr([<name>[,<occ>]]) : exact string match
hdr_beg([<name>[,<occ>]]) : prefix match 首部開頭
hdr_dir([<name>[,<occ>]]) : subdir match
hdr_dom([<name>[,<occ>]]) : domain match
hdr_end([<name>[,<occ>]]) : suffix match 首部結尾
hdr_len([<name>[,<occ>]]) : length match
hdr_reg([<name>[,<occ>]]) : regex match
hdr_sub([<name>[,<occ>]]) : substring match
示例:
acl bad_curl hdr_sub(User-Agent) -i curl
block if bad_curl
status : integer
返回在響應報文中的狀態碼
預定義ACL
ACL名稱 等價於 說明
TRUE always_true 總是匹配
FALSE always_false 從不匹配
HTTP req_proto_http 匹配HTTP協議
HTTP_1.0 req_ver 1.0 匹配HTTP協議1.0
HTTP_1.1 req_ver 1.1 匹配HTTP協議1.1
HTTP_CONTENT hdr_val(content-length) gt 0 匹配已存在內容長度
HTTP_URL_ABS url_reg [/:]*😕/ 匹配URL絕對路徑
HTTP_URL_SLASHurl_beg / 匹配URL相對路徑
HTTP_URL_STAR url * 匹配 URL 等於 "*"
LOCALHOST src 127.0.0.1/8 匹配從localhost來的連接
METH_CONNECT method CONNECT 匹配HTTP CONNECT方法
METH_GETmethod GET HEAD #match HTTP GET or HEAD method
METH_HEAD method HEAD #match HTTP HEAD method
METH_OPTIONS method OPTIONS #match HTTP OPTIONS method
METH_POST method POST #match HTTP POST method
METH_TRACE method TRACE #match HTTP TRACE method
RDP_COOKIE req_rdp_cookie_cnt gt 0 #match presence of an RDP cookie
REQ_CONTENT req_len gt 0 #match data in the request buffer
WAIT_ENDwait_end #wait for end of content analysis
acl配置
基於IP的訪問控制
use_backend <backend> [{if | unless} <condition>]
當if/unless一個基於ACL的條件匹配時切換指定backend
acl invalid_src src 172.16.200.2
block if invalid_src
errorfile 403 /etc/fstab
七層請求的訪問控制
http-request { allow | deny |add-header <name> <fmt> |set-header <name> <fmt> } [ { if | unless } <condition> ]
四層請求訪問控制
tcp-request connection {accept|reject} [{if | unless} <condition>]
示例:
listen ssh
bind :22022
balance leastconn
acl invalid_src src 172.16.200.2
tcp-request connection reject if invalid_src
mode tcp
server sshsrv1 172.16.100.6:22 check
server sshsrv2 172.16.100.7:22 check backup
基於ACL的動靜分離示例
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
use_backend staticsrvs if url_static
default_backend appsrvs
backend staticsrvs
balance roundrobin
server stcsrv1 172.16.100.6:80 check
backend appsrvs
balance roundrobin
server app1 172.16.100.7:80 check
server app1 172.16.100.7:8080 check
listen stats
bind :9091
stats enable
stats auth admin:admin
stats admin if TRUE
#一個ACL定義了兩個條件,如果用戶的請求滿足PATH中帶有/static /images /javascript /stylesheets 這些字符的,或者path是以.jpg .gif .png .css .js .html .txt .htm 這些字符結尾的就匹配ACL定義
#滿足ACL定義的請求為靜態請求,被調度到后端的staticsrvs機組上
#不滿組以上兩個條件的請求默認調度都后端包含兩台服務器輪詢的appsrvs機組上
支持https協議
配置HAProxy支持https協議:
1 支持ssl會話;
bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE
crt 后證書文件為PEM格式,且同時包含證書和所有私鑰
cat demo.crt demo.key > demo.pem
2 把80端口的請求重向定443
bind *:80
redirect scheme https if !{ ssl_fc }
3 向后端傳遞用戶請求的協議和端口(frontend或backend)
http_request set-header X-Forwarded-Port %[dst_port]
http_request add-header X-Forwared-Proto https if { ssl_fc }