1. 程式人生 > >從淘寶到雲端的高可用架構演進

從淘寶到雲端的高可用架構演進

近日在Qcon開發者大會北京站上,來自阿里巴巴商家事業部技術專家沐劍在專場分享了題為《高可用實踐:從淘寶到上雲的差異》的演講,主要介紹了其近幾年在阿里電商平臺及阿里雲上的高可用設計的經驗,分為兩個部分:第一部分主要包括傳統的淘寶店鋪穩定性體系的建設及相關的基礎鏈路設計、快取和容災方案的設計及部署;第二部分主要包括目前公有云高可用設計的主要思路、經典故障及應對措施等。

演講全文:

架構演進

沐劍

大家好,我今天分享的題目是《高可用實踐:從淘寶到上雲的差異》,取這個標題是因為會涉及到兩個方面內容,一方面以淘寶為例子,傳統的IDC的時候,我們穩定性是怎麼做的,另外在雲端計算背景下,有很多創業公司是基於阿里雲這樣的公有云基礎設施做研發,在公有云的環境下怎麼做好我們系統的高可用。

長期做穩定性的人會有一些職業病,記得去年冬天有個週末,我要寄快遞,穿著睡衣在門口填快遞單,那時候我家裡養了一隻貓,因為怕貓跑出去,就把門關上了。寄完快遞口袋一掏發現自己沒帶鑰匙,冷靜了3秒鐘,打車到公司剛巧碰到同事,看我穿著睡衣來公司了問我什麼情況,我說家裡鑰匙忘帶被鎖在門外面了,不過沒事,我還有一把backup鑰匙放在公司。生活中很多時候都需要有一個backup。

我的花名叫沐劍,2011年加入淘寶做評價系統,2012-2015年在店鋪平臺,負責店鋪的前臺瀏覽系統和後臺的RPC服務,以及一些效能優化、雙11保障的事情。到了2015年開始到了TAE團隊,開始負責雲端架構及整體高可用方案,TAE的升級版EWS現在也在聚石塔上面幫大量ISV和創業公司解決運維部署、自動化監控和效能分析等等問題。去年我是作為阿里商家事業部雙11作戰專案研發的PM。2017年我開始接手商家營銷團隊。在阿里五六年的經驗,其實就做了幾件事,比如連續五年參加了雙十一的核心備戰,然後像去IOE、異地多活,全鏈路壓測、安全混合雲、容器服務等專案參與設計和實施。

首先我會從淘寶店鋪角度分享,以前在店鋪是怎麼樣做雙11保障的,後面是一些公有云相關的內容。這是一個淘寶的店鋪系統,這套系統是一個非常典型的高併發的瀏覽系統,在前幾年的雙11峰值有20萬次的Web頁面請求,平均一個頁面對應了20次的RPC呼叫,這個時候對於整個系統的集合來說每秒的QPS是400萬次,這中間就會涉及到快取、資料庫以及其它二方的RPC呼叫,對於這樣的系統來說,在效能、穩定性和體驗間要做一個平衡,既不能純用太多的機器死扛這個訪問量,又要保障使用者的體驗。

從請求鏈路來說,首先DNS把CDN的VIP解析出來,分佈在全球不同的區域,CDN回源到接入層分別經過4層和7層的負載均衡,近幾年會發現CDN這個行業早已不僅僅侷限做CSS/JS等靜態資源的快取,也承擔了一些動態加速和收斂的特性,所以我們是通過CDN做域名收斂,收斂後會把這個流量發到統一接入層,然後到應用叢集,後面再經過應用儲存、Cache這些服務。

當我們在做穩定性的時候,會考慮效能和穩定性之間是什麼關係,很多人認為這兩者是衝突的,比如我要保障一個東西的效能非常高的時候,要犧牲掉很多別的東西,可能你要引入一個非常新的框架或者基礎設施來提升效能,但它的穩定性可能是不那麼成熟的,但是從容量規劃的角度看,只有這套系統性能足夠好,才能承擔像雙11那樣的大訪問量。

優化

