標籤:

攜程第四代架構探秘之運維基礎架構升級

作為國內最大的OTA公司,攜程為數以億計的海內外用戶提供優質的旅遊產品及服務。2014年底攜程技術中心的框架、系統和運維團隊共同啟動了架構改造項目,歷時2年,涉及所有業務線。本文回顧了攜程在整個技術架構改造過程中的一些實踐和收穫。

一、寫在前面

隨著攜程業務量迅速增長、業務變化越來越敏捷,對於應用交付的效率也提出了更高的要求。根據統計,截止2014年底攜程總應用數在5000個左右,平均每周約有3000次以上的發布需求。所以作為整體交付環節中極為重要的一環,應用的部署和發布是提高交付效率的關鍵,然而攜程原來的發布系統Croller卻成為了阻礙交付效率提升的一大瓶頸。

【關於攜程火車發布】

*攜程火車發布規定:每天定時安排發布車次,以pool為單位安排車廂,在一個pool中的應用必須在「同一車次」的「同一個車廂」內做發布。

*攜程實際發布情況:每個應用在發布前需要「買票」,也就是申請和備案的過程,然後被分配到某個「車次」與同在一個pool且需要發布的其他應用形成一個「車廂」,當到達規定發布時間時,該「車廂」內的所有應用以灰度的方式做發布。

*該模式的弊端:(1)如果提前準備好了發布,在未到達規定發車時間,只能等待,不能發布。(2)如果錯過了某個發車時間點,只能等待下一次。(3)如果發布過程中,同一個車廂內有一個應用發布失敗,則整個車廂中的應用全部發布失敗。

具體來說,攜程Croller設計的是火車模式發布,主要面臨的核心問題包括:

(1)由於ASP.NET的應用佔大多數,基本都採用的是Windows + IIS 的單機多應用的部署模式,應用和應用之間的隔離性較弱,且由於應用劃分的顆粒度比較細,在單機上往往可能同時部署20~30個應用,多的甚至達到60個,導致大量不同應用之間共用應用程序池的情況存在,即多個應用運行在同一個進程下,這種情況下任何一個應用的發布都可能影響到其他的關聯應用。

(2)使用硬體負載均衡設備承載應用的訪問入口,以域名為單位隔離。單機上的多個應用程序共享同一個訪問入口(同一個域名),所以健康檢測也只能實現到伺服器級別,無法識別應用級的故障。

(3)由於治理系統中的應用信息不統一或不準確,影響監控和排障。

二、從破題到解題

1. 破題思路

針對混亂又複雜的情況,如果要想從根本上去解決這些問題,提高交付效率,則必須要從配置管理、部署架構上全面支持以應用為最小顆粒度的管理能力。

我們解決思路包括:

(1)引入Group的概念,設計從App、Server、Pool、Group、Route的完整數據結構模型來描述應用相關的配置部署信息,並由CMS作為權威數據源向外提供數據介面,確保數據的一致性和準確性。

這些定義如下:

App 代表一個應用,可以是web、service、job等

Server 代表伺服器

Pool 部署了相同應用程序的一組伺服器集合

Group 一組相同應用的實例集合

(2)引入七層負載均衡(SLB),實現應用的訪問入口的隔離。使每個訪問入口(集群)的成員(即應用進程實例)可具備獨立的管理能力,實現應用級的健康檢測。

(3)設計實現新一代的發布系統Tars,解決Croller發布系統的痛點,支持應用級的發布。

2. 具體實現

雖然有了破題思路,但具體實現仍然有很多細節需要考慮,主要包括:

(1)統一配置(CMS)

(2)彈性路由(SLB)

(3)想發就發(TARS)

統一配置(CMS)

正如大型傳統企業發展初期缺失ERP系統一樣,互聯網公司也需要發展到一定規模才能意識到一個完備的配置信息系統的重要性。

互聯網公司在整個產品研發和運行生命周期中不斷產生大量的系統和工具,例如測試平台、發布平台、監控系統和資源管理工具等。業務產品研發效率和業務系統穩定運行依賴這些工具平台的高效協同工作。而如果要實現這種高效協同,關鍵就是擁有一個統一的配置信息平台。

不成熟的配置管理往往有以下特徵:

