Nginx動態發現方案與實踐
本文主要介紹了適用於nginx的zk動態後端發現模組(nginx-upstream-reloader)及其使用方法。
上篇文章回顧: ofollow,noindex">SOAR 101 快速入門指南
一、背景
很多公司都有做動態排程系統,有些是基於mesos+docker,有些採用了google的K8s,或者是自研的系統,這些系統有一個明顯的特徵就是服務例項的ip會頻繁更換。這種容器化的部署方式和傳統的服務部署形式不一樣,原有的服務都是部署在某些物理機或者雲主機上,這些物理機或者雲主機的ip地址不會輕易更換,這樣我們配置nginx做流量轉發的時候就可以直接寫ip。但是切換到這些容器化的系統後,服務的例項重啟頻繁,每一次重啟後例項的ip就會發生變化,這樣我們再用手動配置、變更後端ip的形式來做nginx的流量轉發就基本上不可行了。這時我們需要想辦法讓釋出後的例項ip自動更新到nginx的配置中去,並且能夠讓其自動生效。本模組正是基於前面的應用場景,用於解決後端例項ip頻繁變化,無法將更新實時同步至nginx的配置中的問題。
二、模組架構
前期通過調研發現,有些公司採用了etcd/consul+nginx第三方模組(nginx-upsync-module)的方式來實現nginx零重啟更新upstream的操作。我們內部並沒有採用etcd或者consul來儲存後端例項配置,而是廣泛採用了zk服務來儲存後端的配置。大部分業務都會將例項ip註冊到zk中去,所以我們的nginx需要從zk中拉取後端例項ip和埠。我們公司內部也有同學開發了nginx連線zk的模組,但是該模組是通過每個worker程序去連線zk,一個nginx可能有多個甚至幾十個worker程序,會造成zk的連線數突增,給zk叢集帶來很大的壓力。後續通過調研發現了dyups這個模組,然後通過自己編碼實現連線zk,從zk中拉取配置,再通過dyups模組的介面更新到upstream的共享記憶體,也可以實現零重啟更新nginx的upstream列表。同時通過自己編碼實現和zk互動的邏輯,也可以控制在zk不可用時執行的邏輯。
在我們的模組有用到dyups這個nginx模組,dyups模組是一個能夠直接更新正在執行的nginx的upstream列表而不需要重新reload nginx配置的模組。這個模組通過開放一個介面,然後外部通過這個介面發起post或者get請求,直接更新或者獲取對應upstream的後端列表。更加詳細的用法可以瀏覽網址
https://github.com/yzprofile/ngx_http_dyups_module
檢視這個模組的github介紹。但是由於dyups模組只能修改nginx的共享記憶體,不能持久化當前的upstream配置到檔案中,所以我們的模組另外一個核心的工作就是持久化upstream配置到配置檔案中。
三、模組功能
本模組結合了我們公司常見業務的應用場景、日常使用中碰到的問題以及dyups的不足之處,主要實現瞭如下幾個功能:
1)獲取註冊到zk中後端列表,並對獲取到的列表資料格式化,儲存到相應的nginx配置檔案中,進行持久化
2)將儲存到檔案的後端伺服器列表通過dyups模組的介面寫入到nginx upstream模組的共享記憶體中,動態更新upstream裡面的後端列表
3)當zk故障時,本模組將不再更新nginx的共享記憶體和本地nginx配置檔案,使nginx的upstream配置保持在zk故障前的狀態
4)支援讀取多個zk叢集的多個zk節點配置
四、模組工作流程
五、模組的使用
基礎依賴:
支援dyups模組的nginx
python 2.6/2.7
1、clone模組程式碼到nginx機器上
這裡我們將模組程式碼放到/home/work目錄下
cd /home/work git clone http://v9.git.n.xiaomi.com/liuliqiu/nginx-upstream-reloader.git複製程式碼
2、執行模組原始碼目錄中依賴安裝指令碼(nginx-upstream-reloader/install_venv.sh),這個指令碼主要用於安裝virtualvenv環境和依賴的第三方模組
cd nginx-upstream-reloader bash install_venv.sh複製程式碼
3、修改nginx-upstream-reloader模組配置檔案
venv環境安裝完成後,修改nginx-upstream-reloader/conf目錄下的upstream_zk_nodes.conf配置檔案,這個配置檔案用於定義後端例項所在的目的zk叢集和zk節點以及對應nginx upstream的名字,具體的修改方法分為如下兩種情況:
1)多個後端服務註冊在一個zk叢集,按照如下配置
upstream_zk_nodes.conf zk_servers:zk-hadoop-test01:11000,zk-hadoop-test02:11000 zk_nodes: bonus-api: /web_services/com.miui.bonus.api.resin-web複製程式碼
zk_servers:後端服務註冊的zk叢集地址和埠
zk_nodes:upstream_name:後端服務註冊的zk節點路徑
當我們啟動後,模組會拉取指定zk節點路徑下的後端列表資訊自動生成upstream_name.upstream檔案,如上述配置,我們在指定的目錄下(這個指定的目錄可以在nginx-upstream-reloader/conf/main.conf配置檔案的files_output_path選項控制,這裡我們將該選項為/home/work/nginx/site-enable)/home/work/nginx/site-enable下會生成一個bonus-api.upstream檔案,檔案的內容會如下:
upstream bonus-api { server ....; server ....; }複製程式碼
2)多個後端伺服器註冊在不同的zk叢集
upstream_zk_nodes.conf - zk_servers: tjwqstaging.zk.hadoop.srv:11000 zk_nodes: ocean-helloworld-upstream1: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi ocean-helloworld-upstream2: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi - zk_servers: tjwqstaging.zk.hadoop.srv:11000 zk_nodes: ocean-helloworld-upstream3: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi複製程式碼
zk_servers:後端服務註冊的zk叢集地址和埠
zk_nodes upstream_name:後端服務註冊的zk節點路徑
有同學跟我反饋為什麼要用yaml格式的配置檔案,而不用json格式的配置檔案,json對格式要求沒有yaml嚴格,但是yaml的配置檔案看起層級直觀多了。
當我們啟動該模組後,模組會拉取指定zk節點路徑下的後端列表資訊自動生成upstream_name.upstream檔案,如上述配置,模組在/home/work/nginx/site-enable會生成一個ocean-helloworld-upstream1.upstream、ocean-helloworld-upstream2.upstream、ocean-helloworld-upstream3.upstream三個檔案,檔案的內容會分別如下:
upstream ocean-helloworld-upstream1 { server ...; server ...; } upstream ocean-helloworld-upstream2 { server ...; server ...; } upstream ocean-helloworld-upstream3 { server ...; server ...; }複製程式碼
4、修改nginx配置檔案
前面已經配置了nginx-upstream-reloader模組連線zk節點獲取後端配置後,自動生成upstream配置檔案,所以我們需要在nginx中include這些upstream配置檔案在server塊中才可以使用這些upstream。目前由於dyups模組的限制,需要將upstream_name設定為一個變數,然後在proxy_pass指令中使用這個變數配置轉發,具體可以參考下面的配置:
include /home/work/nginx/site-enable/ocean-helloword-upstream1.upstream; include /home/work/nginx/site-enable/ocean-helloword-upstream2.upstream; include /home/work/nginx/site-enable/ocean-helloword-upstream3.upstream; server { listen80; location /helloworld1 { set $ups1 ocean-helloword-upstream1; proxy_pass http://$ups; } location /helloworld2 { set $ups2 ocean-helloword-upstream2; proxy_pass http://$ups2; } location /helloworld3 { set $ups3 ocean-helloword-upstream3; proxy_pass http://$ups3; } }複製程式碼
5、修改nginx配置檔案,開啟dyups介面
這裡新增一個單獨的server,監聽本地地址的14443埠
server{ listen 127.0.0.1:14443; server_name _; location / { dyups_interface; } }複製程式碼
6、啟動zk動態發現模組和nginx
這裡需要先執行nginx-upstream-reloader/start.sh檔案,啟動nginx-upstream-reloader模組,然後再啟動nginx,因為當我們還沒有啟動upstream-reloader模組時,upstream配置檔案還未生成,但我們nginx配置檔案中已經include這些upstream配置檔案,這時啟動nginx就會報錯
bash nginx-upstream-reloader/start.sh /home/work/nginx/sbin/nginx #這裡假設我們的nginx安裝在/home/work/nginx/目錄下複製程式碼
六、相容性
目前該模組已經在centos6和centos7上測試通過,適用於容器和物理機。
本文首發於公眾號“小米運維”,點選檢視原文