店鋪也是一套經歷了很多年的系統,在應用層上的優化基本上已經做到極致了,我們就轉變思路,在作業系統層能不能做一些優化,這裡藉助了一個比較好的工具perf,在作業系統層面告訴你係統呼叫的開銷是集中在哪裡,從perf上就可以定位到有一個百分比,可以看到是比如陣列分配還是GC產生了大量的開銷。最初我們發現是異常帶來的開銷,就會看為什麼這個系統的異常會導致20%以上的CPU開銷,最後用BTrace跟了一下異常的建構函式,發現是我們依賴的開源的三方包裡通過異常做控制流,每一次它處理結束的時候,就拋一個EOFException出來,這個就導致了非常大的開銷,我們就把開源包替換掉了。當你依賴一些底層的東西的時候,如果對原理不太瞭解會給你帶來一些意料之外的事情。JVM裡是有一個常量池儲存字串常量的地方,就是一個雜湊表,如果說這個表的大小不足夠大,就會從雜湊查詢變成連結串列查詢,效能就會特別低。

再談一個warm up的問題,當我們應用剛剛啟動的時候,還沒有把位元組碼編譯成native code,延遲非常高,使用者就得到一個有損的服務。我們現在在內部的JVM做一個功能,會採集線上系統的呼叫,把熱點方法收集下來做分析,在應用把真實流量掛上去之前,已經預先把所有的熱點方法編譯成native code保證這個效能。開源界也有其他的方案,比如Azul的Zing有個ReadyNow,IBM的J9有個AOT,也是做類似的事情。另外這裡我放了一個Github的連結,這個agent能夠讓你在perf介面裡直接看Java Method的開銷。

Cache

談到快取,Cache裡有一些小技巧,在做雙十一備戰時發現一個店鋪的基礎服務平時日常就每天有100億的呼叫量,當時是幾十臺機器估了一下可能要成倍增長,成本是非常高的,怎麼解決這個問題,當時寫了個富客戶端,讓業務方先去查我們分散式Cache,如果命中就直接返回來,如果不命中再走我們的服務端查。這種情況下,只要你能夠保證命中率足夠高,比如98%的命中率,就意味著只有2%是需要後端伺服器承擔剩下的請求,用非常少的伺服器去承擔非常大的流量,這是成本和效能間的權衡。

在快取方面,我們很少會關心快取的高可用是怎麼部署的,它是一個偏運維的內容,我把快取的部署簡化成一個雙機房的模型,因為它在高可用裡是最簡單的場景。對於快取來說有兩種經典部署模式,第一種叫共享叢集部署,在IDC裡我的應用是分機房部署的,Cache叢集也是分機房部署,對於應用伺服器來說,兩邊的Cache對他來說邏輯上是一個叢集,會往IDC 1的Cache寫一半過去,往IDC 2也寫一半過去,這種部署的好處在於,機房間網路斷掉的時候,有一半的資料是在快取的,保證一半的資料能夠命中,不會直接死掉,另外對成本上相對比較友好,沒有浪費任何一個Cache的節點,這個Cache本身是複用的。但是也正如剛才說的問題,如果中間斷掉了,有一半的命中率是有損的,所以就誕生了另外的一個部署模式,就是獨立部署,不管你哪個機房掛掉,命中率是基本不變的,兩邊同時保持了98%的命中率,但是它是成本不友好的,兩邊要同時部署,同時承擔副本的作用,並且失效時,要同時失效另外一個IDC 2,這樣才保證一致性。

在快取上,我認為一切東西都可以被快取的,通常我們認為快取跟實際資料庫裡存在的東西可能是不一樣的,有幾毫秒的延遲或者怎麼樣,所以我們設計一個系統的時候,對一致性要求非常高的時候,會傾向於不用快取,用資料庫扛這個流量。但以MySQL為例,InnoDB裡有一個很重要的快取Buffer Pool,對於一個數據庫,在冷庫的情況下用一堆SQL去查它,和慢慢預熱完再查它的時候效果是不一樣的,這個是我們當初在做異地多活時面臨的一個問題,例如我已經有一個機房,希望建立一個新單元去承擔這個機房的流量,當我建完這個單元,把所有的應用都部署好了後,把流量切50%過來會怎麼樣,假設這兩個單元的機器數一樣,這個單元會掛,因為這個資料庫是冷的,快取是空的,不能承擔之前那個單元資料庫所能承擔的QPS。

現在業界有很多叫API閘道器或者CDN,他們在邊緣節點也做了一層短暫的Cache,可能只Cache 50或者100毫秒,但是當你係統受到攻擊的時候可以拯救你後端的應用系統,攻擊引發的命中率通常比較高,有這50毫秒的快取,可能後端只有幾百個QPS過來,那個流量你是可以承受的。

在高可用裡兩個非常經典的做法是限流和降級,在阿里雙11,有一位老兵說過一句話,他說當雙11到來的時候,任何一個系統都可能出問題,你要做的是對你的上游限流,對你的下游限流。怎麼理解,當上流的流量超過你的能力的時候就要限流,當下遊比如DBA告訴你資料庫壓力很大了,那就對下游限流,只要保證住這個限流,你基本不會掛,每個系統都做到這個的時候,整個系統都是可用的。當流量超出你掌控的時候,這個做法可以讓你成為這個暴風下的倖存者。

限流降級

對限流降級的思考,第一限流降級考驗的是什麼問題,我認為本質上考驗的是故障自恢復能力,在平時工作中會遇到機房斷網或者停電,每半個月都會做斷網演練,不告訴你發生什麼,就把這個網切斷,看你的應用O不OK,一般是在晚上兩三點,接到很多的機房報警,這個時候看你的架構設計的是否足夠可用,如果足夠可用就沒問題,不會造成什麼影響,繼續睡覺,如果設計不好,就得爬起來立即處理。

而開關降級最大的作用,比如我們發現一些線上的問題,第一反映是趕緊回滾,但是當你的系統很大的時候,特別像Java這種,一個系統啟動要啟動幾分鐘,你的回滾完成,20分鐘都過去了,這個過程對使用者來說都是有損的,而開關可以在一瞬間把所有的邏輯切到老的。這個是避免回滾時間導致的問題。開關有的時候能救命,如果沒有這個開關的話,避免問題放大就只能回滾,所以開關是一個很大的價值所在。

容災設計

另外一點非常重要的是,在設計一個技術方案的時候,就會把容災的設計融入到方案裡。比如在設計技術方案的時候,在最後一章單獨有一個容災設計,這個節點裡任何服務掛掉的時候,你要保持什麼樣的方式保持這個服務是可用的。

在容災設計時有幾點必須考慮,比如我引了一個新jar包或者調了一個新的RPC的服務、引入了分散式的儲存,以前沒用過也不知道它穩不穩定,第一想法是它肯定會掛,它掛了我們怎麼做,我們當時在做前臺系統的非同步化的時候,因為Redis支援map的資料結構,所以我們就是用Redis的hmget從這個map裡拿出部分的key減少網絡卡的流量,但即使這個掛掉了,我們還會走老的Cache,只不過網絡卡流量會大一些,但是對使用者的服務是無損的,所以這裡要考慮如果它掛了怎麼做降級,有什麼樣的恢復流程。

另外是釋出計劃,在新系統上線時就會關注這些問題,比如這次有沒有做資料遷移,比如以前我是8個庫不夠用了我拆到16個庫或者32個庫,中間一定是有資料遷移的,涉及到資料遷移一定要有一套對賬系統保證這個資料是新資料和老資料是對得平的,不然一定有問題,因為我們是做交易相關的,訂單、金額絕對不能出問題。

另外是你的釋出順序是不是有依賴,如果出了問題的時候,誰要先回滾,這裡是取決於技術設計。另外是否要通過客服公告的方式告訴外部使用者說有5分鐘的不可用,如果真的有使用者打電話有疑問客服同學可以向用戶解釋。

在高可用這個領域做久了會有一種直覺,這個直覺很重要,來源於你的經驗轉換成這種直覺,但是對於一個成熟的團隊來說,需要把這種直覺轉化為產品或工具。有很多牛人他們的技能都只能叫手藝,你需要把這種手藝轉換成產品和工具。

2015年我去做雲產品,這裡給大家分享下我們是怎麼樣幫客戶包括我們的系統在雲上是做高可用的。

故障案例

