每日最新頭條.有趣資訊

從亞馬遜的實踐,談分布式系統的難點

接收程式員的技術早餐

作者|陳皓

編輯|楊爽

從目前可以得到的資訊來看,對分布式服務化架構實踐最早的應該是亞馬遜。因為早在 2002 年的時候,亞馬遜 CEO 傑夫·貝索斯(Jeff Bezos)就向全公司頒布了下面的這幾條架構規定(來自《Steve Yegge 對 Google 平台吐槽》一文)。

所有團隊的程式模塊都要通過 Service Interface 方式將其數據與功能開放出來。

團隊間程式模塊的資訊通信,都要通過這些接口。

除此之外沒有其它的通信方式。其他形式一概不允許:不能直接鏈結別的程式(把其他團隊的程式當做動態鏈接庫來鏈接),不能直接讀取其他團隊的數據庫,不能使用共享記憶體模式,不能使用別人模塊的後門,等等。唯一允許的通信方式是調用 Service Interface。

任何技術都可以使用。比如:HTTP、CORBA、Pub/Sub、自定義的網絡協定等。

所有的 Service Interface,毫無例外,都必須從骨子裡到表面上設計成能對外界開放的。也就是說,團隊必須做好規劃與設計,以便未來把接口開放給全世界的程式員,沒有任何例外。

不這樣做的人會被炒魷魚。

這應該就是 AWS(Amazon Web Service)出現的基因吧。當然,前面說過,採用分布式系統架構後會出現很多的問題。比如:

一個線上故障的工單會在不同的服務和不同的團隊轉運站過來轉過去的。

每個團隊都可能成為一個潛在的 DDoS 攻擊者,除非每個服務都要做好配額和限流。

監控和查錯變得更為複雜。除非有非常強大的監控手段。

服務發現和服務治理也變得非常複雜。

為了克服這些問題,亞馬遜這麽多年的實踐讓其可以運維和管理極其複雜的分布式服務架構。我覺得主要有以下幾點。

分布式服務的架構需要分布式的團隊架構。在亞馬遜,一個服務由一個小團隊(Two Pizza Team 不超過 16 個人,兩張 Pizza 可以喂飽的團隊)負責,從前端負責到數據,從需求分析負責到上線運維。這是良性的分工策略——按職責分工,而不是按技能分工。

分布式服務查錯不容易。一旦出現比較嚴重的故障,需要整體查錯。出現一個 S2 的故障,就可以看到每個團隊的人都會上線。在工單系統裡能看到,在故障發生的一開始,大家都在簽到並自查自己的系統。如果沒問題,也要在線待命(standby),等問題解決。(我在《故障處理最佳實踐:應對故障》一文中詳細地講過這個事)。

沒有專職的測試人員,也沒有專職的運維人員,開發人員做所有的事情。開發人員做所有事情的好處是——吃自己的狗糧(Eat Your Own Dog Food) 最微觀的實踐。自己寫的代碼自己維護自己養,會讓開發人員明白,寫代碼容易維護代碼複雜。這樣,開發人員在接需求、做設計、寫代碼、做工具時都會考慮到軟體的長期維護性。

運維優先,崇尚簡化和自動化。為了能夠運維如此複雜的系統,亞馬遜內部在運維上下了非常大的功夫。現在人們所說的 DevOps 這個事,亞馬遜在 10 多年前就做到了。亞馬遜最為強大的就是運維,拚命地對系統進行簡化和自動化,讓亞馬遜做到了可以輕鬆運維擁有上千萬台虛機的 AWS 雲平台。

內部服務和外部服務一致。無論是從安全方面,還是接口設計方面,無論是從運維方面,還是故障處理的流程方面,亞馬遜的內部系統都和外部系統一樣對待。這樣做的好處是,內部系統的服務隨時都可以開放出來。而且,從第一天開始,服務提供方就有對外服務的能力。可以想像,以這樣的標準運作的團隊其能力會是什麽樣的。

在進化的過程中,亞馬遜遇到的問題很多,甚至還有很多幾乎沒有人會想到的非常生僻的東西,它都一一學習和總結了,而且都解決得很好。

構建分布式系統非常難,充滿了各種各樣的問題,但亞馬遜還是毫不猶豫地走了下去。這是因為亞馬遜想做平台,不是“像淘寶這樣的中介式流量平台”,而是那種“可以對外輸出能力的平台”。

亞馬遜覺得自己沒有像史蒂夫·喬布斯(Steve Jobs)這樣的牛人,不可能做出像 iPhone 這樣的爆款產品,而且用戶天生就是眾口難調,與其做一個大家都不滿意的軟體,還不如把一些基礎能力對外輸出,引入外部的力量來一起完成一個用戶滿意的產品。這其實就是在建立自己的生態圈。雖然在今天看來這個事已經不稀奇了,但是貝索斯早在十五年前就悟到了,實在是個天才。

所以,分布式服務架構是需要從組織,到軟體工程,再到技術上的一個大的改造,需要比較長的時間來磨合和改進,並不斷地總結教訓和成功經驗。

分布式系統中需要注意的問題

我們再來看一下分布式系統在技術上需要注意的問題。

問題一:異構系統的不標準問題

這主要表現在:

軟體和應用不標準。

通訊協定不標準。

數據格式不標準。

開發和運維的過程和方法不標準。

不同的軟體,不同的語言會出現不同的兼容性和不同的開發、測試、運維標準。不同的標準會讓我們用不同的方式來開發和運維,引起架構複雜度的提升。比如:有的軟體修改配置要改它的.conf 檔案,而有的則是調用管理 API 接口。

在通訊方面,不同的軟體用不同的協定,就算是相同的網絡協定裡也會出現不同的數據格式。還有,不同的團隊因為用不同的技術,會有不同的開發和運維方式。這些不同的東西,會讓我們的整個分布式系統架構變得異常複雜。所以,分布式系統架構需要有相應的規範。

比如,我看到,很多服務的 API 出錯不返回 HTTP 的錯誤狀態碼,而是返回個正常的狀態碼 200,然後在 HTTP Body 裡的 JSON 字元串中寫著個:error,bla bla error message。這簡直就是一種反人類的做法。我實在不明白為什麽會有眾多這樣的設計。這讓監控怎麽做啊?現在,你應該使用 Swagger 的規範了。

再比如,我看到很多公司的軟體配置管理裡就是一個 key-value 的東西,這樣的東西靈活到可以很容易地被濫用。不規範的配置命名,不規範的值,甚至在配置中直接嵌入前端展示內容……

一個好的配置管理,應該分成三層:底層和作業系統相關,中間層和中間件相關,最上面和業務應用相關。於是底層和中間層是不能讓用戶靈活修改的,而是隻讓用戶選擇。比如:作業系統的相關配置應該形成模板來讓人選擇,而不是讓人亂配置的。只有配置系統形成了規範,我們才 hold 得住眾多的系統。

再比如:數據通訊協定。通常來說,作為一個協定,一定要有協定頭和協定體。協定頭定義了最基本的協定數據,而協定體才是真正的業務數據。對於協定頭,我們需要非常規範地讓每一個使用這個協定的團隊都使用一套標準的方式來定義,這樣我們才容易對請求進行監控、調度和管理。

這樣的規範還有很多,我在這就不一一列舉了。

問題二:系統架構中的服務依賴性問題

對於傳統的單體應用,一台機器掛了,整個軟體就掛掉了。但是你千萬不要以為在分布式的架構下不會發生這樣的事。分布式架構下,服務是會有依賴的,於是一個服務依賴鏈上,某個服務掛掉了,會導致出現“多米諾骨牌”效應,會倒一片。

所以,在分布式系統中,服務的依賴也會帶來一些問題。

如果非關鍵業務被關鍵業務所依賴,會導致非關鍵業務變成一個關鍵業務。

服務依賴鏈中,出現“木桶短板效應”——整個 SLA 由最差的那個服務所決定。

這是服務治理的內容了。服務治理不但需要我們定義出服務的關鍵程度,還需要我們定義或是描述出關鍵業務或服務調用的主要路徑。沒有這個事情,我們將無法運維或是管理整個系統。

這裡需要注意的是,很多分布式架構在應用層上做到了業務隔離,然而,在數據庫結點上並沒有。如果一個非關鍵業務把數據庫拖死,那麽會導致全站不可用。所以,數據庫方面也需要做相應的隔離。也就是說,最好一個業務線用一套自己的數據庫。這就是亞馬遜伺服器的實踐——系統間不能讀取對方的數據庫,隻通過服務接口耦合。這也是微服務的要求。我們不但要拆分服務,還要為每個服務拆分相應的數據庫。

問題三:故障發生的概率更大

在分布式系統中,因為使用的機器和服務會非常多,所以,故障發生的頻率會比傳統的單體應用更大。隻不過,單體應用的故障影響面很大,而分布式系統中,雖然故障的影響面可以被隔離,但是因為機器和服務多,出故障的頻率也會多。另一方面,因為管理複雜,而且沒人知道整個架構中有什麽,所以非常容易犯錯誤。

你會發現,對分布式系統架構的運維,簡直就是一場噩夢。我們會慢慢地明白下面這些道理。

出現故障不可怕,故障恢復時間過長才可怕。

出現故障不可怕,故障影響面過大才可怕。

運維團隊在分布式系統下會非常忙,忙到每時每刻都要處理大大小小的故障。我看到,很多大公司,都在自己的系統裡拚命地添加各種監控指標,有的能夠添加出幾萬個監控指標。我覺得這完全是在“使蠻力”。一方面,資訊太多等於沒有資訊,另一方面,SLA 要求我們定義出“Key Metrics”,也就是所謂的關鍵指標。然而,他們卻沒有。這其實是一種思維上的懶惰。

但是,上述的都是在“救火階段”而不是“防火階段”。所謂“防火勝於救火”,我們還要考慮如何防火,這需要我們在設計或運維系統時都要為這些故障考慮,即所謂 Design for Failure。在設計時就要考慮如何減輕故障。如果無法避免,也要使用自動化的方式恢復故障,減少故障影響面。

因為當機器和服務數量越來越多時,你會發現,人類的缺陷就成為了瓶頸。這個缺陷就是人類無法對複雜的事情做到事無巨細的管理,只有機器自動化才能幫助人類。 也就是,人管代碼,代碼管機器,人不管機器!

問題四:多層架構的運維複雜度更大

通常來說,我們可以把系統分成四層:基礎層、平台層、應用層和接入層。

基礎層就是我們的機器、網絡和存儲設備等。

平台層就是我們的中間件層,Tomcat、MySQL、Redis、Kafka 之類的軟體。

應用層就是我們的業務軟體,比如,各種功能的服務。

接入層就是接入用戶請求的網關、負載均衡或是 CDN、DNS 這樣的東西。

對於這四層,我們需要知道:

任何一層的問題都會導致整體的問題;

沒有統一的視圖和管理,導致運維被割裂開來,造成更大的複雜度。

很多公司都是按技能分工是,把技術團隊分為產品開發、中間件開發、業務運維、系統運維等子團隊。這樣的分工導致各管一攤,很多事情完全連不在一起。整個系統會像 “多米諾骨牌”一樣,一個環節出現問題,就會倒下去一大片。因為沒有一個統一的運維視圖,不知道一個服務調用是如何經過每一個服務和資源,也就導致我們在出現故障時要花大量的時間在溝通和定位問題上。

之前我在某雲平台的一次經歷就是這樣的。從接入層到負載均衡,再到服務層,再到作業系統底層,設定的 KeepAlive 的參數完全不一致,導致用戶發現,軟體運行的行為和文檔中定義的完全不一樣。工程師查錯的過程簡直就是一場惡夢,以為找到了一個,結果還有一個,來來回回花了大量的時間才把所有 KeepAlive 的參數設定成一致的,浪費了太多的時間。

分工不是問題,問題是分工後的協作是否統一和規範。這點,你一定要重視。

小   結

好了,我們來總結一下今天分享的主要內容。首先,我以亞馬遜為例,講述了它是如何做分布式服務架構的,遇到了哪些問題,以及是如何解決的。我認為,亞馬遜在分布式服務系統方面的這些實踐和經驗積累,是 AWS 出現的基因。隨後分享了在分布式系統中需要注意的幾個問題,同時給出了應對方案。

我認為,構建分布式服務需要從組織,到軟體工程,再到技術上的一次大的改造,需要比較長的時間來磨合和改進,並不斷地總結教訓和成功經驗。

獲得更多的PTT最新消息
按讚加入粉絲團