(1)配置系統之間相對獨立和分散,缺少關聯關係,且運維、研發工具、測試生產環境都有各自視角的局部配置管理系統;

(2)缺少明確的定義和抽象。例如,不同語言開發的應用對配置的描述方式有很大的差異性;或者對集群、發布節點和訪問入口等重要對象的定義很模糊;

(3)配置信息不準,依賴手工維護,沒有工具和流程約束,要執行者自己來保證操作和配置數據的一致性;

(4)配置描述不完整,使得系統架構,比如集群、域名映射等關鍵環節缺乏嚴格的配置管理。

而攜程這種配置管理暴露出很多問題,當監控到伺服器資源異常時,例如CPU、內存出現異常,無法快速查找該異常影響的範圍,造成不知道應該聯繫誰進行排障;而對於非.NET的應用無法提供發布工具,或只是提供了一些功能有限的發布模塊,但由於不同技術使用的發布工具有著很大的差異性,給使用方和開發維護方都帶來了極大的不便;當資源和應用之間的關係不清晰,運維無法實現完善的資源計費等重要管理職能。

要應對這些問題,需要定義統一的配置模型和一致的配置數據,清晰地描述組織、業務、應用、系統架構和資源等要素及互相間的關係。從更高層次設計一套配置管理系統,使得各個維度的配置信息既要專註於自身的領域,又能和其他相關的配置信息維度建立起關聯,確保這些工具能以一致的定義來理解配置數據,進行順暢而有效的協同工作。

於是攜程的配置管理系統CMS就應運而生了,CMS核心目標包括:

(1)數據準確(即與實際保持一致)且合規

(2)數據關係的查詢方便高效

(3)數據變動可追溯

(4)系統高可用

(5)數據模型簡潔易懂

1. CMS系統演變過程

(1)抽象,定義,建立關係,存儲數據;

對於應用層面運維所涉及到的對象進行統一地抽象,使得使用不同技術、不同架構的應用體系都能使用一樣的模型結構來進行描述。

根據攜程的應用體系和管理方式,我們抽象出一套最核心的應用配置對象,包括組織、產品線、產品、應用、集群、發布節點、伺服器等。經過與那些不同語言不同技術架構所開發的應用間的磨合實驗,我們驗證了這套抽象的配置對象有其普適性,並可以完備地描述攜程範圍內各種應用的配置狀態。

只要按照這套配置對象系統對一個應用完成了描述,那麼該應用從發布到上線運行再到下線的全生命周期內,各種相關工具均能通過獲取這些配置狀態得到足夠的信息進行工作。換句話說,通過這套統一的配置信息資料庫,不同開發者、不同階段、不同功能的平台實現了協同工作。

(2)將CMS作為一種服務提供出去。

由於建立了描述應用體系的核心配置資料庫,這必然會有大量用戶和工具成為CMS的消費者。所以我們希望CMS消費者可以通過網路隨時隨地獲取、維護和管理CMS。這要求CMS能提供完備的API和一套簡潔直觀的管理界面。

(3)通過Portal和工作流引擎完成配置變更,實現業務邏輯的自動化執行。

除了建立統一的應用配置模型,還要建立應用配置的生命周期管理,做到生成配置,修改配置以及銷毀配置都合規,都經過授權,都有記錄可查。

(4)搭建一個強壯可靠的配置管理體系。

通過更多的子模塊助力搭建配置管理體系來提高穩定性和可用性,實現查錯追溯和數據巡檢糾錯等功能。

2. CMS系統架構

CMS系統在開發過程中遇到和解決了一系列的棘手問題,系統本身的架構也反映了這些方案的設計實施情況。

(1)數據治理

CMS系統最基本而關鍵的需求是提供正確的數據,數據必須能真實反映生產環境的配置現狀,並且還要符合公司制定的運維規範,不能出現違規配置。例如,攜程不允許同一個應用在一台伺服器上運行多於一個實例;不允許在一台伺服器上運行多於一個Java應用;每個伺服器上只能運行同樣類型的應用等。

所以為保證數據的準確性,CMS數據需要持續治理。我們把這部分的治理工作通過一個相對獨立的規則引擎來實現。該規則引擎主要完成的工作包括:

  • 允許快速添加新規則,可以使用輕量的腳本語言快速定義各種規則進行數據檢查;
  • 針對複雜規則設計了場景和規則兩層結構,不同場景可根據需求來配置不同規則;
  • 數據入庫時做檢查,並進行定期巡檢,最大限度查找和消滅錯誤配置。