首先看兩個經典故障案例,第一個是Gitlab生產資料庫刪了,它恢復了很久,Snapshot等全都沒有生效,做了五六層的備份也都沒有什麼用。這個事情說明第一我們的故障要定期演練,比如中介軟體在做的線上故障演練,你說你的系統可用性好,我把這個主庫斷了,虛擬機器掛掉幾臺試試,做這些演練就可以知道你這個容災體系是不是可靠的,如果沒有這個演練的話,當真正的故障發生時你才會發現這個東西是不OK的。

另外一個很典型的問題,Gitlab對備份的原理是不夠了解的,比如當時用的PostgreSQL的一個版本,當時是有問題的,沒有驗證,開發人員對這個又不是特別瞭解的情況下就會出現這個問題,這就是為什麼要去了解你的依賴以及你依賴的依賴。去年我們做壓測,有個應用一邊壓測一邊在優化做釋出,發現第一批發的起不來了,就只是改了一兩行程式碼加日誌,他就去看什麼原因,最後發現依賴的某個jar包依賴一個配置,而這個配置在壓測中被降級了,一個jar包就把應用啟動卡住了。如果在雙十一當天或者在平時業務高峰期的時候發現這個問題是來不及修復的。所以這個時候,我們就要求,依賴的二方jar包必須看一下里面是怎麼實現的,依賴哪些東西。

反過來說,別人依賴我的客戶端就意味著他不僅依賴著我的服務還依賴著我的快取,這個快取出了問題對他也有影響,我們每年雙十一前有一個強弱依賴梳理,不僅要梳理自己應用裡面的,還有依賴的所有東西都梳理出來,中間任何一個節點掛掉了你應該怎麼辦,需要給一個明確答覆。第二個故障案例是今年發生的,AWS S3敲錯了一個命令把基礎核心服務下線了,有一個物件索引服務和位置服務系統被offline,後來也做了一些改進,每次敲的命令有一個靜默期,讓你有個反悔的機會,線上有個最小的資源保證服務。

這個給我們帶來的啟示是什麼,雲服務本身也是會發生故障的,比如買了雲資料庫,我們沒有辦法假設它是100%可用的,當它出現問題我們怎麼辦,是給雲廠商提工單說什麼時候能恢復,還是我自己能夠有一個容災的方案解決這個問題。從2015年開始,我們越來越多地發現,對架構可用性最大的威脅是什麼?在市政施工裡一定機率就會莫名其妙搞出光纜被挖斷等故障,我們不得不考慮,當雲服務本身出現問題我們該怎麼辦。

所以我們需要有一套面向雲的高可用架構。在很早以前有廠商提出類似SDN的一個概念,叫SDI——軟體定義基礎設施,過去我們發現只有大廠可以做這個事情,設計一套很複雜的管理系統幫他實現,這裡放一個路由器,這邊放一臺虛擬機器,可以通過軟體的控制流去控制這個東西。但是在雲的時代,資源變得很容易獲得。以阿里云為例子,可以用API隨時建立新的虛擬機器,新的負載均衡,或者是新的儲存,都可以通過API隨時建立隨時銷燬,這對於你的基礎設施的靈活度非常有好處。

以前有的人會覺得,效能問題或者容量問題應該通過效能優化的方式解決,通過一些黑科技方式解決,加機器會覺得很low。但我覺得一個問題如果能簡單用加機器來解決是很不容易的,意味著對你的整個架構的水平擴充套件性要求非常高,而且解決效率很高,加機器就解決了,而對一些中心化的系統來說就比較麻煩,加機器都加不了,可能要把機器關掉升配置再重新拉起來。所以我們說,在公有云上面,在資源如此容易獲得的情況下要充分利用這個特性,要做一個能夠做水平擴充套件的架構。

那麼第一步要做什麼,前兩年很火的容器、微服務,本質上都是解決了是無狀態的應用怎麼做自動化的擴容這個問題。右邊這個圖上面,上面是一個負載均衡,中間是一個前端的服務,後端是一個無狀態的後端服務,底層是MQ、物件儲存、資料庫這些東西,如果我們能夠把前端和後端的無狀態服務第一步先容器化,就可以做到當流量過來的時候,只要後端的儲存沒有問題,整套架構就是能夠水平擴充套件的。

