Nginx負載均衡中後端節點服務器健康檢查 - 運維筆記

 

正常狀況下,nginx作反向代理,若是後端節點服務器宕掉的話,nginx默認是不能把這臺realserver踢出upstream負載集羣的,因此還會有請求轉發到後端的這臺realserver上面,這樣勢必形成網站訪問故障。雖然nginx能夠在localtion中啓用proxy_next_upstream來解決返回給用戶的錯誤頁面,以下:javascript

例如公司的網站訪問的時候所有變成404頁面,最後發現是後端的一臺服務器不可用,直接訪問那臺後臺的服務器的時候,返回的是404頁面,由於upstream 裏面設置了ip_hash。因此致使訪問網站時怎麼刷新都是404頁面。這時可使用nginx的一個功能,就是當後端的服務器返回給nginx50二、50四、40四、執行超時等錯誤狀態的時候,nginx會自動再把這個請求轉發到upstream裏面別的服務器上面,從而給網站用戶提供更穩定的服務。
配置以下:
location /
{
#若是後端的服務器返回50二、50四、執行超時等錯誤,自動將請求轉發到upstream負載均衡池中的另外一臺服務器,實現故障轉移。
proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;
}
這樣的話,也算是保障了後端服務器的一個高可用性。(下面實例配置中會用到)

以上的配置你們能夠參考一下,但這個仍是會把請求轉發給這臺服務器的,而後再轉發給別的服務器,這樣以來就浪費了一次轉發,對於網站性能來講也不是最佳理想的方案。爲了不上面說顧慮的狀況,能夠對nginx後方realserver的健康狀態進行檢查,若是發現後端服務器不可用,則請求不轉發到這臺服務器。
目前主要有三種方式能夠實現對nginx負載均衡的後端節點服務器進行健康檢查:
1)ngx_http_proxy_module模塊和ngx_http_upstream_module模塊(這是nginx自帶模塊)
    參考地址:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream
2)nginx_upstream_check_module模塊(淘寶技術團隊開發)
    參考地址:https://github.com/yaoweibin/nginx_upstream_check_module
3)ngx_http_healthcheck_module模塊
------------------------------------------------------------------------------------------------------------------------php

1、利用nginx自帶模塊ngx_http_proxy_module和ngx_http_upstream_module對後端節點作健康檢查
嚴格來講,nginx自帶是沒有針對負載均衡後端節點的健康檢查的,可是能夠經過默認自帶的ngx_http_proxy_module模塊和ngx_http_upstream_module模塊中的相關指令來完成當後端節點出現故障時,自動切換到健康節點來提供訪問。下面列出這兩個模塊中相關的指令:css

1)ngx_http_proxy_module模塊中的 proxy_connect_timeout指令、proxy_read_timeout指令和proxy_next_upstream指令
語法:   proxy_connect_timeout time;
默認值:  proxy_connect_timeout 60s;
上下文:  http, server, location
設置與後端服務器創建鏈接的超時時間。應該注意這個超時通常不可能大於75秒。
 
語法: proxy_read_timeout time;
默認值:  proxy_read_timeout 60s;
上下文:  http, server, location
定義從後端服務器讀取響應的超時。此超時是指相鄰兩次讀操做之間的最長時間間隔,而不是整個響應傳輸完成的最長時間。若是後端服務器在超時時間段內沒有傳輸任何數據,鏈接將被關閉。
 
---------------------------------------------------------------------------------------
語法: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 |http_404 | off ...;
默認值:  proxy_next_upstream error timeout;
上下文:  http, server, location
指定在何種狀況下一個失敗的請求應該被髮送到下一臺後端服務器:
 
error      和後端服務器創建鏈接時,或者向後端服務器發送請求時,或者從後端服務器接收響應頭時,出現錯誤
timeout    和後端服務器創建鏈接時,或者向後端服務器發送請求時,或者從後端服務器接收響應頭時,出現超時
invalid_header  後端服務器返回空響應或者非法響應頭
http_500   後端服務器返回的響應狀態碼爲500
http_502   後端服務器返回的響應狀態碼爲502
http_503   後端服務器返回的響應狀態碼爲503
http_504   後端服務器返回的響應狀態碼爲504
http_404   後端服務器返回的響應狀態碼爲404
off        中止將請求發送給下一臺後端服務器
 
須要理解一點的是,只有在沒有向客戶端發送任何數據之前,將請求轉給下一臺後端服務器纔是可行的。也就是說,若是在傳輸響應到客戶端時出現錯誤或者超時,這類錯誤是不可能恢復的。
 
範例以下(這個在文檔開頭已介紹):
 