(2)關係管理和變更追溯

對配置數據關聯關係的管理和使用是CMS用戶最為看重的功能之一。被CMS管理著的組織、產品、應用、集群、伺服器、域名、發布節點等配置間都有著千絲萬縷的複雜關係,用戶可能從任何一個配置對象開始查找與另一個配置對象的關係,比如從應用查找伺服器;從伺服器查找組織;從域名查找應用等等。

為提供最便利強大的查找功能,我們專門設計了一套查詢框架,根據定義好的對象關係快速生成配置對象之間的查詢。現在用戶可以通過CMS界面和API非常方便地查找到配置數據間的關聯關係。

與此相關的還有變更歷史的查找,用戶除了需要查找一個配置對象自身的變更歷史外,還經常需要查找一個配置對象相關的對象變更歷史,比如要查找一個應用下面所有伺服器的擴容縮容歷史;查找一個集群中應用上下線的歷史等等。於是我們採用了一種將變更消息沿對象關係鏈廣播出去的方案,把變更和相關配置對象連接起來。

(3)完善的監控和應對訪問壓力

CMS因匯聚了生產環境核心的配置數據而被大量工具所依賴,因此其必須能夠承受大量而密集的查詢需求(工作時間內每分鐘上萬次請求是常態)。

下圖是攜程介面網關日誌分析出的各種工具對CMS介面的調用情況。

彈性路由(SLB)

攜程部署架構採用的是單機多應用,每台伺服器上部署了很多個應用。這些應用不一定存在緊密內聯關係,且很可能屬於不同團隊,這種架構存在著明顯的問題。

其實攜程面臨的這些問題並不是突然暴發的,而是經過十多年的演進和慢慢累積,最終大家不得不正視這些問題。

從本質上講,這些問題的根源是應用間的耦合,最好的解決方案就是單機單應用。因為單機單應用實現了應用間的天然物理隔離(部署在不同的伺服器上),從而極大地降低了運維的複雜度,部署、排障、溝通、配置和個性化等都不用再擔心會對其他應用有影響。

單機單應用是業界普遍推薦和採用的一種部署架構,但對攜程而言這卻是個系統性的大工程,需要從底層基礎設施到配套系統工具、從流程規範到開發人員的思維轉變等方面投入大量的人力和時間。所以我們首先就要考慮如何在單機多應用的情況下,實現應用解耦,也就是做到應用粒度的運維。

相比應用粒度的運維目標,攜程當時實際情況則是伺服器的運維粒度,並且絕大多數的運維操作還是通過硬體LB來完成。雖然硬體LB的好處顯而易見,例如,高吞吐量、高性能和優秀的穩定性等。但其缺點也同樣明顯:

(1)水平擴展成本高昂;

(2)基於規則無法建模,規則過多時就會陷入運維泥潭;

(3)無法進行高頻次的變更,因為集中式管理模式中,配置數據一多,API性能就會急劇下降;

(4)只能由少數的專職運維人員做操作。

所以,硬體LB除了無法做到應用粒度外,低效也成為一個很重大缺陷。為了解決在路由運維方面的粒度和效率問題,攜程決定打造自己的軟負載(SLB)系統,替代掉硬體LB的七層路由職責。經過討論,SLB確定了自己的職能目標,即可以高並發、實時、靈活、細粒度調整七層路由規則。從另一方面想,SLB還需要實現由面向機器運維到面嚮應用運維的轉變,以及由硬體支撐到軟體支撐的進化。

在攜程SLB的開發過程中,最重要的幾點是:

(1)面嚮應用建模;

(2)多次更新一次生效

(3)多並發操作的挑戰;

(4)多角色運維衝突的問題;

(5)監控和告警。

1. 面嚮應用建模

攜程經過評估最終選擇了Nginx來構建軟負載系統。開發前我們參考了業界內其他公司的實現方式,基本包含幾個特點:

(1)開發了一個Nginx配置文件的批量管理工具;

(2)需要專業的運維人員來操作;

(3)日常操作頻率較低;

(4)和現有系統接合較鬆散。

結合攜程的現狀,我們在建模時還需要考慮:

(1)和現有系統無縫接合,融入現有系統的生態體系;

(2)支持高頻率的並發操作;

但如何和現有建模體系融合起來?在開發人員眼中最重要最核心的常見模型就是一個一個的應用。所以SLB要做的是如何和應用模型融合起來,換句話說,所有對SLB的操作都要被抽象為對一個應用的操作。Nginx是基於文本配置文件,其內建了一個自己的模型,一次運維操作可以導致多個Nginx模型的變更。所以我們需要創建一個模型,這個模型可以和應用模型一一對應,又能被翻譯成Nginx的內建模型,而這就是Group:

(1)一個Group是一個應用在SLB的投影;

(2)SLB上所有的操作都抽象成對Group的操作;

(3)不同Group的操作互不影響。

這樣只要解決一個Group的問題,就相當於解決了1000個、甚至更多個Group的問題。

2. 多次更新一次生效

建模成功地隱藏了Nginx的內存模型,並將操作轉換成了對Group的操作。雖然隔離不同Group間的操作,但在SLB上對單一Group的操作仍然是一個有風險的行為(對某一具體應用而言)。為了降低這種風險性,可以引入3種機制,包括多版本系統、日誌追蹤和多次更新一次生效。

Group的每次變更都會產生一個新的版本;Group的所有變更都會留下日誌;對Group的變更操作並不會直接對生產生效,可以在多次變更後,有一次明確的激活操作後,從而在生產環境正式生效。

3. 多並發操作

引入group後實現了應用的獨立運維,但如果有上千個Group要同時進行擴容操作,那麼如何做到每個Group的操作都在5秒內完成?

因為Nginx是基於一個文本配置文件的,那麼這樣的要求就會轉換為對配置文件的上千次操作,然後再對SLB重新載入上千次配置文件。假設一次操作花費1s,那麼最後一個操作可能要等1000s,這種實現方式顯然對於那些排在後面的Group更新者是無法接受的,而且SLB在這種高頻度更新下,自身也無法工作。所以簡單地把一次Group更新轉換成一次Nginx的配置更新是肯定行不通的。(攜程真實情況是Nginx變更日操作達到8萬次,整個軟負載API日請求數達到300萬次)。

為了實現Group更新的互不影響,並確保所有Group更新保持在一個穩定返回時間內,SLB確定了核心業務流程:

(1)將一段時間內所有的Group更新操作(比如2秒內)緩存在一個任務隊列中;

(2)對任務隊列中的所有操作進行合併,最終只對Nginx的配置文件做一次更新。

這個流程的核心邏輯就是多次操作一次更新,最大程度減少對Nginx配置文件的操作,但外部看來Group更新操作是獨立且保持在穩定返回時間內的。

4. 多角色運維的衝突

一個Group可能會有多種角色進行更新,比如應用Owner、專業運維人員和發布系統人員等。這就引出了一個新的問題,即當一個角色對一個Group的伺服器進行拉出操作後,另一個角色可不可以對這些伺服器做拉入操作?

比如,發布系統人員在發布完成後,準備做拉入,卻發現運維人員對這台伺服器進行了拉出操作。這時發系統應該如何決策?這不僅造成決策的困擾,也會使不同的角色產生聯繫,甚至相互耦合在一起。

為了解決這個問題,我們決定採用多狀態的機制:

(1)為每一種角色分配一個伺服器狀態;

(2)一個角色對這個狀態進行了失效操作,最終也只能由這個角色進行恢復操作;

(3)SLB在所有角色都認為這台伺服器有效時,才會認為這台伺服器可工作。

5. 健康檢測帶來的瓶頸

SLB另一個核心功能是健康檢測,即需要以一定頻率對應用伺服器進行心跳檢測,連續失敗多次後對伺服器進行拉出操作,成功後再進行拉入恢復。大多數公司採用了節點獨立檢測造成了帶寬浪費和伺服器壓力,而攜程採用了節點共享檢測,具體機制是一個獨立的應用負責檢測,然後把檢測結果在SLB節點間傳播共享。

【攜程的健康檢測效果】

