Nginx 實現動態負載均衡(Nginx-1.10.1 + Consul v0.6.4)
一直也沒有找到合適的類似Socat + Haproxy 的組合能用在Nginx,後來發現了Nginx的幾個模塊,但是也存在各種不足. 而且Nginx 在大流量的情況下nginx -s reload 是有15% 以上的損耗,並且work線程要處理完以後纔會退出,並生成新的線程去處理連接. 作爲Ha轉發器是不是很蛋疼?最後覺得還是nginx_upsync_module 能夠通過命令行來平滑上下線主機的需求, 之後就來怎麼使用吧.
1 ) 幾種Nginx 上下線模塊說明:
## 1.1) 因爲nginx本身並沒有提供這些上下線API,需要openresty並配合一些第三方擴展來實現
Tengine 的Dyups模塊(ngx_http_dyups_module)。
新浪微博的Upsync+Consul 實現動態負載均衡。
OpenResty的balancer_by_lua(又拍雲使用其開源的slardar(Consul balancer_by_lua))。
ngx_http_dyups_module (https://github.com/yzprofile/ngx_http_dyups_module) # 提供了粗粒度的upstream管理方法,可以對整個upstream進行新增,刪除。
lua-upstream-nginx-module (https://github.com/openresty/lua-upstream-nginx-module) #則提供了細粒度的管理方式,可以對某一個服務IP進行管理,其中提供的set_peer_down方法,可以對upstream中的某個ip進行上下線。
ngx_dynamic_upstream (https://github.com/cubicdaiya/ngx_dynamic_upstream) #這些插件有一個共同點,那就是在不需要重啓nginx的基礎上, 動態修改nginx的配置.
最終決定使用 微博的 Nginx + Upsync + Consul 這個方案,主要考慮了配置持久化的問題和註冊中心掛了,是否影響生產的問題.正好可以達到效果所以最後決定採用這個方案.
## 1.2) Github上開源的 Lua + nginx 上下線的項目:
https://github.com/firstep/grayscale
2 ) 實驗環境 :
主機名 | 域名 | 端口 | 軟件 | 內網IP | 功能 | 系統 |
---|---|---|---|---|---|---|
bj-node-1 | con.linux08.com | 8500 | Consul_0.6.4 | 10.10.78.17 | 註冊中心 | Centos 7 x64 |
bj-master-1 | www.linux08.com | 80 | Nginx + upsync | 10.10.123.235 | nginx 分發 | Centos 7 x64 |
cli-1 | 空 | 80 | nginx web | 10.10.16.182 | web服務 | Centos 7 x64 |
cli-2 | 空 | 80 | nginx web | 10.10.185.201 | web服務 | Centos 7 x64 |
2.2) 環境說明:
2.2.1) 四臺主機要互相可以連通. cli-1和cli-2 安裝好nginx-1.10.1,並配置好首頁文件隨便寫點內容用來區別主機.
2.2.2) 提前做好防火牆策略,實驗就開放所有端口給辦公IP.(注意網絡安全尤其是Consul web 權限管理太弱了).
2.3) 使用模塊:
nginx-upsync-module # 與consul交換數據模塊,配合Consul組成nginx upstream 平滑上下線主機功能(新浪開發)。
nginx_upstream_check_module # 探測nginx upstream 組內主機並顯示Web的模塊(阿里開發).
2.4) 版本說明:
Nginx 對插件的版本要求很高, 其他的試了幾次都沒有把兩種軟件都加入到Nginx裏並編譯成功的. 最後看了幾個文章發現都是用下面的版本號來做的,
Nginx-1.10.1
Consul_0.6.4_linux_amd64
nginx_upstream_check_module # 這個裏面的補丁包都寫了版本號, 儘量按照教程來.本次使用(check_1.9.2+.patch )
3 ) 安裝Nginx 並添加模塊:
cd /data/src/
3.1 )下載nginx源碼:
wget http://nginx.org/download/nginx-1.10.1.tar.gz
3.2) 下載nginx_upstream_check_module 模塊:
git clone https://github.com/xiaokai-wang/nginx_upstream_check_module
3.2) 下載 nginx-upsync-module :
wget https://codeload.github.com/weibocom/nginx-upsync-module/tar.gz/v2.1.2
3.4) 解壓縮軟件:
tar -zxf nginx-1.10.1.tar.gz
tar -zxf v2.1.2
3.5) 安裝依賴包:
yum -y install libpcre3 libpcre3-dev ruby zlib1g-dev patch openssl openssl-devel pcre pcre-devel
3.6) 給Nginx 打補丁(nginx_upstream_check_module):
** 注意 ** 此補丁適用於nginx-1.10+ (一定要用這個版本):
cd /data/src/nginx-1.10.1/
patch -p0 < /data/src/nginx_upstream_check_module/check_1.9.2+.patch
如下顯示即打補丁成功:
[root@bj-master-1 nginx-1.10.1]# patch -p0 < /data/src/nginx_upstream_check_module/check_1.9.2+.patch
patching file src/http/modules/ngx_http_upstream_hash_module.c
patching file src/http/modules/ngx_http_upstream_ip_hash_module.c
patching file src/http/modules/ngx_http_upstream_least_conn_module.c
patching file src/http/ngx_http_upstream_round_robin.c
patching file src/http/ngx_http_upstream_round_robin.h
3.7)編譯安裝nginx:
groupadd -g 1001 work
useradd -u 1001 -g 1001 work
echo '123456' | passwd --stdin work
cd /data/src/nginx-1.10.1/
./configure --user=work --group=work --prefix=/data/work/nginx \
--with-http_ssl_module --with-pcre \
--with-http_stub_status_module --with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module --with-http_sub_module \
--add-module=/data/src/nginx_upstream_check_module \
--add-module=/data/src/nginx-upsync-module-2.1.2
make -j 2 && make install
** 注意 ** --add-module= 後邊跟着的都是補丁和模塊的源碼包路徑. 版本號補丁要寫正確,如果是新版本的話請自行覈對.
測試看Nginx 是否添加模塊成功:
[root@bj-master-1 sbin]# ./nginx -V
nginx version: nginx/1.10.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --user=work --group=work --prefix=/data/work/nginx --with-http_ssl_module --with-pcre --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-http_sub_module --add-module=/data/src/nginx_upstream_check_module --add-module=/data/src/nginx-upsync-module-2.1.2
4 ) 安裝Consul_0.6.4並啓動:
4.1) 下載Consul_0.6.4:
wget https://releases.hashicorp.com/consul/0.6.4/consul_0.6.4_linux_amd64.zip
4.2) 安裝並啓動Consul_0.6.4:
unzip consul_0.6.4_linux_amd64.zip
mkdir -p /data/soft/consul/data; mv consul /data/soft/consul/
cd /data/soft/consul
./consul agent -server -ui -bootstrap-expect=1 -syslog -bind=10.10.78.17 -client=0.0.0.0 -data-dir=/data/soft/consul/data -log-level=debug &
4.3) Condul 啓動腳本:
cd /data/soft/consul/
vim start.sh
#!/bin/bash
cd /data/soft/consul
nohup ./consul agent -server -ui -bootstrap-expect=1 -syslog -bind=10.10.78.17 -client=0.0.0.0 \
-data-dir=/data/soft/consul/data -log-level=debug &
chmod 755 start.sh
sh start.sh
4.5)查看Consul 啓動日誌:
tail -f -n nohup.log
2020/12/21 18:43:02 [INFO] raft: Node at 10.10.78.17:8300 [Follower] entering Follower state
2020/12/21 18:43:02 [INFO] serf: EventMemberJoin: bj-node-1 10.10.78.17
2020/12/21 18:43:02 [INFO] serf: EventMemberJoin: bj-node-1.dc1 10.10.78.17
2020/12/21 18:43:02 [INFO] consul: adding LAN server bj-node-1 (Addr: 10.10.78.17:8300) (DC: dc1)
2020/12/21 18:43:02 [INFO] consul: adding WAN server bj-node-1.dc1 (Addr: 10.10.78.17:8300) (DC: dc1)
2020/12/21 18:43:02 [ERR] agent: failed to sync remote state: No cluster leader
2020/12/21 18:43:03 [WARN] raft: Heartbeat timeout reached, starting election
2020/12/21 18:43:03 [INFO] raft: Node at 10.10.78.17:8300 [Candidate] entering Candidate state
2020/12/21 18:43:03 [DEBUG] raft: Votes needed: 1
2020/12/21 18:43:03 [DEBUG] raft: Vote granted from 10.10.78.17:8300. Tally: 1
2020/12/21 18:43:03 [INFO] raft: Election won. Tally: 1
2020/12/21 18:43:03 [INFO] raft: Node at 10.10.78.17:8300 [Leader] entering Leader state
2020/12/21 18:43:03 [INFO] consul: cluster leadership acquired
2020/12/21 18:43:03 [INFO] consul: New leader elected: bj-node-1
2020/12/21 18:43:03 [INFO] raft: Disabling EnableSingleNode (bootstrap)
2020/12/21 18:43:03 [DEBUG] raft: Node 10.10.78.17:8300 updated peer set (2): [10.10.78.17:8300]
2020/12/21 18:43:03 [DEBUG] raft: Node 10.10.78.17:8300 updated peer set (2): [10.10.78.17:8300]
2020/12/21 18:43:03 [DEBUG] consul: reset tombstone GC to index 6
2020/12/21 18:43:03 [DEBUG] agent: Service 'consul' in sync
2020/12/21 18:43:03 [INFO] agent: Synced node info
4.6) 訪問Consul web 管理界面(web 端口8500):
http://con.linux08.com:8500/ui/
5 ) 配置Nginx upstream 並聯動Consul:
## 5.1) 修改Nginx 配置文件:
vim /data/work/nginx/conf/nginx.conf
user work work;
worker_processes auto;
error_log /data/work/nginx/logs/error.log;
#pid logs/nginx.pid;
worker_rlimit_nofile 60000;
events {
use epoll;
worker_connections 60000;
}
http {
include mime.types;
default_type application/octet-stream;
charset utf-8;
log_format main '$remote_addr - $remote_user [$time_local]$upstream_addr-$upstream_status-$request_time'
'-$upstream_response_time-$bytes_sent-$gzip_ratio "$host$request_uri" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format upstream '$time_iso8601 $http_x_forwarded_for $host $upstream_response_time $request $status $upstream_addr';
access_log /data/logs/nginx/access.log main;
types_hash_max_size 2048;
sendfile on;
.......... 此處省略部分配置..........
######################### Server ##############################
upstream con_server { # upstream 名字 很重要,consul Key 最好以這個命名.
server 127.0.0.1:11111; # 這行就是一個佔位的, 沒有它無法啓動.
upsync 10.10.78.17:8500/v1/kv/upstreams/con_server/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
upsync_dump_path /data/work/nginx/conf/servers/con_server.conf; # 將註冊中心(Condul)的內容持久化到本地,下面的要和這個保持一致.
include /data/work/nginx/conf/servers/con_server.conf; # 引入持久化的配置文件,即使註冊中心掛了服務依然可以運行. 目錄和文件必須存在才能正常啓動獲取註冊中心的信息.
check interval=5000 rise=1 fall=3 timeout=4000 type=http port=80; # upstream check 模塊的必要選項,有了這些web status 才能顯示.
}
server {
listen 80;
server_name www.linux08.com;
location / {
proxy_next_upstream http_404 http_500 http_502 http_503 http_504 error timeout invalid_header;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
proxy_pass http://con_server/;
}
}
server {
listen 80;
server_name status.linux08.com;
location / {
check_status;
# allow 0.0.0.0;
# deny all;
auth_basic "login";
auth_basic_user_file /data/work/nginx/conf/.htpasswd;
}
}
}
## 5.2)創建備份配置文件目錄並創建備份配置文件:
mkdir -p /data/work/nginx/conf/servers/ # 創建備份配置文件目錄 (很重要啓動前必須存在)
touch /data/work/nginx/conf/servers/con_server.conf # 創建備份配置文件,文件名就是server裏面配置 (很重要啓動前必須存在)
mkdir -p /data/logs/nginx/ # 創建日誌文件目錄
## 5.3)狀態頁驗證創建:
yum install httpd-tools -y
htpasswd -bcm /data/work/nginx/conf/.htpasswd root 123456 # 最後2位是 賬號和密碼
[root@bj-master-1 conf]# htpasswd -bcm /data/work/nginx/conf/.htpasswd root 123456
Adding password for user root
## 5.4) 啓動nginx:
/data/work/nginx/sbin/nginx -t
/data/work/nginx/sbin/nginx
## 5.5)向Consul server () 添加主機:
curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":3}' 10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.16.182:80
curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":3}' 10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.185.201:80
[root@bj-master-1 nginx]# curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":3}' 10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.16.182:80
true
## 5.6) 下線Consul server 主機:
curl -X DELETE http://10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.16.182:80
curl -X DELETE http://10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.185.201:80
** 注意: 每個組最好不要所有主機下線,否則無法提供服務.
[root@bj-master-1 nginx]# curl -X DELETE http://10.10.78.17:8500/v1/kv/upstreams/con_server/10.10.16.182:80
true ## 提交的信息沒問題,命令會返回true. 反覆提交也不報錯, 只會覆蓋.
## 5.7) 命令行獲取結果:
curl -s http://10.10.78.17:8500/v1/kv/upstreams/con_server/?recurse
[root@bj-master-1 nginx]# curl -s http://10.10.78.17:8500/v1/kv/upstreams/con_server/?recurse
[{"LockIndex":0,"Key":"upstreams/con_server/10.10.16.182:80","Flags":0,"Value":"eyJ3ZWlnaHQiOjEsICJtYXhfZmFpbHMiOjIsICJmYWlsX3RpbWVvdXQiOjN9","CreateIndex":9616,"ModifyIndex":9623},{"LockIndex":0,"Key":"upstreams/con_server/10.10.185.201:80","Flags":0,"Value":"eyJ3ZWlnaHQiOjEsICJtYXhfZmFpbHMiOjIsICJmYWlsX3RpbWVvdXQiOjN9","CreateIndex":5311,"ModifyIndex":5311}][root@bj-master-1 nginx]#
## 5.8) 測試結果:
訪問 www.linux08.com 內容會在 web1 和 web2 之間切換, 用命令下線一臺主機,再次刷新 只能顯示一臺在線.同時在status.linux08.com 頁面上查看信息
6 ) Nginx status 介紹:
## 6.1 ) status 頁面內容介紹:
server number # 後端服務器的數量
generation # Nginx reload的次數
Index # 服務器的索引
Upstream # 在配置中upstream的名稱
Name # 服務器IP
Status # 服務器的狀態
Rise # 服務器連續檢查成功的次數
Fall # 連續檢查失敗的次數
Check type # 檢查的方式
Check port # 後端專門爲健康檢查設置的端口
7 ) Nginx check status 配置參數介紹:
## 7.1 ) Nginx 配置文件內容
......
check interval=5000 rise=1 fall=3 timeout=4000 type=http port=80;
#每隔5秒檢查後端真實節點狀態,成功1次爲up狀態,失敗3次爲down狀態,超時時間爲4秒,檢查類型爲http
check_http_send "HEAD / HTTP/1.0\r\n\r\n"; # 該指令可以讓負載均衡器模擬向後端realserver發送,監控檢測的http包,模擬LVS的檢測。
check_http_expect_alive http_2xx http_3xx; # 返回指定HTTP code,符合預期就算檢測成功
# 返回2xx,3xx狀態碼爲正常狀態,其它狀態碼爲down狀態
......
## 7.2 ) 配置參數語法介紹:
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
check interval 指令可以打開後端服務器的健康檢查功能, 指令後面的參數意義是:
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: # 指定後端服務器的檢查端口。
check_http_send: # 該指令可以讓負載均衡器模擬向後端realserver發送,監控檢測的http包,模擬LVS的檢測。
check_http_expect_alive: # 返回2xx,3xx狀態碼爲正常狀態,其它狀態碼爲down狀態.