本文主要介紹了適用於nginx的zk動態後端發現模塊(nginx-upstream-reloader)及其使用方法。
上篇文章回顧: SOAR 101 快速入門指南
不少公司都有作動態調度系統,有些是基於 mesos+docker,有些採用了 google 的 K8s,或者是自研的系統,這些系統有一個明顯的特徵就是服務實例的ip會頻繁更換。這種容器化的部署方式和傳統的服務部署形式不同,原有的服務都是部署在某些物理機或者雲主機上,這些物理機或者雲主機的 ip 地址不會輕易更換,這樣咱們配置 nginx 作流量轉發的時候就能夠直接寫ip。可是切換到這些容器化的系統後,服務的實例重啓頻繁,每一次重啓後實例的 ip 就會發生變化,這樣咱們再用手動配置、變動後端 ip 的形式來作 nginx 的流量轉發就基本上不可行了。這時咱們須要想辦法讓發佈後的實例ip自動更新到 nginx 的配置中去,而且可以讓其自動生效。本模塊正是基於前面的應用場景,用於解決後端實例 ip 頻繁變化,沒法將更新實時同步至 nginx 的配置中的問題。node
前期經過調研發現,有些公司採用了 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 不可用時執行的邏輯。python
在咱們的模塊有用到 dyups 這個 nginx 模塊,dyups 模塊是一個可以直接更新正在運行的 nginx 的 upstream 列表而不須要從新 reload nginx 配置的模塊。這個模塊經過開放一個接口,而後外部經過這個接口發起 post 或者 get 請求,直接更新或者獲取對應 upstream 的後端列表。更加詳細的用法能夠瀏覽網址nginx
本模塊結合了咱們公司常見業務的應用場景、平常使用中碰到的問題以及 dyups 的不足之處,主要實現了以下幾個功能:git
1)獲取註冊到zk中後端列表,並對獲取到的列表數據格式化,保存到相應的 nginx 配置文件中,進行持久化github
2)將保存到文件的後端服務器列表經過dyups模塊的接口寫入到 nginx upstream 模塊的共享內存中,動態更新 upstream 裏面的後端列表web
3)當 zk 故障時,本模塊將再也不更新 nginx 的共享內存和本地 nginx 配置文件,使 nginx 的 upstream 配置保持在 zk 故障前的狀態docker
4)支持讀取多個 zk 集羣的多個 zk 節點配置json
基礎依賴:後端
支持 dyups 模塊的 nginxcentos
python 2.6/2.7
這裏咱們將模塊代碼放到/home/work 目錄下
cd /home/work
git clone http://v9.git.n.xiaomi.com/liuliqiu/nginx-upstream-reloader.git複製代碼
cd nginx-upstream-reloader
bash install_venv.sh複製代碼
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 ...;
}複製代碼
前面已經配置了 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 {
listen 80;
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;
}
}複製代碼
這裏添加一個單獨的 server,監聽本地地址的14443端口
server{
listen 127.0.0.1:14443;
server_name _;
location / {
dyups_interface;
}
}複製代碼
這裏須要先執行 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 上測試經過,適用於容器和物理機。
本文首發於公衆號「小米運維」,點擊查看原文