1. 程式人生 > >基於Upsync模組實現Nginx動態配置

基於Upsync模組實現Nginx動態配置

Nginx

題圖:By Federico Respini From Unsplash

Upsync是新浪微博開源的基於Nginx實現動態配置的三方模組。Nginx-Upsync-Module的功能是拉取Consul的後端server的列表,並動態更新Nginx的路由資訊。此模組不依賴於任何第三方模組。Consul作為Nginx的DB,利用Consul的KV服務,每個Nginx Work程序獨立的去拉取各個upstream的配置,並更新各自的路由。

Upsync模組工作原理

在Nginx的設計中,每一個upstream維護了一張靜態路由表,儲存了backend的ip、port以及其他的meta資訊。每次請求到達後,會依據location檢索路由表,然後依據具體的排程演算法(如round robin )選擇一個backend轉發請求。但這張路由表是靜態的,如果變更後,則必須reload,經常reload的話這對SLA有較大影響。

為了達到減少reload的目的,大多通過動態更新維護路由表來解決這個問題。通常路由表的維護有Push與Pull兩種方式。

  • Push方案

通過Nginx API向Nginx發出請求,操作簡單、便利。

架構圖如下:

Nginx架構

http api除了操作簡單、方便,而且實時性好;缺點是有多臺Nginx時,不同Nginx路由表的一致性難於保證,如果某一條註冊失敗,便會造成服務配置的不一致,容錯複雜。另外擴容Nginx伺服器,需要從其他的Nginx中同步路由表。

  • Pull方案

路由表中所有的backend資訊(含meta)儲存到Consul,所有的Nginx從Consul拉取相關資訊。有變更則更新路由表,利用Consul解決一致性問題,同時利用Consul的wait機制解決實時性問題。利用Consul的index進行增量摘取,解決頻寬佔用問題。

在Consul中,一個K/V對代表一個backend資訊,增加一個即視作擴容,減少一個即為縮容。調整meta資訊,如權重,也可以達到動態流量調整的目的。

架構圖如下:

Consul

  • 基於動態路由的方案實現

Upsync模組使用了第二種模式,通過拉取Consul的後端Server的列表,並動態更新Nginx的路由資訊。Upsync模組工作流程圖如下:

Upsync

每個Work程序定時的去Consul拉取相應upstream的配置,若Consul發現對應upstream的值沒有變化,便會hang住這個請求五分鐘(預設值)。在這五分鐘內對此upstream的任何操作,都會立刻返回給Nginx對相應路由進行更新。

upstream變更後,除了更新Nginx的快取路由資訊,還會把本upstream的後端server列表dump到本地,保持本地server資訊與consul的一致性。

除了註冊/登出後端的server到consul,會更新到Nginx的upstream路由資訊外,對後端server屬性的修改也會同步到nginx的upstream路由。

Upsync模組支援修改的屬性有:weight、max_fails、fail_timeout、down。

  1. 修改server的權重可以動態的調整後端的流量。
  2. 若想要臨時移除server,可以把server的down屬性置為1。
  3. 若要恢復流量,可重新把down置為0。

每個work程序各自拉取、更新各自的路由表,採用這種方式的原因:

  1. 基於Nginx的程序模型,彼此間資料獨立、互不干擾。
  2. 若採用共享記憶體,需要提前預分配,靈活性可能受限制,而且還需要讀寫鎖,對效能可能存在潛在的影響。
  3. 若採用共享記憶體,程序間協調去拉取配置,會增加它的複雜性,拉取的穩定性也會受到影響。

Upsync模組高可用性

Nginx的後端列表更新依賴於Consul,但是不強依賴於它,具體表現為:

  1. 即使中途Consul意外掛了,也不會影響Nginx的服務,Nginx會沿用最後一次更新的服務列表繼續提供服務。
  2. 若Consul重新啟動提供服務,這個時候Nginx會繼續去Consul探測,這個時候Consul的後端服務列表發生了變化,也會及時的更新到Nginx。
  3. work程序每次更新都會把後端列表dump到本地,目的是降低對Consul的依賴性,即使在consul不可用時,也可以Reload Nginx。

