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


 

正常情況下,nginx做反向代理,如果后端節點服務器宕掉的話,nginx默認是不能把這台realserver踢出upstream負載集群的,所以還會有請求轉發到后端的這台realserver上面,這樣勢必造成網站訪問故障。雖然nginx可以在localtion中啟用proxy_next_upstream來解決返回給用戶的錯誤頁面,如下:

例如公司的網站訪問的時候全部變成404頁面,最后發現是后端的一台服務器不可用,直接訪問那台后台的服務器的時候,返回的是404頁面,因為upstream 里面設置了ip_hash。所以導致訪問網站時怎么刷新都是404頁面。這時可以使用nginx的一個功能,就是當后端的服務器返回給nginx502、504、404、執行超時等錯誤狀態的時候,nginx會自動再把這個請求轉發到upstream里面別的服務器上面,從而給網站用戶提供更穩定的服務。
配置如下:
location /
{
#如果后端的服務器返回502、504、執行超時等錯誤,自動將請求轉發到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模塊
------------------------------------------------------------------------------------------------------------------------

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

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的效率是非常低的。

具體實例配置如下:

[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日志里。

二、利用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中。部署流程如下:

比如健康檢查配置:
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;
	     }
      }
}

具體實例配置如下: 

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實時的健康狀態。
溫馨提示:在生產環境的實施應用中需要注意下面兩點

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后端節點健康檢查的方法 

三、利用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版本以后都不支持了!因此,對於目前常見的生產環境上基本都不會去用這個模塊了~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM