基於Nginx反向代理及負載均衡php
只要沒有被啓用,默認就是開啓的,由於proxy屬於nginx內置標準模塊,一般實現代理的時候,最核心模塊是proxy_pass,用於將用戶請求的rui遞交至上游服務器的某個URI但這個模塊大部分用於location當中,所以要實現將某一URI的訪問代理某個上游服務器大體的格式爲:css
location /name/ {html
proxy_pass http://127.0.0.1/remote/;前端
}node
參數解釋:nginx
location /name/ 指定當前服務器server的某一訪問路徑,原本這個location中定義的是root或其餘相關參數,今後這個 location不在本地提供任何服務,而是經過proxy_pass模塊傳至遠程其餘主機http://127.0.0.1/remote/上去web
其中/name/ 和 /remote/ 能夠是不相匹配的, nginx能夠自動處理這種映射關係。express
但須要注意的是,當定義location的時候,其必須有一個轉換關係,意爲咱們當前主機的路徑uri要轉換另外服務器的uri,這是其對應關係,事實上目標主機的uri能夠省略掉,可是一旦省略掉就表示不將其轉換apache
示例:vim
location/some/path/ {
proxy_pass http://127.0.0.1;#這裏只有ip地址,後面沒有任何uri
}
例:
nginx-->web
如圖所示當咱們請求nginx某個uri的時候,nginx自動的代理至web服務器中,它並不真正提供用戶請求的內容而僅僅的將用戶的請求接進來,而且代理用戶去web服務器上去取數據
一般web服務器會記錄日誌的,那麼這時訪問日誌中遠程ip則是nginx的ip地址
對於一個web服務器來說記錄的全部的ip地址都是代理服務器的,那麼咱們在某些分析的層面來說是沒有意義的,因此一般在這類場景下一般須要在nginx服務上作一些簡單修改,而且在後端服務器上也作一些修改用於記錄真實的客戶端IP地址
將請求在nginx轉發至後端主機的時候在頭部加入header信息
參考:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_header
實現反向代理
IP地址 服務器角色
10.0.10.61 Nginx 實現反向代理
10.0.10.83 Apache上游服務器
10.0.10.82 Apache上游服務器
啓動後端apache 服務
[root@mode local]#/usr/local/apache/bin/apachectl start
[root@mode test]# echo'<h1>10.0.10.83</h1>' > index.html
[root@modephp_test]# curl localhost
<h1>10.0.10.83</h1>
咱們指望用戶訪問某個路徑的時候,來源的路徑是後端服務器的:
咱們只要建一個新的location就能夠了,以下所示
新創建location,明確說明使用proxy_pass模塊代理後端主機10.0.10.83
location /test {
proxy_pass http://10.0.10.83/; #注意一旦帶有斜線就表示有url的若是沒有url必定不能帶斜線,這就表示訪問的test沒錯,可是將test映射至後端主機,但後端並無test目錄
}
檢查語法是否正確並從新加載配置文件
[root@node1 nginx]#/usr/local/nginx/sbin/nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@node1 nginx]#/etc/init.d/nginx reload
使用curl訪問測試http://10.0.10.61/test
[root@node1 nginx]#curl http://10.0.10.61/test/
<h1>10.0.10.83</h1>
咱們再來查看後端apache的訪問日誌
[root@modetest_php]# tail access_w_20140508.log
10.0.10.61 - -[08/May/2014:11:04:38 +0800] "GET / HTTP/1.0" 304 - "-""Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, likeGecko) Chrome/34.0.1847.131 Safari/537.36"
10.0.10.61 - - [08/May/2014:11:04:38+0800] "GET / HTTP/1.0" 304 - "-" "Mozilla/5.0(Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/34.0.1847.131 Safari/537.36"
能夠看到來源地址都是nginx服務器的地址,對於咱們以後分析日誌沒有任何意義所在,因此那這個時候咱們想換成客戶端地址必須藉助於proxy_set_header模塊
實現顯示真實客戶端IP
proxy_set_header模塊使用示例
location /test {
proxy_passhttp://10.0.10.83/;
proxy_set_header X-Real-IP $remote_addr;
}
proxy_set_header表示將發送至upsream server的報文的某首部進行重寫;
X-Real-IP 表示 hader的名稱爲X-Real-IP
傳遞給X-Real-IP的地址是$remote_addr
再來查看後端日誌的訪問ip,不管怎麼刷新它的記錄ip依舊仍是nginx
由於後端的服務器只記錄了client值,並無記錄咱們定義的header的值,因此咱們還須要對apache的日誌參數作修改
編輯httpd.conf,定義日誌格式
[root@mode conf]#vim httpd.conf
將其註釋並複製
#LogFormat "%h%l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""combined
將%h改成"%{X-Real-ip}i
LogFormat "%{X-Real-IP}i %l %u %t \"%r\"%>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
%h是表示遠端客戶的ip地址,這裏咱們將其改爲某個特定首部的值也就是剛纔我定義的X-Real-IP,而引用特定首部的值的格式是%{xxx}i這樣就表示引用這個首部的值
保存退出並從新加載apache
[root@mode conf]#/usr/local/apache/bin/apachectl graceful
再次訪問並查看日誌
已看到目前已記錄客戶端的真實IP地址
[root@modetest_php]# tail -1 access_w_20140508.log
10.0.10.1- - [08/May/2014:12:44:40 +0800] "GET / HTTP/1.0" 304 -"-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/34.0.1847.131 Safari/537.36"
使用模式匹配
示例:
location ~/test {
proxy_pass http://10.0.10.83/hello;
proxy_set_header X-real-ip $remote_addr;
}
只要客戶端訪問的uri中只要包含test的路徑則進行跳轉至於10.0.10.83的/test的物理路徑;
一旦使用了模式匹配,那麼後面跟上test以後裏面代理的時候,後面必定不能帶有路徑hello,必定什麼路徑都不要加,它會將test的內容填充至於後面
測試:
location ~/test {
proxy_pass http://10.0.10.83/hello;#必定不能帶有任何字符
proxy_set_header X-real-ip $remote_addr;
}
保存退出並檢查語法
[root@node1 nginx]#nginx -t
nginx:[emerg] "proxy_pass" cannot have URI part in location given byregular expression, or inside named location, or inside "if"statement, or inside "limit_except" block in /etc/nginx/nginx.conf:49
nginx:configuration file /etc/nginx/nginx.conf test failed
首先語法檢測根本不經過,提示路徑中不能加入uri,因此若是出現此類配置方式是連服務都不能啓動的
將後面的uri去掉再測試:
location /test {
proxy_passhttp://10.0.10.83; #注意沒有斜線
proxy_set_headerX-real-ip $remote_addr;
}
總結:使用匹配模式,必定不能加uri,只是將其前端的參數傳遞過來
從新加載並訪問測試:
[root@mode test_php]#curl http://10.0.10.61/test
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
加入斜線
location /test {
proxy_passhttp://10.0.10.83/;
proxy_set_headerX-real-ip $remote_addr;
}
訪問測試:
[root@modetest_php]# curl http://10.0.10.61/test
<h1>10.0.10.83</h1>
若是location 給了uri 而proxy_pass沒有跟任何uri 意味着將location的uri附加至proxy_pass 當作請求的路徑,哪怕加一個斜線也表示對應的uri,因此請求的test路徑相對於http://10.0.10.83/目錄下的文件
基於匹配規則實現動靜分離
新建location,內容以下
location ~* \.(jpg|bng|html|css|png|gif|ico|)${
proxy_pass http://10.0.10.83;
proxy_set_header X-real-ip $remote_addr;
}
若是訪問的內容是不區分大小寫的方式:
若是訪問的uri後綴是以靜態內容或圖片格式結尾的,那麼直接將請求轉向10.0.10.83
檢查語法並重啓
[root@node1 nginx]#!ngin
[root@node1 nginx]# !ser
而後在服務10.0.10.83服務器建立目錄及文件
上傳某張圖片或建立某路徑文件並訪問測試
[root@mode if]# pwd
/var/html/php_test/if
[root@mode if]#echo "test 10.0.10.83" > 1.html
[root@mode if]#curl http://10.0.10.61/if/1.html
1
先來測試一下訪問靜態內容
[root@node1 nginx]#curl http://10.0.10.61/if/1.html#指定其uri
test 10.0.10.83#能夠看到,已經成功跳轉至10.0.10.83
因此就算前端沒有這樣的路徑但能映射到後端服務器相關路徑都能訪問的到
那麼假如說有另外服務器能夠訪問動態內容的話,則能夠將全部的動態請求直接轉發至動態內容服務器
以下所示
咱們本機已經存在fastcgi,因此將nginx的fastcgi功能開啓,因爲9000端口在本地監聽,因此這裏默認便可
location ~ \.php$ {#已經默認幫咱們定義了location匹配規則,若是後綴是php的uri,那麼所有轉發至這個主機
root/web/htdocs;#指定其php目錄
fastcgi_pass127.0.0.1:9000;#fastcgi在咱們本機已監聽
fastcgi_indexindex.php;
fastcgi_paramSCRIPT_FILENAME/scripts$fastcgi_script_name;
includefastcgi_params;
}
定義在本機,
那麼直接訪問測試:
能夠發現,如今咱們將2個請求分開了
若是請求的是靜態內容則到10.0.10.83上去,若是請求的是動態內容則到本機,而若是動態請求在另外服務器上部署的也徹底能夠實現分割
基於匹配規則實現上傳轉發功能
隨着咱們業務量的愈來愈大,需求將上傳服務器也對其進行分離,在站點上搭建一個專門的服務器用來接收上傳
有時候web服務器能夠實現上傳,web也支持上傳,可是須要將DAV功能打開
新建虛機10.0.10.62並開啓apache上傳功能
[root@node2apache]# ./bin/apachectl -M | grep -i 'dav'
dav_module (static)
dav_fs_module (static)
這時只須要在DocumentRoot上對應的訪問權限上啓動對應的DAV便可
加入參數:
Dav on
Options Indexes FollowSymLinks
將網頁目錄給予寫權限
[root@node2apache]# setfacl -m u:apache:rwx /var/www/html/
編輯nginx配置文件,加入if判斷語句並引用$request_method模塊,判斷若是用戶操做是put,那麼將其轉發至10.0.10.62
location / {
root/web/htdocs/;
indexindex.php index.html index.htm;
if ($request_method ~*"PUT"){
proxy_pass http://10.0.10.62;
break;
}
}
保存退出檢查語法並從新加載配置
[root@node1 nginx]#nginx -t
nginx: theconfiguration file /etc/nginx/nginx.conf syntax is ok
nginx:configuration file /etc/nginx/nginx.conf test is successful
[root@node1 nginx]#!ser
這時咱們可使用curl -T 上傳文件
[root@node1 nginx]#curl -T koi-utf http://10.0.10.62/
查看文件是否存在
[root@node2 conf]#ll /var/www/html/
total 16
-rw-r--r--. 1rootroot11 May6 18:31 index.html
-rw-r--r--. 1rootroot6 Apr 28 21:15 index.html.bak
-rw-r--r--. 1apache apache 2837 May6 00:08 koi-utf
如上,說明支持put的方法,已經能夠上傳
假設咱們如今代理若是用戶請求的內容是上傳操做則專至62這臺服務器,這樣就一個靜態動態 一個負責上傳的角色,徹底將服務角色分割開來
因而可知,咱們的服務扮演了三種角色,將上傳、動態、靜態內容分別分發至不一樣的服務器
nginx實現負載均衡
nginx實現負載均衡有單獨一模塊來實現需求,叫作upstream
參考:http://nginx.org/en/docs/http/ngx_http_upstream_module.html
示例:
#首先在全局配置中定義upstream name
upstream backend {
server backend1.example.comweight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.example.com:8080backup;
server backup2.example.com:8080backup;
}
#定義完成後再從location中引用
server {
location / {
proxy_pass http://backend;
}
}
一旦啓動了此模塊,將引入一個新的上下文(只要加大括號就表示新引入一個新的上下文)所以upstream也引入了一個新的上下文
每一個server後面跟了一個主機名或ip 能夠加端口,但注意的是前面必定不能加http://
而upstream爲一個關鍵字,後面的backend爲名稱,說明這是一組服務器能夠被輪流訪問的
以後在location定義反向代理的時候,定義的不再是某個指定服務器了而是upstream,所以用戶的請求發往這個upstream之後,這個upstream模塊會自動從定義的規則中每一次選擇一個server 進行分發
爲了演示效果,咱們將後端的apache服務器分別建立不一樣的頁面,步驟略
配置nginx 先將其備份,方便以後恢復
[root@node1 nginx]#cp nginx.conf nginx.conf.bak
[root@node1 nginx]# vim nginx.conf
將以前定義的location全都刪掉,步驟略
在httpd{}上下文中定義upstream
upstream webservers {
server 10.0.10.62;
server 10.0.10.83;
}
定義upstream名爲webservers,其組內定義2個web主機 分別是10.0.10.62和10.0.10.83 注意的是ip前面不能加http://
定義location,以下所示
location / {
#root/web/htdocs/;
#indexindex.php index.html index.htm;
proxy_pass http://webservers;#webservers爲定義的upstream的名稱
}
假如用戶訪問的location的根目錄,當訪問的是根的時候直接使用proxy_pass直接映射至upstream組內的主機
保存退出檢測語法並重啓nginx
[root@node1 nginx]#!nginx
[root@node1 nginx]#!ser
訪問以下
[root@node1 nginx]#curl http://10.0.10.61
10.0.10.62
[root@node1 nginx]#curl http://10.0.10.61
<h1>10.0.10.83</h1>
實現後端realserver健康狀態檢查
使其一旦出現故障再也不將其加進來
定義upstream,以下所示
upstream webservers {
#server 10.0.10.62 ;
server 10.0.10.61 max_fails=3fail_timeout=1s backup;
server 10.0.10.83 weight=1max_fails=3 fail_timeout=2s;
}
weight=1 設置其權重;
max_fails=3最大容許3次失敗;
fail_timeout=2s若是超過2秒則超時;
在第三行能夠看到,咱們在本機也啓動一個web服務器,可是這個服務器不是專門提供工做的,一旦後端服務器出現故障,那麼則轉至backup服務器,使其服務器專門爲用戶提供錯誤頁面
backup表示其主機始終不會生效除非組內全部主機所有故障
大功告成