從去年公開的報道和故障來看,很多人有個誤會是說雲廠商的機器應該是不會掛的,我買了一臺雲廠商的虛擬機器應該是隨時可用的,即使不可用雲廠商也要幫我解決熱遷移的問題,熱遷移在業界是很複雜的問題,不光涉及到磁碟儲存的遷移,也涉及到記憶體是要做遷移的,可能還要用RDMA。並且對於傳統的IDC來說,不管物理機還是虛擬機器都是有可能掛的,對雲也不例外。當我們在使用公有云服務的時候,都是會掛的,這是個心理準備。不光是機器,包括負載均衡是不是也有可能掛,下面的訊息佇列或者資料庫是不是也有可能會掛,當你基於任何東西都可能會掛的前提設計一個系統的時候,才能真正做到這個系統在任何情況下都不會受底層雲服務的故障影響。

而對於不同的雲服務來說是有不同的容災策略。比如一臺虛擬機器掛了,通常來說負載均衡不管是4層還是7層都會做健康檢查,掛了健康檢查不通自動會把流量切斷。如果我的負載均衡掛了怎麼辦,如果DNS有健康檢查那就方便了,如果沒有的話可能就要設計一個旁路系統解決這個問題,發現這個已經不通了,就自動把它從DNS上摘掉。

不管是雲服務發生故障還是自己應用發生故障有個大原則是如何最快速解決問題,就是一個字,切。為什麼要做異地多活,為什麼要把流量往各個地方引,切流量是解決問題最快的,把壞流量切到好的地方馬上就解決了,如果你要等定位問題解決問題再上線客戶就流失掉了。對淘寶來說也是一樣,當某一個單元低於我們認為的可用性的時候,我們會把這個單元的流量引到另外一個可用的單元,當然前提是那個單元的容量是足夠的。

彈性是不是萬能的?所有的雲服務都是彈性的,彈性其實不是萬能的,容量規劃仍然是有必要的,不然就沒必要做雙十一備戰了。這裡有一個你需要付出的代價,彈性的過程往往是需要時間的,那麼容量規劃在這個環節中起到的作用就很重要,當真的逼不得已的時候,我要擴容了,怎麼保證我擴完容之前系統不雪崩?就是要結合之前的限流,儘可能保障每個客戶得到他應有的服務,但是也要保障系統的穩定性。

Region和Availability Zone這兩個,當實踐的時候,購買虛擬機器、負載均衡或者資料庫,一定要選擇多可能區的服務,比如阿里雲買SLB是可選可用區的,有個主可用區和副可用區,如果一邊掛了可以切換到另外一邊,RDS也是一樣的。

這幅圖是一套典型基於公有云的一套架構,不叫異地多活,應該叫跨區域設計。左右兩個大框是兩個城市,左邊是北京,右邊是上海,每個裡面又有不同的機房可用區去承擔這個流量,假如北京的掛掉了,就切,原來是在A可用區,就切到B可用區,這時候A可用區沒有流量進來,通過切換的方式能夠把這個服務快速恢復。下面畫了一個跨區域複製,我們在異地多活專案裡,涉及到了跨城市跨資料中心的複製,比如我的北京是提供寫服務的,上海要提供讀服務,就要通過這種方式同步資料過去。

最後,我們在招聘,招一些志同道合的小夥伴。我看到很多人做招聘,他們都在說我們想要什麼樣的人,需要你具備什麼樣的技能,我的想法可能不太一樣,我會先思考你到我們團隊能得到什麼,我能給你什麼東西。那麼第一可以參與到阿里最前線的作戰經驗,今年2017年雙十一大促可以跟我們一起做,第二我們團隊內部有非常好的氛圍,可以聽到來自阿里巴巴各領域的專家像魯肅、畢玄等大師的分享,第三可以飛速成長,一對一師兄帶領,快速度過適應期。新零售新技術,會創造出下一個時代的商業文明,對於技術人員來說我們不光有技術的思維,也要有業務和商業的思維來培養自己。

文章來自微信公眾號:網際網路架構師