http {
proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;
}
---------------------------------------------------------------------------------------
 
2)ngx_http_upstream_module模塊中的server指令
語法: server address [parameters];
默認值:  ―
上下文:  upstream
 
範例以下:
   upstream name {
      server 10.1.1.110:8080 max_fails=1 fail_timeout=10s;
      server 10.1.1.122:8080 max_fails=1 fail_timeout=10s;
        }
 
--------------指令參數解釋----------------
max_fails=number   設定Nginx與服務器通訊的嘗試失敗的次數。在fail_timeout參數定義的時間段內,若是失敗的次數達到此值,Nginx就認爲服務器不可用。在下一個fail_timeout時間段,服務器不會再被嘗試。
                   失敗的嘗試次數默認是1。設爲0就會中止統計嘗試次數,即不對後端節點進行健康檢查。認爲服務器是一直可用的。
 
fail_timeout=time  設定服務器被認爲不可用的時間段以及統計失敗嘗試次數的時間段。在這段時間中,服務器失敗次數達到指定的嘗試次數,服務器就被認爲不可用。
                   默認狀況下,該超時時間是10秒。
 
在實際應用當中:
1)若是後端應用是可以快速重啓的應用,好比nginx的話,自帶的模塊是能夠知足需求的。
   可是須要注意,若是後端有不健康節點,負載均衡器依然會先把該請求轉發給該不健康節點,而後再轉發給別的節點,這樣就會浪費一次轉發。
2)若是當後端應用重啓時,重啓操做須要好久才能完成的時候就會有可能拖死整個負載均衡器。
   此時,因爲沒法準確判斷節點健康狀態,致使請求handle住,出現假死狀態,最終整個負載均衡器上的全部節點都沒法正常響應請求。
 
好比公司的業務程序是java開發的,所以後端主要是nginx集羣和tomcat集羣。因爲tomcat重啓應部署上面的業務不一樣,有些業務啓動初始化時間過長,就會致使上述現象的發生,所以不是很建議使用該模式。
而且ngx_http_upstream_module模塊中的server指令中的max_fails參數設置值,也會和ngx_http_proxy_module 模塊中的的proxy_next_upstream指令設置起衝突。
若是將max_fails設置爲0,則表明不對後端服務器進行健康檢查,這樣還會使fail_timeout參數失效(即不起做用)。
此時判斷後端服務器狀況的惟一依據即是ngx_http_proxy_module模塊中的proxy_connect_timeout指令和proxy_read_timeout指令,經過將它們的值調低來發現不健康節點,進而將請求往健康節點轉移。
若是這兩個參數設置得太小,但後端程序的執行或多或少會超過這個時間的話,這種狀況nginx的效率是很是低的。

具體實例配置以下:html

[root@master-node ~]# vim /usr/local/nginx/conf/nginx.conf
user  www;
worker_processes  8;
  
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
  
