京東Nginx平臺化實踐
Nginx是優秀的HTTP和反向代理服務器,京東各部門都在廣泛使用,但普遍都面臨著一些問題:
-
配置復雜,專業性強。
-
配置文件無法批量修改且配置變更依賴重啟操作。
-
不同應用依賴不同模塊、配置項,管理混亂。
-
同一應用的Nginx無法批量、快速擴容。
所有問題的根源在于Nginx是一個單機系統,雖然模塊化、高性能,但在互聯網高速發展的今天,像京東這樣擁有大規模Nginx、業務集群的場景下,所有問題都有可能被無限放大,針對這種現狀我們設計研發了JEN(JD EXTENDED NGINX),截止目前JEN已覆蓋京東金融大部分核心業務,如奪寶吧,卡超市,白條等。
一、整體結構
圖1:JEN結構圖
如上圖,運維通過Web控制臺做相應的配置操作,若是分流、限流等配置,則信息入庫等待Nginx通過Restful API同步規則后開始生效;若是平滑升級、重啟等強運維性操作,則Web控制臺通過控制Ansible對Nginx進行相應操作。
圖2:Nginx和Web控制臺多機房部署圖
JEN特點:
- 支持Nginx自動發現,分組管理,狀態監控。
- 統一入口,通過抽象配置,簡化操作管控Nginx集群生命周期,并支持規則批量配置,操作批量執行。
- 擴展了原生Nginx的分流、限流功能,支持規則的內存實時同步,無需修改配置文件,更無需重啟Nginx進程。
1.基礎信息
Web上所有的展示和操作全部基于對基礎信息的計算整合 ,主要包含兩類:
- 分組信息(業務線、應用、機房、Nginx IP)
- Nginx屬性,例如upstream信息,server_name,listen_port等,主要來源Nginx讀取Nginx.conf內容后的信息上報(心跳)
對于分組信息,JEN支持以下兩種方式填充:
- 調用外部服務的Restful API導入完整的基礎信息。
- 對自動發現的Nginx做分組的手工編輯。
圖3:各分組間關系圖
如上圖,分組包括業務線、應用、機房、Nginx共四層關系,在大規模集群環境下可以通過這種關系并結合Nginx屬性,支持對所有操作的批量執行,如批量修改配置文件,批量升級重啟等,解放生產力。
2.規則獲取
用戶在Web控制臺配置后,在Nginx端我們實現了全異步的模塊支持定時向Web獲取屬于當前Nginx的規則信息,規則存儲內存,即時生效,其中:
a)規則信息每個進程存儲一份,避免進程間資源共享導致鎖競爭。
b)版本號設計,保證規則和心跳的絕對順序,不因丟包、延遲等網絡因素導致版本錯亂,而且在規則未變更時Nginx無需頻繁解析大量規則信息而消耗CPU資源。
3.安全
JEN支持三類角色,每種角色支持不同的操作權限(默認是普通用戶角色,無寫權限),任何角色對Web的任何操作都會被記錄,并在Web提供了入口支持多維度操作日志查詢,便于審計
4.監控
我們實現了更為全面的監控信息采集與展示,包括:
a)擴展了tengine的主動探測模塊,支持上游服務器的平均、當前延時統計。
b)通過與Web的心跳保持支持Nginx存活狀態監控。
c)支持TCP連接信息,in/out流量,QPS,1xx到5xx回應報文等信息監控。
以上的監控信息支持分組統計(業務線、應用、機房)和大屏展示,便于相關人員(業務,運維)實時監控應用狀態。
二、分流
概念:根據請求特征(IP,header中任意關鍵字)支持把某些特定請求分流到單個或多個上游服務器中,如下圖:
圖4:分流示例圖
分流主要適用灰度發布,ab testing等場景,另外我們也對分流功能做了擴展,支持Web控制臺一鍵啟停上游服務器,便于當應用服務器需要維護或升級時,用戶請求正常訪問。
三、限流
京東618等大促,貨物都提前堆積在購物車,等待零點秒殺,換成工程師的語言來說,就是前一秒的QPS很低,但是下一秒QPS非常高,流量大意味著機器負載高,若一個應用的一兩臺機器沒有扛住,這樣就會導致整個應用集群雪崩。
限流不可盲目,首先需要根據業務特點選擇合適的限流算法(漏桶算法、令牌桶算法),其次需要結合歷史流量、應用服務能力、營銷力度等因素綜合評定限流參數,最后決定以何種優雅的方式反饋用戶。
Nginx在實現上通過共享內存共享限流中間信息的方式來達到多進程間的狀態統一。在JEN設計初衷,原本計劃和分流一致,即每個進程存儲一份限流規則,限流只在當前進程內限流,但不可避免的會出現如下問題:
- 每個進程“你限你的,我限我的”,信息不一致進而導致限流不準確。
- 類似用戶ID的限流,在京東這樣擁有龐大日活用戶的場景下,每個進程需要開辟足夠大的內存才能避免限流算法中對于紅黑樹節點的頻繁置換,這樣一來Nginx占用內存就會隨著進程數成倍擴大。
我們的做法:
- 預分配共享內存,Nginx獲取到限流規則時動態適配一塊共享內存。
- 規則共享,生效后實時同步至所有進程,規則鏈保證所有舊版本規則只有在當前流量更新之后才會刪除,如下圖:
圖5:規則鏈
我們在限流功能上的幾點擴展:
-
支持錯誤頁定制,除了返回Nginx靜態頁,還支持302錯誤頁重定向,根據在Web控制臺的配置可以重定向到任何外部鏈接,但302重定向存在一個問題:用戶瀏覽器的URL和內容都發生了變更,意味著用戶需要重新輸入URL重新請求或者是重復之前的操作步驟,用戶體驗差可能導致用戶放棄此次購買行為而轉投它家。在邏輯上我們通過Nginx的subrequest機制支持返回內容發生變更而URL保持不變,這樣一來每當用戶被限流,只需重新刷新頁面即可重復之前的操作步驟。
圖6:兩種錯誤頁對比
- 通過擴展限流算法支持限流后一段時間不可用,例如按IP限流且某個IP已經觸發限流,則支持該IP一段時間內不可訪問,無需重新通過算法計算。
- 同步實現了黑名單、白名單功能,通過白名單避免一些復雜場景下的限流“誤殺”(例如nat網絡下按ip限流)。
四、運維特性
運維特性主要指Nginx的安裝、升級、配置文件修改、啟停等操作,運維特性與之前介紹內容的最大區別在于需要重啟操作,所以結合第三方工具Ansible是比較合適的想法(Ansible相對于Puppet等運維工具,其遷移成本相對較小)。
在實際生產中Ansible和Web為避免單點需要集群部署,我們的方案是:Web和Ansible在同一PC上部署,相關數據改用DB存儲替代Ansible本地文件存儲,通過這種簡單的改造可以方便Ansible和Web這組“套件”進行擴容。
圖7:自動化運維操作邏輯圖
如上圖,用戶通過Web操作控制Ansible對Nginx進行升級、重啟等操作,Web是Nginx操作的統一入口,這是平臺化的重要意義所在,可以放棄SSH,Shell甚至是監控系統,開始在JEN自給自足了。
通過主動拉取或者是用戶在頁面導入、手工配置,JEN會為所有Nginx存儲配置文件,這樣不僅原本因為每個應用都依賴不同的配置項而導致管理混亂的局面得到了改善,而且也可以方便的對配置文件做些擴展,例如歷史記錄追溯,配置比對,配置復用,操作回滾等。
在頁面執行相關操作時,Web會讀取Ansible的標準輸出并在頁面實時展示,為了讓使用者以相對友好的方式獲知進度我們對Ansible做了優化:
-
豐富了標準輸出的內容,盡量細化到每一個步驟。
-
格式化標準輸出,便于Web獲取和展示。
Nginx在生產環境大規模部署,倘若因為一些原因導致Nginx大規模異常,這是我們不希望看到的,所以在可靠性方面,JEN也提供了多種機制來保證:
1. 三層錯誤校驗,保證只有在完全正確的情況下才會重啟和更新進程,中途發生任何錯誤不影響線上服務
a)在Web填充表單時做第一層校驗。
b)在目標機器做操作時做第二層檢測,例如先執行Nginx –t校驗。
c)執行完畢做第三層校驗,例如端口是否啟動,進程數是否一致等。
2. 灰度執行
a)單個Nginx依次執行,有任何異常立即中斷開始人工介入。
b)按百分比支持批量執行,例如某個機房的Nginx先升級10%。
五、總結
以上整理了京東在Nginx平臺化方面的一些實踐,JEN提供了統一入口管控整個Nginx生命周期,并支持規則的批量修改即時生效,我們希望這些實踐經驗能對所有讀者產生幫助。
來自:http://www.infoq.com/cn/articles/JD-Nignx-JEN