亞馬遜的實踐:分布式系統(tǒng)的難點
問題一:異構(gòu)系統(tǒng)的不標(biāo)準(zhǔn)問題
在軟件開發(fā)和運維過程中,存在許多標(biāo)準(zhǔn)化問題。例如,軟件和應(yīng)用缺乏統(tǒng)一的標(biāo)準(zhǔn),通訊協(xié)議和數(shù)據(jù)格式各不相同,開發(fā)和運維的過程和方法也不一致。不同的軟件和編程語言帶來了不同的兼容性問題,以及各自不同的開發(fā)、測試、運維標(biāo)準(zhǔn)。這種差異導(dǎo)致我們在開發(fā)和運維時采用不同的方法,從而增加了架構(gòu)的復(fù)雜度。
舉例來說,有的軟件需要通過修改 .conf 文件來更改配置,而另一些則需要調(diào)用管理 API 接口。在通訊方面,不同軟件采用不同的協(xié)議,即使在相同的網(wǎng)絡(luò)協(xié)議中,也可能出現(xiàn)不同的數(shù)據(jù)格式。不同的技術(shù)團隊由于使用了不同的技術(shù)棧,也會導(dǎo)致開發(fā)和運維方式的多樣化。這些差異都會使我們的分布式系統(tǒng)架構(gòu)變得極其復(fù)雜。因此,分布式系統(tǒng)架構(gòu)需要制定相應(yīng)的規(guī)范。
例如,我注意到許多服務(wù)的 API 返回錯誤時不使用 HTTP 的錯誤狀態(tài)碼,而是返回 HTTP 200,然后在響應(yīng)體(HTTP Body)中的 JSON 字符串中包含類似 “error, bla bla error message” 的錯誤信息。這種設(shè)計不僅反直覺,而且極大地影響了監(jiān)控的實現(xiàn)。實際上,現(xiàn)在應(yīng)該使用 Swagger 等規(guī)范來標(biāo)準(zhǔn)化 API。
另一個例子是配置管理。很多公司在軟件配置管理中只是采用簡單的 key-value 形式,這種靈活性雖然很高,但也很容易被濫用。例如,不規(guī)范的配置命名,不規(guī)范的值,甚至在配置中直接嵌入前端展示內(nèi)容。一個合理的配置管理應(yīng)分為三層:底層和操作系統(tǒng)相關(guān),中間層和中間件相關(guān),最上層則是業(yè)務(wù)應(yīng)用相關(guān)。底層和中間層的配置不應(yīng)讓用戶隨意修改,而是通過模板選擇。比如,操作系統(tǒng)相關(guān)的配置應(yīng)形成標(biāo)準(zhǔn)模板供用戶選擇,而非隨意修改。只有當(dāng)配置管理形成了規(guī)范,我們才能有效控制各種系統(tǒng)的復(fù)雜性。
再比如,數(shù)據(jù)通訊協(xié)議通常包括協(xié)議頭和協(xié)議體。協(xié)議頭定義了基本的協(xié)議數(shù)據(jù),而協(xié)議體則包含業(yè)務(wù)數(shù)據(jù)。我們需要在協(xié)議頭的定義上制定嚴(yán)格的標(biāo)準(zhǔn),以便所有使用該協(xié)議的團隊都遵循同一套規(guī)則。這種規(guī)范化能夠幫助我們更好地進行請求監(jiān)控、調(diào)度和管理。
通過這些規(guī)范化措施,我們可以更好地控制分布式系統(tǒng)的復(fù)雜度,提高系統(tǒng)的可維護性和穩(wěn)定性。
問題二:系統(tǒng)架構(gòu)中的服務(wù)依賴性問題
在傳統(tǒng)的單體應(yīng)用中,如果某臺服務(wù)器宕機,整個應(yīng)用就會停止運行。然而,這并不意味著分布式架構(gòu)下就不會出現(xiàn)類似的問題。在分布式系統(tǒng)中,各個服務(wù)之間通常存在依賴關(guān)系。當(dāng)依賴鏈上的某個服務(wù)發(fā)生故障時,可能會引發(fā)“多米諾骨牌”效應(yīng),導(dǎo)致一系列連鎖反應(yīng)。因此,在分布式架構(gòu)中,服務(wù)的依賴關(guān)系也可能帶來許多問題。
一個典型的情況是,非關(guān)鍵業(yè)務(wù)被關(guān)鍵業(yè)務(wù)依賴,結(jié)果導(dǎo)致非關(guān)鍵業(yè)務(wù)變得像關(guān)鍵業(yè)務(wù)一樣重要。服務(wù)依賴鏈中常會出現(xiàn)“木桶效應(yīng)”,即整個系統(tǒng)的服務(wù)水平協(xié)議(SLA)由表現(xiàn)最差的那個服務(wù)所決定。這屬于服務(wù)治理的范疇。有效的服務(wù)治理不僅要求我們定義每個服務(wù)的重要程度,還需要清晰地定義和描述關(guān)鍵業(yè)務(wù)的主要調(diào)用路徑。沒有這樣的治理和管理措施,我們將難以有效地運維和管理整個系統(tǒng)。
需要特別注意的是,盡管許多分布式架構(gòu)在應(yīng)用層面做到了業(yè)務(wù)隔離,但在數(shù)據(jù)庫層面卻沒有。如果一個非關(guān)鍵業(yè)務(wù)因為數(shù)據(jù)庫問題而導(dǎo)致整個數(shù)據(jù)庫負(fù)載過高,可能會拖垮整個系統(tǒng),導(dǎo)致全站不可用。因此,在數(shù)據(jù)庫層面也需要進行相應(yīng)的隔離。最佳實踐是每條業(yè)務(wù)線使用獨立的數(shù)據(jù)庫。這也是亞馬遜服務(wù)器的實踐之一:系統(tǒng)之間禁止直接訪問對方的數(shù)據(jù)庫,只能通過服務(wù)接口進行交互。這種做法符合微服務(wù)架構(gòu)的要求。
總而言之,我們不僅要拆分服務(wù),還需要為每個服務(wù)分配獨立的數(shù)據(jù)庫,從而避免不同服務(wù)之間相互干擾。這樣才能真正實現(xiàn)業(yè)務(wù)隔離,提升系統(tǒng)的可靠性和穩(wěn)定性。
問題三:故障發(fā)生的概率更大
在分布式系統(tǒng)中,由于使用的機器和服務(wù)數(shù)量龐大,故障發(fā)生的概率比傳統(tǒng)的單體應(yīng)用更高。雖然分布式系統(tǒng)可以通過隔離來減小故障的影響范圍,但由于組件繁多、結(jié)構(gòu)復(fù)雜,故障的發(fā)生頻率反而更高。另一方面,由于管理難度大,系統(tǒng)架構(gòu)全貌難以掌握,因此錯誤更容易發(fā)生。對于分布式系統(tǒng)的運維而言,這幾乎是噩夢般的挑戰(zhàn)。隨著時間的推移,我們會逐漸認(rèn)識到以下幾點:
- 故障發(fā)生并不可怕,恢復(fù)時間過長才是真正的問題。在分布式系統(tǒng)中,故障幾乎是不可避免的,但如果恢復(fù)時間過長,就會對業(yè)務(wù)產(chǎn)生嚴(yán)重影響。
- 故障發(fā)生并不可怕,影響范圍過大才令人擔(dān)憂。在設(shè)計分布式系統(tǒng)時,我們需要盡量控制故障的影響范圍,避免單點故障帶來的連鎖反應(yīng)。
運維團隊在分布式系統(tǒng)中面臨巨大的壓力,幾乎時刻都在處理各種大小不一的故障。很多大公司試圖通過添加大量的監(jiān)控指標(biāo)來應(yīng)對這些問題,有時甚至設(shè)置了幾萬個監(jiān)控點。但這種做法實際上是“蠻力”策略。一方面,過多的信息會導(dǎo)致信息過載,反而難以獲取有價值的洞察;另一方面,服務(wù)水平協(xié)議(SLA)要求我們定義關(guān)鍵指標(biāo)(Key Metrics),即最為重要的性能和狀態(tài)指標(biāo)。然而,很多公司在實際操作中忽視了這一點。
這種做法反映了運維思維上的惰性,因為它只關(guān)注“救火階段”而不是“防火階段”。正所謂“防火勝于救火”,我們需要在設(shè)計和運維系統(tǒng)時預(yù)先考慮故障的發(fā)生,即所謂的“設(shè)計即為容錯”(Design for Failure)。在系統(tǒng)設(shè)計中,就應(yīng)考慮如何減輕故障的影響。如果無法完全避免故障,也應(yīng)通過自動化手段盡快恢復(fù)故障,將影響控制在最小范圍內(nèi)。
隨著系統(tǒng)中機器和服務(wù)數(shù)量的增加,我們會發(fā)現(xiàn)人類的局限性逐漸成為管理的瓶頸。人類無法對復(fù)雜系統(tǒng)進行面面俱到的管理,這時,自動化手段就顯得尤為重要。“人管理代碼,代碼管理機器,人不直接管理機器”,這一理念意味著我們應(yīng)通過自動化和代碼治理來管理分布式系統(tǒng)的復(fù)雜性,將人的作用集中在管理代碼和策略上,而將具體的系統(tǒng)操作交由自動化系統(tǒng)執(zhí)行。這種方式不僅能夠提升系統(tǒng)的穩(wěn)定性和可控性,還能有效減輕運維團隊的負(fù)擔(dān)。
問題四:多層架構(gòu)的運維復(fù)雜度更大
通常情況下我們可以將系統(tǒng)分為四個層次:基礎(chǔ)層、平臺層、應(yīng)用層和接入層。
- 基礎(chǔ)層包括機器、網(wǎng)絡(luò)和存儲設(shè)備等基礎(chǔ)設(shè)施。
- 平臺層指的是中間件層,包含 Tomcat、MySQL、Redis、Kafka 等軟件。
- 應(yīng)用層包含各種業(yè)務(wù)軟件和功能服務(wù)。
- 接入層負(fù)責(zé)用戶請求的接入,如網(wǎng)關(guān)、負(fù)載均衡、CDN 和 DNS 等。
對于這四個層次,我們需要明確一點:任意一層出現(xiàn)問題,都會影響整個系統(tǒng)的運行。如果沒有一個統(tǒng)一的視圖和管理機制,各層的運維就會被割裂,導(dǎo)致更大的復(fù)雜性。
許多公司在團隊分工上是按照技能進行劃分的,比如產(chǎn)品開發(fā)、中間件開發(fā)、業(yè)務(wù)運維、系統(tǒng)運維等子團隊。這種分工方式會導(dǎo)致每個團隊只關(guān)注自己負(fù)責(zé)的部分,造成整個系統(tǒng)缺乏協(xié)同,信息不暢。當(dāng)某個環(huán)節(jié)出現(xiàn)問題時,整個系統(tǒng)就像“多米諾骨牌”一樣,一個故障會引發(fā)連鎖反應(yīng),影響范圍不斷擴大。
由于缺乏統(tǒng)一的運維視圖,團隊無法清楚地了解一個服務(wù)調(diào)用在每個層次和資源中是如何流轉(zhuǎn)的。因此,當(dāng)出現(xiàn)故障時,定位問題和溝通協(xié)調(diào)會消耗大量時間和精力。此前我在某云平臺工作時曾遇到過類似的情況:從接入層到負(fù)載均衡,再到服務(wù)層和操作系統(tǒng)層,每個環(huán)節(jié)的 KeepAlive 參數(shù)設(shè)置不一致,導(dǎo)致軟件的實際運行行為與文檔描述不符。工程師在排查問題的過程中屢屢受挫,以為解決了一個問題,結(jié)果又出現(xiàn)了新的問題。經(jīng)過多次反復(fù)排查和調(diào)試,最終才將所有 KeepAlive 參數(shù)設(shè)置成一致,耗費了大量時間。
由此可見,分工本身不是問題,問題在于分工后的協(xié)作是否統(tǒng)一和規(guī)范。這一點尤其值得重視。在系統(tǒng)運維中,確保各層次之間的協(xié)調(diào)一致、標(biāo)準(zhǔn)化管理以及對系統(tǒng)的整體視圖掌握,是避免運維混亂、提高效率和系統(tǒng)穩(wěn)定性的關(guān)鍵。
圖片