代理方式有三種:正向代理、透明代理和反向代理
正向代理
httpd通過ProxyRequests指令配置正向代理的功能。例如:
ProxyRequests On ProxyVia On <Proxy "*"> Require host internal.example.com </Proxy>
其中< Proxy >容器表示的是只有internal.example.com下的主機可以通過該正向代理去訪問任意URL的請求內容。ProxyVia指令表示在響應首部中添加一個Via字段。
反向代理
為了成為一個"基本的"web server,提供靜態和動態內容給最終用戶,httpd(以及其他大多數web server)可以扮演反向代理服務器的角色,也就是眾所周知的"網關"服務器。
在這種場景下,Httpd自身不生成產出數據,而是從后端服務器中獲取數據,這些后端服務器器一般不會和外界網絡通信。當httpd從客戶端接收到請求,請求被代理到后端服務器組中的其中一個服務器上,該后端服務器處理請求,生成內容並返回內容給httpd server,最后由httpd server生成實際的HTTP響應給客戶端。
有無數應該使用反向代理的理由,最常見的是安全、高可用、負載均衡、集中授權/認證。反向代理的布置和架構中,后端服務器(真正處理請求的服務器)和外界完全絕緣並由此受到保護,對於外界客戶端來說,當他們需要關心服務器對象是誰時,它們得到的結果總是反向代理服務器,而非后端服務器。
一個典型的實現如下:
簡單的反向代理配置
ProxyPass指令用於映射請求到后端服務器。最簡單的代理示例是對所有請求"/"都映射到一個后端服務器上:
ProxyPass "/" "http://www.example.com/" ProxyPassMatch "^/((?i).*\.php)$" "fcgi://127.0.0.1:9000/var/www/a.com/$1"
為了地址重定向時也能正確使用反向代理,應該使用ProxyPassReverse指令,該指令的作用見下文。
ProxyPass "/" "http://www.example.com/" ProxyPassReverse "/" "http://www.example.com/"
或者只為特定的URI進行代理,例如下面的配置,只有/images開頭的路徑才會代理轉發,其他的所有請求都在本地處理。
ProxyPass "/images" "http://www.example.com/" ProxyPassReverse "/images" "http://www.example.com/"
假如本地服務器地址為http://www1.example.com
,當請求http://www1.example.com/images/a.gif
時,將代理為http://www.example.com/a.gif
。
在沒有重定向的情況下,ProxyPassReverse是可以省略的,否則一般情況下應該將其設置為和ProxyPass相同。ProxyPassReverse提供的是一種功能,它並不依賴於ProxyPass(但一般都同時存在)。它的作用是防止重定向時客戶端無法正確訪問。例如,http://www.example.com/images/a.gif
被代理為http://192.168.100.17/a.gif
,如果此時a.gif被重定向到b.gif,那么代理服務器返回給客戶端的將是http://192.168.100.17/b.gif
。但客戶端是無法訪問后端內網主機192.168.100.17的,於是出現file not found錯誤。如果此時使用ProxyPassReverse,那么代理服務器響應給客戶端的請求會被調整為http://www.example.com/images/b.gif
,這樣的請求再發給代理服務器,就能正確訪問。
負載均衡:后端成員
上面的配置中沒有添加后端服務器節點,無法享受反向代理的優點。因此,有必要添加后端節點。添加的方法是使用< proxy >容器將后端節點定義成一個負載均衡組,各節點是該組中成員,然后代理目標指向組名即可。
例如:
<Proxy balancer://myset> BalancerMember http://www2.example.com:8080 BalancerMember http://www3.example.com:8080 ProxySet lbmethod=bytraffic </Proxy> ProxyPass "/images/" "balancer://myset/" ProxyPassReverse "/images/" "balancer://myset/"
alancer://myset告訴httpd,它創建了一個負載均衡節點集合,名稱為myset,此集合中有兩個后端成員。在上面的配置中,任意/images的請求都會代理至2個成員中的一個。ProxySet指令指定myset均衡組使用的均衡算法為bytraffic,即基於I/O流量字節數權重的算法。ProxySet指令設置的是Proxy容器的公共屬性。
httpd有3種負載均衡算法:
- byrequests:默認。基於請求數量計算權重。
- bytraffic:基於I/O流量大小計算權重。
- bybusyness:基於掛起的請求(排隊暫未處理)數量計算權重。
對於上面的示例,還可以稍加修改,使其支持更多功能。例如添加權重比例,使得某后端節點被轉發到的權重是另一節點的3倍,等待后端節點返回數據的超時時間為1秒。
<Proxy balancer://myset> BalancerMember http://www2.example.com:8080 BalancerMember http://www3.example.com:8080 loadfactor=3 timeout=1 ProxySet lbmethod=byrequests </Proxy> ProxyPass "/images" "balancer://myset/" ProxyPassReverse "/images" "balancer://myset/"
故障轉移
還可以再次調整實現故障轉移,例如當所有負載節點都失敗時,指定一個備份節點(standby node)。參考如下配置:
<Proxy balancer://myset> BalancerMember http://www2.example.com:8080 BalancerMember http://www3.example.com:8080 loadfactor=3 timeout=1 BalancerMember http://hstandby.example.com:8080 status=+H BalancerMember http://bkup1.example.com:8080 lbset=1 BalancerMember http://bkup2.example.com:8080 lbset=1 ProxySet lbmethod=byrequests </Proxy> ProxyPass "/images/" "balancer://myset/" ProxyPassReverse "/images/" "balancer://myset/"
其中成員1、2、4、5是負載節點,成員3是備份節點。當所有負載節點都不健康時,將轉發請求給備份節點,並由備份節點處理請求,httpd設置備份節點的方式很簡單,只需將狀態設置為"H",表示hot-standby。還需注意的是負載節點4、5,它們額外的參數為lbset=1,不寫時默認為0,這是負載均衡時的優先級設置,負載均衡時總是先轉發給低數值的節點,也就是說數值越小,優先級越高。所以上面的配置中,當節點1、2正常工作時,只在它們之間進行負載,此時節點4、5處於閑置狀態。只有當節點1、2都失敗時,才會在節點4、5之間進行負載。
提供負載狀態顯示頁面
<Location "/bm"> SetHandler balancer-manager Require host localhost Require ip 192.168.100 </Location>
然后在瀏覽器中輸入http://server/bm即可,返回結果如圖。
proxy相關指令
ProxyPass指令
該指令將遠程服務器映射到本地主機上,但本地主機不是真實的服務器,而是遠程主機的一個鏡像。這個鏡像通常稱為反向代理服務器或網關。該指令不能用於< Directory >、< Files >容器中,且使用該指令時通常會關閉正向代理,即ProxyRequests=off
。
語法:
ProxyPass [path] !|url [key=value [key=value ...]]
path參數為本地主機的URL路徑,url參數為遠程服務器的url一部分,不能包含查詢參數。如果第一個參數path尾隨了斜線,則url部分也必須尾隨斜線,反之亦然。如果該指令封裝在< Location >容器中,則第一個參數path可以省略,因為Location中已經指定了URL路徑。如果第二個參數為"!",則表示此path不使用反向代理功能。
<Location "/mirror/foo/"> ProxyPass "http://backend.example.com/foo/" </Location>
當訪問http://server/mirror/foo/bar時,將轉發到http://backend.example.com主機上,並請求該主機的/foo/bar文件。下面的配置指令與此等價。
ProxyPass "/mirror/foo/" "http://backend.example.com/foo/"
如果想讓某個子目錄不進行反向代理,而是在本地處理。可以設置第二個參數為"!"。例如,下面的配置中,/mirror/foo會被代理,但/mirror/foo/i則不會被代理。
ProxyPass "/mirror/foo/i" "!" ProxyPass "/mirror/foo" "http://backend.example.com"
再需要說明的是連接池,httpd會為后端節點創建連接池,httpd會連接連接池中的各個節點。后端節點屬性相同的共享一個連接池。后端節點的屬性由key=value參數指定。以下是常見的一些屬性設置,完整的屬性見官方手冊。
keepalive=Off|On
:默認為Off。設置httpd和后端節點之間是否開啟長連接,注意,這和web服務的長連接不一樣,此處設置的是反向代理服務器和后端節點兩者連接,當httpd將請求轉發給連接池中的一個節點,並等待返回數據,當數據返回完成后,連接立即關閉,如果開啟了長連接,連接暫時不關閉,只有等待均衡算法下次輪到該節點時才會再使用該連接。通常只有在httpd和后端節點間使用了防火牆時才設置為On。lbset=N
:默認為0。設置后端節點的優先級。數值N越低的,優先級越高。httpd總是會先嘗試優先級高的,只有優先級高的節點不可用時,才一會嘗試優先級低的。ping=N
:默認為0。設置和ajp13協議(不支持http協議)通信時健康狀況檢查時間間隔。該ping只能檢查是否能ping通對方,也就是檢測是否能與對方通信。單位為秒,可以帶上后綴"ms"表示毫秒。更多的健康狀況檢查應該使用mod_proxy_hcheck模塊。retry=N
:默認為60秒。當檢測到后端某節點錯誤狀態(error status)時,將在每N秒后才轉發一次請求給該節點。設置為0表示正常轉發請求,不用任何等待時間。該屬性通常設置用來維護服務器下線然后再上線的情況。-
status=VALUE
:將節點手動置為何種狀態。包括以下幾種狀態,各狀態可使用"+"(默認)來賦予屬性,使用"-"來取消屬性。例如"+H","S-E"。- D: 該節點被禁用,不再接受任何請求。
- S: 該節點處於管理維護的目的被停止。
- I: 將該節點設置為無視錯誤(ignore-errors)模式,此模式下httpd將認為該節點可用,總會轉發請求給該節點。
- H: 該節點處於hot-standby模式,該節點只有在其他所有后端節點都失效時才啟用。因此,該節點為備份節點。
- E: 將該節點設置為錯誤狀態(error-state)。
- N: 將該節點設置為drain模式,該模式只接受已預定粘滯會話的請求sticky session,其他所有請求都會被忽略。
-
timeout=ProxyTimeout
:設置httpd等待后端節點返回數據的超時時間。
如果使用了"balancer://",例如前面的balancer://myset,將創建一個虛擬的連接池。虛擬連接池中的各節點可共享部分屬性,也可以為每個節點設置上面所說的屬性。共享屬性使用ProxySet指令設置,常見的包括下面幾種:
lbmethod=METHOD
:設置負載均衡算法。有三種:byrequests(默認)按照請求數量計算均衡節點;bytraffic按照io流量計算均衡節點;bybusyness按照繁忙程度計算計算均衡節點。nofailover=On|Off
:默認為off。session不可用時是否轉移到其他具有相同session的節點上。如果后端節點不支持session復制,應將此項設置為on。stickysession
:設置session粘滯的名稱,如JSESSIONID、PHPSESSIONID。
例如:
<Proxy balancer://myset> BalancerMember http://www2.example.com:8080 BalancerMember http://www3.example.com:8080 loadfactor=3 timeout=1 BalancerMember http://hstandby.example.com:8080 status=+H BalancerMember http://bkup1.example.com:8080 lbset=1 BalancerMember http://bkup2.example.com:8080 lbset=1 ProxySet lbmethod=byrequests </Proxy> ProxyRequests off ProxyPass "/images/" "balancer://myset/" ProxyPassReverse "/images/" "balancer://myset/"
ProxyPassMatch指令
正則匹配模式的ProxyPass。例如:
ProxyPassMatch "^/(.*\.gif)$" "http://backend.example.com/$1" ProxyPassMatch "^/((?i).*\.php)$" "fcgi://127.0.0.1:9000/var/www/a.com/$1"
唯一需要注意的是,在正則匹配之前,遠程url參數必須是能夠解析的URL地址。例如下面兩條指令,第一條指令將失敗,因為在正則解析前,url參數無法解析為正確的URL地址,這是一個bug,可以通過修改正則表達式的分組部分將"/"分離出去,正如下面的第二個指令。
ProxyPassMatch "^(/.*\.gif)$" "http://backend.example.com:8000$1" ProxyPassMatch "^/(.*\.gif)$" "http://backend.example.com:8000/$1"
ProxySet指令
設置Proxy后端節點的屬性。通常用來設置共享屬性,但也可以設置某一個節點的屬性。
例如:
<Proxy "balancer://hotcluster"> BalancerMember "http://www2.example.com:8080" loadfactor=1 BalancerMember "http://www3.example.com:8080" loadfactor=2 ProxySet lbmethod=bytraffic </Proxy> <Proxy "http://backend"> ProxySet keepalive=On </Proxy> ProxySet "balancer://foo" lbmethod=bytraffic timeout=15
< Proxy >容器
< Proxy >容器用於封裝一組proxy相關指令,這些指令主要用於設置訪問權限、負載均衡成員組以及它們的屬性。
例如,下面的設置了只有yournetwork.example.com下的主機才能通過該(正向或反向代理)服務器訪問任意請求的內容(使用了*進行通配)。
<Proxy "*"> Require host yournetwork.example.com </Proxy> <Proxy "balancer://hotcluster"> BalancerMember "http://www2.example.com:8080" loadfactor=1 BalancerMember "http://www3.example.com:8080" loadfactor=2 ProxySet lbmethod=bytraffic </Proxy>
ProxyStatus指令
ProxyStatus {on|off|full}
決定是否開啟server-status中關於proxy的狀態信息,默認為off,full是on的同義詞。
例如
ProxyStatus on <Location "/server-status"> SetHandler server-status Require all granted </Location>
以下是關於proxy相關的狀態示例:
---------------------------------------------------------------------- Proxy LoadBalancer Status for balancer://myset SSes Timeout Method - 0 byrequests Sch Host Stat Route Redir F Set Acc Wr Rd http 192.168.100.14 Init Ok 1 0 0 0 0 http 192.168.100.15 Init Ok 3 0 0 0 0 http 192.168.100.54 Init Stby Ok 1 0 0 0 0 http 192.168.100.16 Init Ok 1 1 0 0 0 http 192.168.100.21 Init Ok 3 1 0 0 0 ---------------------------------------------------------------------- SSes Sticky session name Timeout Balancer Timeout Sch Connection scheme Host Backend Hostname Stat Worker status Route Session Route Redir Session Route Redirection F Load Balancer Factor Acc Number of uses Wr Number of bytes transferred Rd Number of bytes read
ProxyVia指令
是否在響應首部中添加"Via:"字段。可以設置為On/Off等。例如如設置為On時:
[root@xuexi ~]# curl -I http://192.168.100.17/index.html HTTP/1.1 200 OK Date: Sun, 01 Oct 2017 18:10:17 GMT Server: Apache/2.4.27 (Unix) Last-Modified: Sun, 01 Oct 2017 14:10:48 GMT ETag: "29-55a7cd31f2329" Accept-Ranges: bytes Content-Length: 41 Content-Type: text/html; charset=UTF-8 Via: 1.1 customer.sharktech.net
ProxyPass指令的排序和共享問題
ProxyPass指令有個需要注意的問題,在匹配生效時,最先被匹配到的指令立即生效,后面的都將失效。但如果ProxyPass指令放在< Location >容器中時,由於容器中只能放置一個ProxyPass指令(因為path參數一樣),此時匹配越精確的越優先。
例如下面的指令,如果將兩個ProxyPass指令位置調換,則/mirror/foo/i也仍會被代理。
ProxyPass "/mirror/foo/i" "!" ProxyPass "/mirror/foo" "http://backend.example.com"
可以將它們分別定義到< Location >容器中,這樣就無需考慮位置順序,而是考慮匹配的精確程度,因為Location容器自身有加載順序優先級。例如,下面的配置是可行的。
<Location "/mirror/foo/"> ProxyPass "http://backend.example.com/" </Location> <Location "/mirror/foo/i"> ProxyPass "!" </Location>
還需考慮一個共享的問題。下面兩個指令中的url參數各有長短,且第一個url是第二個url的子串。這時第二個ProxyPass的屬性部分總是會使用第一個指令的屬性。因此/examples/bar的請求被轉發到backend.example.com/examples/bar時,它的屬性timeout=60而非10。這樣的屬性共享可以減少創建連接池,相對來說更有效一些。
ProxyPass "/apps" "http://backend.example.com/" timeout=60 ProxyPass "/examples" "http://backend.example.com/examples" timeout=10
健康狀況檢查模塊
ProxyPass指令自帶了ping屬性,可用於簡單判斷后端節點是否健康,只要Ping能通信就認為是健康的。但顯然,對於Http服務來說,健康的指標並不能簡單地通過它來判斷。例如,檢測某個頁面是否正常、是否允許某方法等。因此,httpd提供了一個專門的健康狀況檢查模塊mod_proxy_hcheck用於個性化訂制檢查指標。
檢查指標也即檢查方法有以下幾種,由hcmethod指定:
- TCP:檢查是否能與后端節點建立TCP套接字,這就是問對方"你還活着嗎"。
- OPTIONS:發送一個HTTP OPTIONS請求給后端節點。
- HEAD:發送一個HTTP HEAD請求給后端節點。
- GET:發送一個HTTP GET請求給后端節點。
該健康狀況檢查模塊認為,只要HTTP方法的檢查指標返回2xx或3xx狀態碼都認為是健康的。
指定了檢查方法后,還需訂制檢查的細節,例如檢查的時間間隔。包括以下幾項:
- hcinterval:默認為30秒。發送檢查的時間間隔,單位為秒。
- hcuri:健康檢查時,追加在URL后的URI。通常用於GET檢查方法。
- hcpasses:默認為1。表示只有檢查了N次后都是通過的,才認為該節點是健康的可再次啟用。
- hcfails:默認為1。表示只有檢查了N次后都是失敗的,才認為該節點已經不健康,於是禁止使用該節點。
例如,以下是幾個健康檢查的配置示例:
<Proxy balancer://foo> BalancerMember http://www.example.com/ hcmethod=GET hcuri=/status.php BalancerMember http://www1.example.com/ hcmethod=TCP hcinterval=5 hcpasses=2 hcfails=3 BalancerMember http://www2.example.com/ </Proxy> ProxyPass "/" "balancer://foo" ProxyPassReverse "/" "balancer://foo"