公司前一段對業務線上的nginx做了整理,重點就是對nginx上負載均衡器的后端節點做健康檢查。目前,nginx對后端節點健康檢查的方式主要有3種,這里列出:
- ngx_http_proxy_module 模塊和ngx_http_upstream_module模塊(自帶)官網地址:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream
- nginx_upstream_check_module模塊 官網網址:https://github.com/yaoweibin/nginx_upstream_check_module
- ngx_http_healthcheck_module模塊(付費) 官網網址:http://wiki.nginx.org/NginxHttpHealthcheckModule
我會分別介紹這三種實現方式以及之間的差異性。
一、ngx_http_proxy_module 模塊和ngx_http_upstream_module模塊(自帶)
嚴格來說,nginx自帶是沒有針對負載均衡后端節點的健康檢查的,但是可以通過默認自帶的ngx_http_proxy_module 模塊和ngx_http_upstream_module模塊中的相關指令來完成當后端節點出現故障時,自動切換到健康節點來提供訪問。
這里列出這兩個模塊中相關的指令:
ngx_http_proxy_module 模塊中的 proxy_connect_timeout 指令、proxy_read_timeout指令和proxy_next_upstream指令
proxy_connect_timeout:
語法: proxy_connect_timeout time;
默認值: proxy_connect_timeout 60s;
上下文: http, server, location
設置與后端服務器建立連接的超時時間。應該注意這個超時一般不可能大於75秒。
proxy_read_timeout:
語法: proxy_read_timeout time;
默認值: proxy_read_timeout 60s;
上下文: http, server, location
定義從后端服務器讀取響應的超時。此超時是指相鄰兩次讀操作之間的最長時間間隔,而不是整個響應傳輸完成的最長時間。如果后端服務器在超時時間段內沒有傳輸任何數據,連接將被關閉。
proxy_next_upstream:
語法: 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 # 停止將請求發送給下一台后端服務器
需要理解一點的是,只有在沒有向客戶端發送任何數據以前,將請求轉給下一台后端服務器才是可行的。也就是說,如果在傳輸響應到客戶端時出現錯誤或者超時,這類錯誤是不可能恢復的。
upstream里參數講解:
max_fails=number
# 設定Nginx與服務器通信的嘗試失敗的次數。在fail_timeout參數定義的時間段內,如果失敗的次數達到此值,Nginx就認為服務器不可用。在下一個fail_timeout時間段,服務器不會再被嘗試。 失敗的嘗試次數默認是1。設為0就會停止統計嘗試次數,認為服務器是一直可用的。 你可以通過指令proxy_next_upstream、fastcgi_next_upstream和 memcached_next_upstream來配置什么是失敗的嘗試。 默認配置時,http_404狀態不被認為是失敗的嘗試。
fail_timeout=time
# 設定服務器被認為不可用的時間段以及統計失敗嘗試次數的時間段。在這段時間中,服務器失敗次數達到指定的嘗試次數,服務器就被認為不可用。默認情況下,該超時時間是10秒。
在實際應用當中,如果你后端應用是能夠快速重啟的應用,比如nginx的話,自帶的模塊是可以滿足需求的。但是需要注意。如果后端有不健康節點,負載均衡器依然會先把該請求轉發給該不健康節點,然后再轉發給別的節點,這樣就會浪費一次轉發。
可是,如果當后端應用重啟時,重啟操作需要很久才能完成的時候就會有可能拖死整個負載均衡器。此時,由於無法准確判斷節點健康狀態,導致請求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自帶的兩個和后端健康檢查相關的模塊。
測試:
配置文件:
upstream cluster_proxy { # simple round-robin server 172.16.204.130:8000 max_fails=2 fail_timeout=20s; server 172.16.204.130:8001 max_fails=2 fail_timeout=20s; } server { listen 80; server_name nginx.proxy.com; access_log logs/nginx_proxy_access_log main; location / { proxy_pass http://cluster_proxy; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; } }
客戶端curl測試:正常輪訓
172.16.204.1 - - [29/Nov/2017:12:40:04 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:40:05 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
172.16.204.1 - - [29/Nov/2017:12:40:06 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:40:07 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
掛掉一台機器后(每fail_timeout檢測一次,檢測到max_fails次失敗后,請求在fail_timeout時間內不會給這個機器):
172.16.204.1 - - [29/Nov/2017:12:44:41 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001, 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:44:42 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:44:43 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001, 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:44:44 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:44:45 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:12:44:46 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
恢復服務:正常輪訓
172.16.204.1 - - [29/Nov/2017:12:46:23 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:12:46:24 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001 172.16.204.1 - - [29/Nov/2017:12:46:25 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:12:46:26 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
二、nginx_upstream_check_module模塊
除了自帶的上述模塊,還有一個更專業的模塊,來專門提供負載均衡器內節點的健康檢查的。這個就是淘寶技術團隊開發的 nginx 模塊 nginx_upstream_check_module,通過它可以用來檢測后端 realserver 的健康狀態。如果后端 realserver 不可用,則所以的請求就不會轉發到該節點上。
在淘寶自己的 tengine 上是自帶了該模塊的,大家可以訪問淘寶tengine的官網來獲取該版本的nginx,官方地址:http://tengine.taobao.org/。
如果我們沒有使用淘寶的 tengine 的話,可以通過補丁的方式來添加該模塊到我們自己的 nginx 中。模塊官網:https://github.com/yaoweibin/nginx_upstream_check_module
安裝git
# yum -y install git
下載源碼
# git clone https://github.com/yaoweibin/nginx_upstream_check_module.git
下載nginx源碼,跟進自己版本而定:http://nginx.org/download/
打補丁
進入nginx目錄,需要注意,下載的nginx版本和補丁版本要嚴格對應,具體請參考官網解釋,例如:
If you use nginx-1.2.1 or nginx-1.3.0, the nginx upstream round robin module changed greatly. You should use the patch named 'check_1.2.1.patch'. If you use nginx-1.2.2+ or nginx-1.3.1+, It added the upstream least_conn module. You should use the patch named 'check_1.2.2+.patch'. If you use nginx-1.2.6+ or nginx-1.3.9+, It adjusted the round robin module. You should use the patch named 'check_1.2.6+.patch'. If you use nginx-1.5.12+, You should use the patch named 'check_1.5.12+.patch'. If you use nginx-1.7.2+, You should use the patch named 'check_1.7.2+.patch'.
我下載的1.12.2 就要用1.12.1+的,版本請對應好!(FAQ:其他版本例如1.11.2如遇到以下問題:)
[root@liukaitest2 nginx-1.11.2]# patch -p1 < /root/software/nginx_upstream_check_module/check_1.11.1+.patch
can't find file to patch at input line 4
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git src/http/modules/ngx_http_upstream_hash_module.c src/http/modules/ngx_http_upstream_hash_module.c
|--- src/http/modules/ngx_http_upstream_hash_module.c 2016-05-31 15:43:51.000000000 +0200
|+++ src/http/modules/ngx_http_upstream_hash_module.c 2016-06-22 17:20:19.553955295 +0200
--------------------------
File to patch:
請把patch -p1 改為 patch -p0
# cd nginx-1.12.2 # patch -p1 < ../nginx_upstream_check_module/check_1.12.1+.patch
編譯nginx:
# ./configure --prefix=/usr/local/nginx \ --user=nginx --group=nginx \ --with-http_ssl_module \ --with-http_realip_module \ --with-http_addition_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-http_sub_module \ --with-pcre \ --add-module=../nginx_upstream_check_module #### 這個是必選,指向下載的補丁解壓包。
測試:
配置文件:
upstream cluster { # simple round-robin server 172.16.204.130:8000; server 172.16.204.130:8001; check interval=1000 rise=1 fall=3 timeout=60000 type=http; } server { listen 80; server_name nginx.check.com; access_log logs/nginx_check_access_log main; location / { proxy_pass http://cluster; } location /status { check_status; access_log logs/nginx_check_status_access_log main; } }
詳細解釋:
上面配置的意思是,對cluster這個負載均衡條目中的所有節點,每1000ms檢測一次,請求1次正常則標記 realserver狀態為up,如果檢測 3 次都失敗,則標記 realserver的狀態為down,超時時間為60000ms,檢測類型:http。
官網解釋:
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。
check_status:健康檢查頁面,可以通過域名/status(自定義配置) 查看節點狀況。例如:
測試現象:正常輪訓
172.16.204.1 - - [29/Nov/2017:09:23:09 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:09:23:10 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
172.16.204.1 - - [29/Nov/2017:09:23:11 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:09:23:12 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
172.16.204.1 - - [29/Nov/2017:09:23:13 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
172.16.204.1 - - [29/Nov/2017:09:23:14 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
掛掉8001端口
測試現象:標記為down,正常切換,何時檢測為正常在標志位up
172.16.204.1 - - [29/Nov/2017:09:25:44 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:25:45 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001 172.16.204.1 - - [29/Nov/2017:09:25:46 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:25:47 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001, 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:25:48 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:25:49 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:25:51 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000
服務恢復后(經過定義好的rise次數之后):
172.16.204.1 - - [29/Nov/2017:09:53:44 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:53:45 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001 172.16.204.1 - - [29/Nov/2017:09:53:46 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8000 172.16.204.1 - - [29/Nov/2017:09:53:47 +0800] "GET / HTTP/1.1" 200 2072 "-" "curl/7.54.0" "-" 172.16.204.130:8001
在生產環境的實施應用中,需要注意的有 2 點:
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";
測試配置文件:
三、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版本以后都不支持了!因此,對於目前常見的生產環境上都不會去用了,這里僅僅留個紀念,給大家介紹下這個模塊!
具體的使用方法,這里可以貼出幾篇靠譜的博文地址以及官方地址:
http://wiki.nginx.org/HttpHealthcheckModule