攜程獨立健康檢測的運行效果良好,目前SLB系統已經負責了攜程超過5萬個結點的健康檢測任務。而下圖是由節點獨立檢測變為節點共享檢測時的SLB單一伺服器網路連接釋放狀況:

6. 監控數據採集和告警

SLB負責了幾乎所有的基於域名的http調度請求,所以也成為了進行請求流量統計和請求質量統計的絕佳場所。包括在有問題時進行報警;根據不同維度統計請求量;響應碼分布和響應時間分布等,攜程使用了分析access log的方式來獲得監控數據:

(1)SLB伺服器流式讀取本機實時產生的access log;

(2)分析聚合log數據,產生不同的統計數據。最終使用了語法樹分析實現了高效分析,一秒可以分析14萬條日誌;

(3)定期(1分鐘)將統計數據吐到監控系統CAT等。

以此可以產生多維度的監控統計數據,如下圖:

基於上述數據,可以查看整個攜程或單個應用性能表現,進行相應的優化。在慢請求和非200請求的數量異常時,執行報警操作,確保及時恢復和挽回損失。

想發就發(TARS)

解決了配置和路由問題後,發布系統前置障礙已基本掃除,而從OPS角度來看,發布系統還有幾個重要目標:

(1)灰度發布

(2)簡單易用

(3)發布迅捷

1、灰度發布

通常發布有三種常規方法,藍綠髮布,滾動發布,金絲雀發布。對這三種發布類別做比較,可以發現:

(1)藍綠髮布:需要額外的伺服器集群支持,且數量可觀,同時由於攜程單機多應用的部署現狀,就會造成發布一個應用需要替換整台伺服器的情況,實現難度巨大且成本不經濟。

(2)滾動發布:雖然可以節省資源,但對應用的兼容性有較高要求,因為發布過程中同時會有兩個版本對外提供服務。但這類問題相對容易解決,實際中往往會通過功能開關,dark launch等方式來解決。

(3)金絲雀發布,比較符合攜程對灰度發布的預期,但可能需要精細的流控和數據的支持,同樣有版本兼容的需求。

【發布相關說明】

藍綠髮布:優先將新版本發布到待發布的機器上,並進行驗證,此時新版本伺服器並不接入外部流量。發布和驗證過程中老版本所在的伺服器仍照常服務,驗證通過後,經過流控處理把流量引導到新伺服器,待全部流量切換完成,老版本伺服器下線。

滾動發布:從老版本伺服器中挑選一批,停止老版本的服務,並更新為新版本,進行驗證,通過後再分批增量更新剩餘伺服器。

金絲雀發布:往往從集群中挑選特定伺服器或一小批符合要求的特徵用戶,對其進行版本更新及驗證,隨後逐步更新剩餘伺服器。

結合攜程的實際情況,最終挑選的方式是滾動發布和金絲雀發布的結合體,首先允許對一個較大的應用集群,特別是跨IDC的應用集群按自定義的規則進行切分,形成較固定的發布單元。每個應用的每個發布單元稱為「group」,這個group與之前提到的SLB的group是一一對應的。

每個發布單元,即group在發布過程時,還可以再分批進行,完成滾動發布。而每個group中包含一台或多台堡壘機,必須先完成堡壘機的發布和驗證,才能繼續其他機器的發布,從而實現金絲雀發布。除堡壘機的發布外,其他機器可按照用戶能接受的最大同時拉出比例來分批,分批間允許設置具體的驗證等待時間。

每台機器在發布過程中都要經歷拉出、下載、安裝、點火和拉入這5個步驟,發布流程為:

基於以上設計,攜程新一代發布系統開發完成,命名為Tars 。

【Tars源代碼】

Tars已做了開源,開源版本地址:github.com/ctripcorp/ta

2、簡單易用

發布配置必須簡單易懂,絕大部分的應用發布都是固定模式,不需要個性化配置,所以Tars只提供了幾個核心配置項,包括(1)允許同時拉出的最大比例;(2)批次間的等待時間;(3)啟動超時時間;(4)是否忽略點火。

除此以外,用戶最關心的是發布過程中可操作按鈕的易用性,Tars在這方面做了充分考慮,通過狀態機的控制,保證用戶在操作界面上同時最多只看到兩個操作按鈕,絕大部分情況下用戶只需在「繼續」或「終止」這樣的0或1的選擇中做出決策。