Nginx啟動流程圖如下:

Nginx流程

Nginx啟動時,master程序首先會解析本地的配置檔案,解析完成功,接著進行一系列的初始化,之後便會開始work程序的初始化。work初始化時會去Consul拉取配置,進行work程序upstream路由資訊的更新,若拉取成功,便直接更新,若拉取失敗,便會開啟配置的dump後端列表的檔案,提取之前dump下來的server資訊,進行upstream路由的更新,之後便開始正常的提供服務。

每次去拉取Consul都會設定連線超時,由於Consul在無更新的情況下預設會hang五分鐘,所以響應超時配置時間應大於五分鐘。大於五分鐘之後,Consul依舊沒有返回,便直接做超時處理。

Upsync模組安裝

安裝Consul

Consul是HashiCorp公司推出的開源工具,用於實現分散式系統的服務發現與配置。

Upsync最新版本是支援ectd,這裡用ectd做為後端儲存。有關consule的講解後面單獨來講。如果你還沒有etcd環境,可參考「etcd使用入門」一文搭建一個。

安裝配置Nginx

  • 下載對應軟體包

這裡使用的是Upsync最新版本,目前支援Nginx 1.9+。nginx-upstream-check-module是Tengine中的模組,主要用於upstream的健康檢查。由於nginx-upstream-check-module最新版本只支援1.9.2,所以這裡Nginx選用1.9.2。

$ cd /root

$ wget ‘http://nginx.org/download/nginx-1.9.2.tar.gz’

$ git clone https://github.com/weibocom/nginx-upsync-module

$ git clone https://github.com/xiaokai-wang/nginx_upstream_check_module

  • 編譯安裝Nginx

$ apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl make build-essential

$ tar xzvf nginx-1.9.2.tar.gz

$ cd nginx-1.9.2

$ patch -p0 < ~/nginx_upstream_check_module/check_1.9.2+.patch

$ ./configure –add-module=/root/nginx_upstream_check_module –add-module=/root/nginx-upsync-module

$ make && make install

  • 配置Nginx

Nginx會預設安裝到/usr/local/nginx目錄下。

建立使用者和相應目錄

$ useradd -M nginx -s /sbin/nologin

$ mkdir -p /var/log/nginx

$ chown -R nginx.nginx /var/log/nginx

$ mkdir /usr/local/nginx/conf/conf.d

$ mkdir -p /usr/local/nginx/conf/servers

修改Nginx主配置檔案

# 備份原配置檔案 $ cd /usr/local/nginx $ mv conf/nginx.conf conf/nginx.conf.bak # 修改配置 $ vim /usr/local/nginx/conf/nginx.conf user  nginx; worker_processes  5; error_log  /var/log/nginx/error.log warn; pid        /var/run/nginx.pid; events {    worker_connections  1024; } http {    include       /usr/local/nginx/conf/mime.types;    default_type  application/octet-stream;    log_format  main  ‘$remote_addr – $remote_user [$time_local] “$request” ‘                      ‘$status $body_bytes_sent “$http_referer” ‘                      ‘”$http_user_agent” “$http_x_forwarded_for”‘;    access_log  /var/log/nginx/access.log  main;    sendfile        on;    #tcp_nopush     on;    keepalive_timeout  65;    #gzip  on;    include /usr/local/nginx/conf/conf.d/*.conf; }

建立站點配置檔案