#pid        logs/nginx.pid;
  
  
events {
    worker_connections  65535;
}
  
  
http {
    include       mime.types;
    default_type  application/octet-stream;
    charset utf-8;
        
    ######
    ## set access log format
    ######
    log_format  main  '$remote_addr $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '$http_user_agent $http_x_forwarded_for $request_time $upstream_response_time $upstream_addr $upstream_status';
  
    #######
    ## http setting
    #######
    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    on;
    keepalive_timeout  65;
    proxy_cache_path /var/www/cache levels=1:2 keys_zone=mycache:20m max_size=2048m inactive=60m;
    proxy_temp_path /var/www/cache/tmp;
  
    fastcgi_connect_timeout 3000;
    fastcgi_send_timeout 3000;
    fastcgi_read_timeout 3000;
    fastcgi_buffer_size 256k;
    fastcgi_buffers 8 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_intercept_errors on;
  
    #
    client_header_timeout 600s;
    client_body_timeout 600s;
   # client_max_body_size 50m;
    client_max_body_size 100m;               #容許客戶端請求的最大單個文件字節數
    client_body_buffer_size 256k;            #緩衝區代理緩衝請求的最大字節數,能夠理解爲先保存到本地再傳給用戶
  
    gzip  on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php;
    gzip_vary on;
  
    ## includes vhosts
    include vhosts/*.conf;
}
 
 
[root@master-node ~]# mkdir /usr/local/nginx/conf/vhosts
[root@master-node ~]# mkdir /var/www/cache
[root@master-node ~]# ulimit 65535
 
[root@master-node ~]# vim /usr/local/nginx/conf/vhosts/LB.conf
upstream LB-WWW {
      ip_hash;                                                  #這是負載均衡的ip_hash負載策略,好處是實現session共享。根據需求決定是否加這個配置。
      server 192.168.1.101:80 max_fails=3 fail_timeout=30s;     #max_fails = 3 爲容許失敗的次數,默認值爲1。 這是對後端節點作健康檢查。
      server 192.168.1.102:80 max_fails=3 fail_timeout=30s;     #fail_timeout = 30s 當max_fails次失敗後,暫停將請求分發到該後端服務器的時間
      server 192.168.1.118:80 max_fails=3 fail_timeout=30s;
    }
     
server {
     listen       80;
     server_name  www.wangshibo.com;
   
      access_log  /usr/local/nginx/logs/www-access.log main;
      error_log  /usr/local/nginx/logs/www-error.log;
   
     location / {
         proxy_pass http://LB-WWW;
         proxy_redirect off ;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header REMOTE-HOST $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_connect_timeout 300;             #跟後端服務器鏈接超時時間,發起握手等候響應時間
         proxy_send_timeout 300;                #後端服務器回傳時間,就是在規定時間內後端服務器必須傳完全部數據
         proxy_read_timeout 600;                #鏈接成功後等待後端服務器的響應時間,已經進入後端的排隊之中等候處理
         proxy_buffer_size 256k;                #代理請求緩衝區,會保存用戶的頭信息以供nginx進行處理
         proxy_buffers 4 256k;                  #同上,告訴nginx保存單個用幾個buffer最大用多少空間
         proxy_busy_buffers_size 256k;          #若是系統很忙時候能夠申請最大的proxy_buffers
         proxy_temp_file_write_size 256k;       #proxy緩存臨時文件的大小
         proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
         proxy_max_temp_file_size 128m;
         proxy_cache mycache;                   #若是該域名負載的訪問請求不須要緩存功能,那就將這如下四行所有註釋掉。
         proxy_cache_valid 200 302 1h; 
         proxy_cache_valid 301 1d;
         proxy_cache_valid any 1m;
        }
}

 須要注意的是:前端

上面的nginx負載均衡配置中已經開啓了cache緩存功能,若是不須要緩存功能,則將上面vhosts目錄下的虛擬主機配置中的proxy_cache mycache及其下面三行註釋便可!

這裏說下曾經碰到過的一個反常狀況:
按照上面第一種nginx upstream的健康檢查配置後,發現將upstream中的後端兩臺機器中的一臺關閉,訪問請求仍是會打到這臺關閉的後端機器上
查看方法:
直接瀏覽器裏訪問,發現訪問結果一下子好,一下子壞,這是由於請求不只打到了後端好的機器上,也打到了關閉的機器上了;

緣由分析:
這是由於後端兩臺機器+端口的訪問結果被包含在了 proxy_next_upstream中定義的狀態碼。

解決辦法:
將
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
改爲
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;

重啓nginx服務後,在瀏覽器裏輸入域名訪問,發現訪問結果是正常的了!
說明:
該域名的訪問請求都打到了後端好着的那臺服務器上了,那臺關閉的服務器已經從upstream負載中踢出去了。

這個經過查看對應域名的access.log日誌能發現:
訪問請求會同時到達後端兩臺機器上,只不過請求到達關閉的那臺機器上時就會經過健康檢查發現它是壞的,就會將它自動提出,這樣在瀏覽器裏的訪問結果顯示的就
只是正常的那臺後端機器處理後的結果了。

查看error.log錯誤日誌,發現裏面的信息都是:訪問請求upstream到後端關閉的機器上時,全是"connect() failed (111: Connection refused)",這是正常的,
由於upstream配置裏每一個幾秒就會去健康後端機器,當鏈接失敗時,錯誤信息就輸出到error.log日誌裏。

2、利用nginx_upstream_check_module模塊對後端節點作健康檢查
除了上面介紹的nginx自帶模塊,還有一個更專業的模塊,來專門提供負載均衡器內節點的健康檢查的。這個就是淘寶技術團隊開發的nginx模塊。
用nginx作前端反向代理,若是後端服務器宕掉的話,nginx是不會把這臺realserver踢出upstream的,還會把請求轉發到後端的這臺realserver上面。因此當某臺機器出現問題時,會看到nginx的日誌會有一段轉發失敗而後轉發正常的日誌。藉助淘寶技術團隊開發的nginx模快nginx_upstream_check_module來檢測後方realserver的健康狀態,若是後端服務器不可用,則會將其踢出upstream,全部的請求不轉發到這臺服務器。當期恢復正常時,將其加入upstream
nginx_upstream_check_module,經過它能夠用來檢測後端realserver的健康狀態。若是後端realserver不可用,則因此的請求就不會轉發到該節點上。我的比較推薦使用這種方式來檢查nginx後端節點的健康狀態。
在淘寶本身的tengine上是自帶了該模塊的,你們能夠訪問淘寶tengine的官網http://tengine.taobao.org來獲取該版本的nginx,
若是沒有使用淘寶的tengine的話,能夠經過補丁的方式來添加該模塊到咱們本身的nginx中。部署流程以下:java

好比健康檢查配置:
upstream test_web {
    server 192.168.1.21:80;
    server 192.168.1.22:80;
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
       
}
上面配置的意思是,對test_web這個負載均衡條目中的全部節點,每一個3秒(3000毫秒)檢測一次,請求2次正常則標記realserver狀態爲up,若是檢測5次都失敗,則標記realserver的狀態爲down,超時時間爲1秒。

---------------------下面列出nginx_upstream_check_module 模塊所支持的指令含義----------------------
Syntax:  check interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp] [port=check_port]
Default: 若是沒有配置參數,默認值是:interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp
Context: upstream

該指令能夠打開後端服務器的健康檢查功能。指令後面的參數意義是:
interval:向後端發送的健康檢查包的間隔。
fall(fall_count): 若是連續失敗次數達到fall_count,服務器就被認爲是down。
rise(rise_count): 若是連續成功次數達到rise_count,服務器就被認爲是up。
timeout: 後端健康請求的超時時間,單位毫秒。
default_down: 設定初始時服務器的狀態,若是是true,就說明默認是down的,若是是false,就是up的。默認值是true,也就是一開始服務器認爲是不可用,要等健康檢查包達到必定成功次數之後纔會被認爲是健康的。
type:健康檢查包的類型,如今支持如下多種類型:
     tcp:簡單的tcp鏈接,若是鏈接成功,就說明後端正常。
     ssl_hello:發送一個初始的SSL hello包並接受服務器的SSL hello包。
     http:發送HTTP請求,經過後端的回覆包的狀態來判斷後端是否存活。
     mysql: 向mysql服務器鏈接,經過接收服務器的greeting包來判斷後端是否存活。
     ajp:向後端發送AJP協議的Cping包,經過接收Cpong包來判斷後端是否存活。
     port: 指定後端服務器的檢查端口。你能夠指定不一樣於真實服務的後端服務器的端口,好比後端提供的是443端口的應用,你能夠去檢查80端口的狀態來判斷後端健康情況。默認是0,表示跟後端server提供真實服務的端口同樣。該選項出現於Tengine-1.4.0。


Syntax: check_keepalive_requests request_num
Default: 1
Context: upstream
該指令能夠配置一個鏈接發送的請求數,其默認值爲1,表示Tengine完成1次請求後即關閉鏈接。

Syntax: check_http_send http_packet
Default: "GET / HTTP/1.0\r\n\r\n"
Context: upstream
該指令能夠配置http健康檢查包發送的請求內容。爲了減小傳輸數據量,推薦採用"HEAD"方法。

當採用長鏈接進行健康檢查時,需在該指令中添加keep-alive請求頭,如:"HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"。 同時,在採用"GET"方法的狀況下,請求uri的size不宜過大,確保能夠在1個interval內傳輸完成,不然會被健康檢查模塊視爲後端服務器或網絡異常。
Syntax: check_http_expect_alive [ http_2xx | http_3xx | http_4xx | http_5xx ]
Default: http_2xx | http_3xx
Context: upstream
該指令指定HTTP回覆的成功狀態,默認認爲2XX和3XX的狀態是健康的。

Syntax: check_shm_size size
Default: 1M
Context: http
全部的後端服務器健康檢查狀態都存於共享內存中,該指令能夠設置共享內存的大小。默認是1M,若是你有1千臺以上的服務器並在配置的時候出現了錯誤,就可能須要擴大該內存的大小。

Syntax: check_status [html|csv|json]
Default: check_status html
Context: location
顯示服務器的健康狀態頁面。該指令須要在http塊中配置。

在Tengine-1.4.0之後,能夠配置顯示頁面的格式。支持的格式有: html、csv、 json。默認類型是html。
也能夠經過請求的參數來指定格式,假設‘/status’是你狀態頁面的URL, format參數改變頁面的格式,好比:
/status?format=html
/status?format=csv
/status?format=json

同時你也能夠經過status參數來獲取相同服務器狀態的列表,好比:
/status?format=html&status=down
/status?format=csv&status=up

下面是一個狀態配置的範例:
http {
      server {
	     location /nstatus {
		     check_status;
		     access_log off;
		     #allow IP;
		     #deny all;
	     }
      }
}

具體實例配置以下: node

1)下載nginx_upstream_check_module模塊,並部署到nginx中。
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/master.zip
[root@localhost src]# unzip unzip master.zip
[root@localhost src]# ls
master.zip  nginx_upstream_check_module-master
[root@localhost src]# ls nginx_upstream_check_module-master/
CHANGES              check_1.2.6+.patch   check.patch                ngx_http_upstream_check_module.c          upstream_fair.patch
check_1.11.1+.patch  check_1.5.12+.patch  config                     ngx_http_upstream_check_module.h          util
check_1.11.5+.patch  check_1.7.2+.patch   doc                        ngx_http_upstream_jvm_route_module.patch
check_1.2.1.patch    check_1.7.5+.patch   nginx-sticky-module.patch  README
check_1.2.2+.patch   check_1.9.2+.patch   nginx-tests                test
  
[root@localhost src]# wget http://nginx.org/download/nginx-1.8.0.tar.gz
[root@localhost src]# tar -zxvf nginx-1.8.0.tar.gz
[root@localhost src]# cd nginx-1.8.0
  
[root@localhost nginx-1.8.0]# patch -p1 < ../nginx_upstream_check_module-master/check_1.9.2+.patch
[root@localhost nginx-1.8.0]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --add-module=../nginx_upstream_check_module-master/
[root@node1 src]# make && make install
  
2)nginx配置
[root@master-node ~]# vim /usr/local/nginx/conf/vhosts/LB.conf
upstream LB-WWW {
      server 192.168.1.101:80;  
      server 192.168.1.102:80;
      check interval=3000 rise=2 fall=5 timeout=1000 type=http;
      check_keepalive_requests 100;
      check_http_send "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
      check_http_expect_alive http_2xx http_3xx;
    }
       
server {
     listen       80;
     server_name  www.wangshibo.com;
     
      access_log  /usr/local/nginx/logs/www-access.log main;
      error_log  /usr/local/nginx/logs/www-error.log;
     
     location / {
         proxy_pass http://LB-WWW;
         proxy_redirect off ;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header REMOTE-HOST $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_connect_timeout 300;          
         proxy_send_timeout 300;            
         proxy_read_timeout 600;              
         proxy_buffer_size 256k;              
         proxy_buffers 4 256k;               
         proxy_busy_buffers_size 256k;        
         proxy_temp_file_write_size 256k;     
         proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
         proxy_max_temp_file_size 128m;
         proxy_cache mycache;                             
         proxy_cache_valid 200 302 60m;                   
         proxy_cache_valid 404 1m;
        }
        
       location /nstatus {
         check_status;
         access_log off;
         #allow IP;
         #deny all;
       }
}

配置完畢後,重啓nginx。而後訪問http://localhost/nstatus這個頁面就能夠看到當前兩臺realserver實時的健康狀態。
舒適提示:在生產環境的實施應用中須要注意下面兩點mysql

1)主要定義好type。因爲默認的type是tcp類型,所以假設服務啓動,無論是否初始化完畢,它的端口都會起來,因此此時前段負載均衡器爲認爲該服務已經可用,實際上是不可用狀態。
2)注意check_http_send值的設定。因爲它的默認值是"GET / HTTP/1.0\r\n\r\n"。
   假設應用是經過http://ip/name訪問的,那麼這裏check_http_send值就須要更改成 "GET /name HTTP/1.0\r\n\r\n"才能夠。
   針對採用長鏈接進行檢查的, 這裏增長 keep-alive請求 頭,即"HEAD /name HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"。
   若是後端的tomcat是基於域名的多虛擬機,此時你須要經過 check_http_send定義host,否則每次訪問都是失敗,範例:
   check_http_send "GET /mobileapi HTTP/1.0\r\n HOST  www.redhat.sx\r\n\r\n";

也能夠參考http://www.cnblogs.com/kevingrace/p/5882006.html這篇文檔裏的nginx後端節點健康檢查的方法 nginx

3、利用ngx_http_healthcheck_module模塊對後端節點作健康檢查
除了上面兩個模塊,nginx官方在早期的時候還提供了一個ngx_http_healthcheck_module模塊用來進行nginx後端節點的健康檢查。nginx_upstream_check_module模塊就是參照該模塊的設計理念進行開發的,所以在使用和效果上都大同小異。
可是須要注意的是,ngx_http_healthcheck_module模塊僅僅支持nginx的1.0.0版本,1.1.0版本之後都不支持了!所以,對於目前常見的生產環境上基本都不會去用這個模塊了~git

相關文章
相關標籤/搜索