而圖形化界面的展示,Tars也確保用戶可以更直觀地觀察到發布的進展,以及出現的問題。

有了簡單操作,危機時刻就會得到放大體現,比如,因生產故障做回滾時,能快速中斷當前發布,並從界面中輕鬆地選到所需回滾的版本,然後一鍵無配置地觸發完成回滾。

3、發布迅捷

天下武功無堅不摧,唯快不破,而發布也一樣。發布速度快了,迭代速度研發效率也就提升了;回滾速度快了,生產故障造成的影響也就減輕了;擴容速度快了,彈性計算就能實施了,這樣運維效率被大幅度提升。

從上面對發布過程的描述中,不能發現在攜程通常影響發布速度的步驟是下載和驗證。

(1)為了提高下載速度,攜程在各個機房搭建了發布包專用的存儲系統,實現了類似CDN的功能,編譯和打包系統在任何一個寫入點寫入發布包,都會儘快同步到各個IDC及各個獨立存儲中,這樣真正發布時,伺服器只需從本IDC或本網段做下載。而回滾方面,Tars則是在伺服器本地保留了n個版本(n根據伺服器磁碟容量計算獲得),做回滾時可快速地進行目錄切換,進而省略了代碼下載過程。

(2)對於驗證,攜程在框架層面統一提供了驗證入口和常規驗證方法(攜程稱為「點火」),收口了所有應用的驗證規範和標準,容錯性得到提升。

(3)Tars在系統設計方面充分考慮了速度需求。每個發布單元採用quick and dirty的方式,不管成功或失敗,優先嘗試把版本發布完成,後續在解決個別發布失敗的問題。

根據同時拉出服務的最高比率(由用戶設置)進行失敗率控制,一旦達到比率,立即中斷當前發布,從而對quick and dirty方式做保護(攜程稱為「剎車」)。發布單元中只要有任何一台伺服器發布失敗,都會被認為是發布局部失敗,允許用戶重試發布。

發布過程中如發現伺服器當前運行版本與發布目標版本一致,且驗證通過,則直接skip。批次間可設置觀察等待時長,從第3個批次起,允許設置0或較少的等待時長,以提高後幾批次的速度(攜程稱為「尾單加速」)。

三、結果和未來

通過CMS+SLB+TARS幾個系統的聯動,並經歷了長達一年半的項目推廣階段,終於實現了1+1+1>>3的效果。新發布系統對於研發效率和研發人員體驗的提升都非常顯著。

這可以通過一些數字來證明,與2年前相比,每周的發布迭代次數成長了4倍,但單次發布的平均時長從13分鐘卻降低到了3分鐘。同時因為發布/回退效率的提升,當需要對線上代碼做緊急修復時,或者將其回退到已發布的代碼版本時,都會更快捷地完成,所以使得發布類故障的處理效率也得到了提升。

對2015年至2017年的發布相關故障的統計後,發現該佔比下降了一半以上。

因為CMS+SLB+TARS基於良好的配置數據模型設計,及其應用級的運維支持能力,為後續的技術架構改造帶來了便捷和優勢。這主要體現在:

(1)高效的容量管理,實現了對應用容量的自動化監測,當發現容量不足時,無需研發介入,全自動地進行應用伺服器擴容、發布、上線和投產等。

(2)在應用容災方面,基於準確的配置數據,可以很容易的將單數據中心的業務應用「克隆」到另外的數據中心來進行部署。

(3)在應用技術棧的遷移(例如.net應用改造為java應用),用戶也能自助地創建新的java應用,並通過SLB靈活實現灰度流量切換,進而自助、高效、穩定、安全地完成整個應用遷移。

【作者簡介】本文由攜程技術中心框架研發部吳其敏、王興朝,技術保障中心高峻、王瀟俊、陳劼聯合撰寫。

沒看夠?更多來自攜程技術人的一手乾貨,歡迎搜索關注「攜程技術中心」微信公號哦~


推薦閱讀:

【乾貨合集】Redis深入之道——Redis4.0、讀寫分離技術揭秘及實戰應用
iOS架構設計之」冗餘性」思考
雙十一絲般順滑體驗背後:阿里雲洛神網路虛擬化系統揭秘

TAG:架构 |