$ vim /usr/local/nginx/conf/conf.d/site.conf upstream test {  # fake server otherwise ngx_http_upstream will report error when startup  server 127.0.0.1:11111;  # all backend server will pull from consul when startup and will delete fake server  # 後端使用consul儲存  # upsync 192.168.2.210:8500/v1/kv/upstreams/test upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;  # 後端使用etcd儲存  upsync 192.168.2.210:2379/v2/keys/upstreams/test upsync_timeout=6m  upsync_interval=500ms upsync_type=etcd strong_dependency=off;  upsync_dump_path /usr/local/nginx/conf/servers/servers_test.conf;  #配置健康檢查  check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;  check_http_send “HEAD / HTTP/1.0\r\n\r\n”;  check_http_expect_alive http_2xx http_3xx; } upstream bar {  server 192.168.2.210:8080 weight=1 fail_timeout=10 max_fails=3; } server {  listen 80;  location = / {    proxy_pass http://test;  }  location = /bar {      proxy_pass http://bar;  }  location = /upstream_show {    upstream_show;  }  location = /upstream_status {    check_status;    access_log off;  } }

  • 建立Nginx Systemd服務

$ vi /lib/systemd/system/nginx.service [Unit] Description=The NGINX HTTP and reverse proxy server Documentation=http://nginx.org/en/docs/ After=syslog.target network.target remote-fs.target nss-lookup.target [Service] Type=forking PIDFile=/var/run/nginx.pid ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target

修改許可權及加入開機啟動

$ sudo chmod +x /lib/systemd/system/nginx.service

$ sudo systemctl enable nginx.service

現在可以使用下面的指令來管理Nginx服務。

$ systemctl start nginx.service

$ systemctl reload nginx.service

$ systemctl restart nginx.service

$ systemctl stop nginx.service

  • 啟動Nginx

$ systemctl start nginx.service

驗證Nginx服務是否正常

$ systemctl status  nginx.service ● nginx.service – The NGINX HTTP and reverse proxy server   Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)   Active: active (running) since Mon 2017-05-08 10:58:06 CST; 1min 6s ago     Docs: http://nginx.org/en/docs/  Process: 22966 ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf (code=exited, status=0/SUCCESS)  Process: 22963 ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf (code=exited, status=0/SUCCESS) Main PID: 22971 (nginx)    Tasks: 6   Memory: 24.5M      CPU: 517ms   CGroup: /system.slice/nginx.service           ├─22971 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.con           ├─22972 nginx: worker process           ├─22973 nginx: worker process           ├─22974 nginx: worker process           ├─22975 nginx: worker process           └─22976 nginx: worker process May 08 10:58:06 dev-master-01 systemd[1]: Starting The NGINX HTTP and reverse proxy server… May 08 10:58:06 dev-master-01 nginx[22963]: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok May 08 10:58:06 dev-master-01 nginx[22963]: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful May 08 10:58:06 dev-master-01 systemd[1]: Started The NGINX HTTP and reverse proxy server.

如果要單獨檢視Nginx服務日誌,可以使用以下命令:

$ journalctl -f -u nginx.service

驗證Upsync模組

啟動一個Web服務

這裡在Nginx預設站點目錄,用Python啟動一個SimpleHTTPServer進行驗證。

$ cd /usr/local/nginx/html

$ python -m SimpleHTTPServer 8080

後端儲存增加資料

  • 後端儲存使用etcd

增加一個後端伺服器

$ curl -X PUT http://192.168.2.210:2379/v2/keys/upstreams/test/192.168.2.210:8080

其它Upstream模組中其它屬性的預設值為:weight=1 max_fails=2 fail_timeout=10 down=0 backup=0;。如果你要調整這些值,可以用以下命令格式進行提交:

$ curl -X PUT -d value=”{\”weight\”:1, \”max_fails\”:2, \”fail_timeout\”:10}” http://

$etcd_ip:$port/v2/keys/$dir1/$upstream_name/$backend_ip:$backend_port

刪除一個後端伺服器

$ curl -X DELETE http://192.168.2.210:2379/v2/keys/upstreams/test/192.168.2.210:8080

  • 後端儲存使用Consul

增加一個後端伺服器

$ curl -X PUT http://192.168.2.210:8500/v1/kv/upstreams/test/192.168.2.210:8080

刪除一個後端伺服器

$ curl -X DELETE http://192.168.2.210:8500/v1/kv/upstreams/test/192.168.2.210:8080

測試站點是否正常訪問

  • 訪問站點

頁面自動根據proxy_pass http://test;成功轉到了後端伺服器。

  • 訪問upstream列表

  • 訪問upstream狀態

  • 檢視新增的後端服務是否被dump到本地

$ cat /usr/local/nginx/conf/servers/servers_test.conf server 192.168.2.210:8080 weight=1 max_fails=2 fail_timeout=10s;

文章來自微信公眾號:運維之美