服務(wù)可用性的一知半解
談到高并發(fā)和高可用往往引起很多人的興趣,有時(shí)候成為框架選擇的噱頭。實(shí)際上,它們往往和框架關(guān)系不大,而是跟架構(gòu)息息相關(guān)。在很多時(shí)候,老碼農(nóng)會(huì)直面一個(gè)問題:
“系統(tǒng)的服務(wù)可用性是多少?是怎么得來?”
但在思考這個(gè)問題之前,先要澄清一個(gè)概念,那就是——
什么是服務(wù)可用性
可用性就是一個(gè)系統(tǒng)處在可工作狀態(tài)的時(shí)間的比例,這通常被描述為任務(wù)可行率。數(shù)學(xué)上來講,相當(dāng)于1減去不可用性。——wiki 百科
相應(yīng)的,我們的軟件系統(tǒng)處于可工作的時(shí)間比例,就是服務(wù)的可用性,也就是說,服務(wù)可用性可以描述為一個(gè)百分比的數(shù)值。我們經(jīng)常用這個(gè)SLO(service-level objective ,服務(wù)級(jí)目標(biāo))來代表服務(wù)可用性,至于SLO,SLA,SLI 等概念之間的差異,這里暫不做深入討論。
SLO用數(shù)字來定義可用性對(duì)于特定服務(wù)的意義,來表示服務(wù)幾乎總是活著,總是處于可以快速運(yùn)行的狀態(tài)。制定SLO是根據(jù)如下:
絕大多數(shù)軟件服務(wù)和系統(tǒng)的目標(biāo)應(yīng)該是近乎完美的可用性,而不是完美的可用性。服務(wù)可用性一般是99.999% 或99.99% ,而不是100%,因?yàn)橛脩魺o法區(qū)分服務(wù)是100% 可用和不“完美”可用之間的區(qū)別。在用戶和服務(wù)之間還有許多其他的系統(tǒng),例如筆記本電腦、家庭 WiFi、互聯(lián)網(wǎng)等等 ,這些系統(tǒng)的可用性遠(yuǎn)遠(yuǎn)低于100% 。因此,99.99% 和100% 之間的邊際差異在其他不可用性的噪音中丟失了,并且,即使為增加最后一部分可用性付出了巨大努力,用戶也可能沒有從中獲得任何好處。
很多云服務(wù)的目標(biāo)是向用戶提供99.99% 的可用性(就是我們常說的“四個(gè)9”)。一些服務(wù)在外部承諾較低的數(shù)字,但在內(nèi)部可能設(shè)定了99.99% 的目標(biāo)。作為SLA,這個(gè)嚴(yán)格的目標(biāo)描述了用戶在違反合同之前對(duì)服務(wù)性能不滿意的情況,因?yàn)檐浖?wù)的首要目標(biāo)是讓用戶滿意。對(duì)于許多服務(wù),99.99% 的內(nèi)部目標(biāo)代表了平衡成本、復(fù)雜性和可用性的最佳位置。
服務(wù)可用性解讀
服務(wù)可用性是中斷頻率和持續(xù)時(shí)間的函數(shù)。它是通過以下方式衡量的: * 停機(jī)頻率,或者是它的倒數(shù): MTTF (平均停機(jī)時(shí)間)。* 持續(xù)時(shí)間,使用 MTTR (平均修復(fù)時(shí)間)。持續(xù)時(shí)間根據(jù)用戶的經(jīng)歷定義: 從故障開始持續(xù)到正常行為恢復(fù)。
因此,可用性在數(shù)學(xué)上定義為使用適當(dāng)單位的 MTTF / (MTTF + MTTR)。
四個(gè)9的服務(wù)可用性可能是很多軟件系統(tǒng)的目標(biāo),如何達(dá)到這一目標(biāo)呢?需要先明確一下導(dǎo)致服務(wù)不可用的來源。服務(wù)不可用有兩個(gè)主要來源: 服務(wù)本身的問題和服務(wù)的關(guān)鍵依賴的問題。關(guān)鍵依賴是指如果出現(xiàn)故障,就會(huì)導(dǎo)致服務(wù)相應(yīng)故障的依賴項(xiàng)。
關(guān)鍵依賴
服務(wù)的可用性不能超過其所有關(guān)鍵依賴關(guān)系的交集。如果服務(wù)的目標(biāo)是提供99.99% 的可用性,那么所有的關(guān)鍵依賴項(xiàng)必須遠(yuǎn)遠(yuǎn)超過99.99% 的可用性。據(jù)說在谷歌內(nèi)部,使用這樣一個(gè)經(jīng)驗(yàn)法則: 因?yàn)槿魏畏?wù)都有幾個(gè)關(guān)鍵依賴項(xiàng),以及自身的特殊問題,關(guān)鍵依賴必須提供一個(gè)與服務(wù)相關(guān)的額外9% 的可用性(這里為99.999%) 。
如果有一個(gè)關(guān)鍵依賴,一個(gè)相對(duì)常見的挑戰(zhàn)是沒有提供足夠的可用性,就必須采取措施來增加依賴項(xiàng)的有效可用性(例如,通過緩存、限流、優(yōu)雅降級(jí)等等)。
降低期望
從數(shù)學(xué)上看,服務(wù)的可用性不能超過其事件頻率乘以其檢測和恢復(fù)時(shí)間。例如,每年有4次完全宕機(jī),每次持續(xù)15分鐘,結(jié)果總共是60分鐘。即使該服務(wù)在這一年中剩下的時(shí)間里都運(yùn)行良好,99.99% 的可用性(每年停機(jī)時(shí)間不超過53分鐘)也是沒有達(dá)的。
如果服務(wù)被依賴于無法提供相應(yīng)水平的可用性級(jí)別,那么就應(yīng)該努力糾正這種情況,可以通過增加自身服務(wù)的可用性等級(jí),或者如前所述的增加緩解措施。降低期望值(即公布的可用性)也是一種選擇,而且往往是正確的選擇: 向相關(guān)服務(wù)明確表示,它應(yīng)該重新設(shè)計(jì)系統(tǒng)以彌補(bǔ)我們服務(wù)可用性,或者降低自己的目標(biāo)。如果不糾正或解決這種差異,可用性將無法達(dá)到要求。
服務(wù)可用性的計(jì)算
考慮一個(gè)目標(biāo)可用性為99.99% 的示例服務(wù),并處理依賴項(xiàng)和停機(jī)響應(yīng)的需求。
一個(gè)例子
假設(shè)這個(gè)99.99% 的可用服務(wù)具有以下特征:
- 每年一次大停機(jī)和三次小停機(jī)。這些數(shù)字聽起來很高,但是99.99% 的可用性目標(biāo)確實(shí)意味著每年有20到30分鐘的大范圍停機(jī)和幾次短暫的部分停機(jī)。這里的假設(shè)是: 單個(gè)分片的失敗并不被認(rèn)為是整個(gè)系統(tǒng)的失敗,總體可用性是根據(jù)分片可用性的加權(quán)和來計(jì)算的。
- 有五個(gè)獨(dú)立關(guān)鍵依賴, 服務(wù)可用性為99.999%。
- 這五個(gè)相互獨(dú)立的碎片,不能相互轉(zhuǎn)移。
- 所有變更逐個(gè)進(jìn)行,每次一個(gè)分片。
那么,本年度服務(wù)中斷的總預(yù)算為每年525,600分鐘的的0.01%或53分鐘(以每年365天為基礎(chǔ))。分配給關(guān)鍵依賴的服務(wù)中斷預(yù)算是5個(gè)525,600分鐘的0.001%,即525,600分鐘的0.005% 或26分鐘。考慮到關(guān)鍵依賴的服務(wù)中斷,該服務(wù)的中斷時(shí)間預(yù)算為53-26=27分鐘。
進(jìn)一步,預(yù)計(jì)停機(jī)次數(shù)為4次(1次完全停機(jī),3次停機(jī)僅影響一個(gè)分片), 預(yù)期服務(wù)中斷的總影響: (1 x 100%) + (3 x 20%)= 1.6。那么,可用于檢測和從中斷恢復(fù)的時(shí)間為27 / 1.6 = 17分鐘。如果監(jiān)控和告警的時(shí)間是2分鐘,值班人員調(diào)查警報(bào)的時(shí)間為5分鐘的話,則有效解決問題的剩余時(shí)間是 10分鐘。
提高可用性的方向
仔細(xì)研究這些數(shù)字,有三個(gè)主要的因素可以使服務(wù)更可靠。
- 透過流程、測試、設(shè)計(jì)review等手段,減少宕機(jī)的次數(shù)。
- 通過分片、地理隔離、優(yōu)雅降級(jí)或客戶隔離,縮小停機(jī)范圍。
- 縮短恢復(fù)時(shí)間ーー透過監(jiān)控、一鍵式回滾等。
可以在這三個(gè)方向之間進(jìn)行權(quán)衡,以便實(shí)現(xiàn)更加容易。例如,如果17分鐘的 MTTR 很難實(shí)現(xiàn),那么應(yīng)該將精力集中在減少平均停用的范圍上。
服務(wù)可用性之依賴嵌套
一個(gè)不經(jīng)意的推斷,依賴鏈中的每個(gè)額外鏈接都需要增加額外的可用性等級(jí)么?例如,二級(jí)依賴需要兩個(gè)額外的9,三級(jí)依賴需要三個(gè)額外的9,以此類推。
這種推論是不正確的,它基于依賴關(guān)系層次結(jié)構(gòu),即在每個(gè)級(jí)別上具有常量扇出的樹 具有許多獨(dú)立關(guān)鍵依賴的高可用性服務(wù)系統(tǒng)顯然是不現(xiàn)實(shí)的。
無論在依賴項(xiàng)樹中出現(xiàn)在哪里,關(guān)鍵依賴項(xiàng)本身都可能導(dǎo)致整個(gè)服務(wù)(或服務(wù)分片)失敗。因此,如果給定的組件A表現(xiàn)為幾個(gè)服務(wù)的依賴項(xiàng),那么 A應(yīng)該只計(jì)算一次,因?yàn)闊o論有多少中間的服務(wù)受到影響,A的故障終將導(dǎo)致服務(wù)的故障。
正確的依賴計(jì)算可能是這樣的:
- 如果一個(gè)服務(wù)具有N個(gè)唯一的關(guān)鍵依賴項(xiàng),那么每個(gè)依賴對(duì)服務(wù)導(dǎo)致的不可用性貢獻(xiàn)1 / N,而不管它在層次結(jié)構(gòu)中的深度如何。
- 即使它在依賴項(xiàng)層次結(jié)構(gòu)中出現(xiàn)多次,每個(gè)依賴也只計(jì)算一次。
例如,假設(shè)服務(wù)b 的故障預(yù)算為0.01% 。服務(wù)擁有者愿意花一半的預(yù)算在他們自己的 bug 和損失上,另一半花在關(guān)鍵依賴上。如果服務(wù)有 N個(gè)這樣的依賴項(xiàng),每個(gè)依賴項(xiàng)接收剩余故障預(yù)算的1 / N。典型的服務(wù)通常有5到10個(gè)關(guān)鍵依賴項(xiàng),因此每個(gè)服務(wù)的失敗率只有服務(wù)b 的十分之一或二十分之一。因此,根據(jù)一般經(jīng)驗(yàn),服務(wù)的關(guān)鍵依賴項(xiàng)必須增加額外的可用性。
服務(wù)可用性之故障預(yù)算
一般地,使用故障預(yù)算來平衡可用性和創(chuàng)新速度。這個(gè)預(yù)算定義了在一段時(shí)間內(nèi)(通常是一個(gè)月)服務(wù)可接受的故障水平。故障預(yù)算只是1減去服務(wù)的 SLO,因此,99.99% 可用的服務(wù)是故障為0.01% 的“預(yù)算”。只要服務(wù)沒有花費(fèi)當(dāng)月的故障預(yù)算,開發(fā)團(tuán)隊(duì)就可以在合理范圍內(nèi)自由地發(fā)布新特性、更新等等。
如果使用了故障預(yù)算,除了緊急安全修復(fù)和解決最初導(dǎo)致違規(guī)的更改之外,服務(wù)可能將凍結(jié)變更。直到服務(wù)在預(yù)算中贏得了空間,或者時(shí)間重置。使用 SLOs 滑動(dòng)窗口,因此故障預(yù)算逐漸增加。對(duì)于 SLO 大于99.99% 的成熟服務(wù),每季度重置預(yù)算是適當(dāng)?shù)模驗(yàn)樵试S的停機(jī)時(shí)間很小。
故障預(yù)算提供了一個(gè)共同的、數(shù)據(jù)驅(qū)動(dòng)的機(jī)制來評(píng)估發(fā)布風(fēng)險(xiǎn),從而消除了可能在運(yùn)維團(tuán)隊(duì)和產(chǎn)品開發(fā)團(tuán)隊(duì)之間產(chǎn)生的結(jié)構(gòu)性緊張。故障預(yù)算還提供了一個(gè)共同的目標(biāo),在不“超出預(yù)算”的情況下實(shí)現(xiàn)更快的創(chuàng)新和更多的發(fā)布。
提高服務(wù)可用性:減少關(guān)鍵依賴
現(xiàn)在,可以重點(diǎn)討論服務(wù)的依賴關(guān)系,如何進(jìn)行設(shè)計(jì)以減少并最小化關(guān)鍵依賴。
關(guān)于關(guān)鍵依賴
一般的,任何關(guān)鍵部件的可用性必須是整個(gè)系統(tǒng)目標(biāo)的10倍。因此,在一個(gè)理想的世界中,目標(biāo)是使盡可能多的組件成為非依賴的。這樣做意味著組件可以堅(jiān)持較低的可靠性標(biāo)準(zhǔn),獲得創(chuàng)新和承擔(dān)風(fēng)險(xiǎn)的自由。
減少關(guān)鍵依賴性的最基本和最明顯的策略是盡可能消除 SPOFs (單點(diǎn)故障)。較大的系統(tǒng)應(yīng)該能夠在沒有任何非關(guān)鍵依賴項(xiàng)或 SPOF 的給定組件的情況下可以可接受地運(yùn)行。
實(shí)際上,您可能無法擺脫所有關(guān)鍵的依賴關(guān)系,但是您可以遵循一些圍繞系統(tǒng)設(shè)計(jì)的最佳實(shí)踐來優(yōu)化可靠性。雖然這樣做并不總是可行的,但是如果你在設(shè)計(jì)和規(guī)劃階段計(jì)劃可靠性,而不是在系統(tǒng)運(yùn)行并影響實(shí)際用戶之后,那么實(shí)現(xiàn)系統(tǒng)可靠性就會(huì)更容易和更有效。
當(dāng)考慮一個(gè)新的系統(tǒng)或服務(wù)時(shí),當(dāng)重構(gòu)或改進(jìn)一個(gè)現(xiàn)有系統(tǒng)或服務(wù)時(shí),一個(gè)架構(gòu)/設(shè)計(jì)評(píng)審可以識(shí)別出內(nèi)部與外部的依賴。如果服務(wù)使用的是共享基礎(chǔ)設(shè)施(例如,多個(gè)用戶可見產(chǎn)品使用的基礎(chǔ)數(shù)據(jù)庫服務(wù)) ,要考慮該基礎(chǔ)設(shè)施是否得到正確使用。明確地將共享基礎(chǔ)結(jié)構(gòu)的所有者確定為附加的利益相關(guān)者。另外,要注意不要讓依賴關(guān)系超載,小心地與這些依賴關(guān)系的所有者協(xié)調(diào)工作。
有時(shí),產(chǎn)品或服務(wù)取決于公司無法控制的因素,例如,代碼庫、第三方提供的服務(wù)或數(shù)據(jù),要識(shí)別這些因素可以減少它們帶來的不可預(yù)測性。
冗余和隔離
通過將依賴設(shè)計(jì)為具有多個(gè)獨(dú)立實(shí)例來減輕對(duì)關(guān)鍵依賴的依賴。例如,如果在一個(gè)實(shí)例中存儲(chǔ)的數(shù)據(jù)提供了該數(shù)據(jù)99.9% 的可用性,那么在三個(gè)分布的實(shí)例中存儲(chǔ)三個(gè)副本提供了9個(gè)9的理論可用性級(jí)別。
在現(xiàn)實(shí)世界中,相關(guān)性永遠(yuǎn)不會(huì)為零,因此實(shí)際可用性不會(huì)接近9個(gè)9,而是遠(yuǎn)遠(yuǎn)高于3個(gè)9。如果一個(gè)系統(tǒng)或服務(wù)是“廣泛分布的” ,地理上的分離并不總是不相關(guān)的。在鄰近地點(diǎn)使用多個(gè)系統(tǒng),可能比在較遠(yuǎn)地點(diǎn)使用同一個(gè)系統(tǒng)更好。
類似地,向一個(gè)集群中的一個(gè)服務(wù)器池發(fā)送 RPC可以提供99.9% 的可用性,但是向三個(gè)不同的服務(wù)器池發(fā)送三個(gè)并發(fā) RPC 并接受到達(dá)的第一個(gè)響應(yīng),這樣有助于將可用性提高到遠(yuǎn)遠(yuǎn)超過三個(gè)9的級(jí)別。如果服務(wù)器池與 RPC 發(fā)送方的距離大致相等,那么這種策略還可以減少延遲。
故障切換與回滾
一個(gè)的基本經(jīng)驗(yàn)是,當(dāng)必須人工在線引發(fā)故障切換時(shí),可能已經(jīng)超出了故障預(yù)算。最好進(jìn)行故障的安全切換,如果出現(xiàn)問題,這些軟件可以自動(dòng)隔離。在無法實(shí)現(xiàn)的情況下,可以執(zhí)行自動(dòng)腳本。同樣,如果問題依賴于某一個(gè)人來檢查,那么滿足SLO 的機(jī)會(huì)會(huì)很小。
將人引入緩解計(jì)劃大大增加了 SLO 的風(fēng)險(xiǎn),需要構(gòu)建方便、快速而可靠回滾的系統(tǒng)。隨著系統(tǒng)逐漸成熟,并且對(duì)檢測問題的監(jiān)視獲得了信心,就可以通過設(shè)計(jì)系統(tǒng)自動(dòng)觸發(fā)安全回滾來降低 MTTR。
在可能的情況下,將依賴項(xiàng)設(shè)計(jì)為異步的,而不是同步的,這樣它們就不會(huì)意外地變得非常重要。如果服務(wù)等待來自其非關(guān)鍵依賴項(xiàng)之一的 RPC 響應(yīng),并且該依賴項(xiàng)的延遲會(huì)大大增加,那么這種延遲將不必要地影響父服務(wù)的延遲。通過將 RPC 調(diào)用設(shè)置為非關(guān)鍵的異步依賴項(xiàng),可以將父服務(wù)的延遲與依賴項(xiàng)的延遲解耦。雖然異步性可能會(huì)使代碼和基礎(chǔ)結(jié)構(gòu)復(fù)雜化,但這種權(quán)衡可能是值得的。
檢查所有可能的失效模式
檢查每個(gè)組件和依賴項(xiàng),并確定其故障的影響。以下問題可能是一些方向:
- 如果其中一個(gè)依賴項(xiàng)失敗,服務(wù)能否繼續(xù)以降級(jí)模式提供服務(wù)?換句話說,為優(yōu)雅的降級(jí)而設(shè)計(jì)。
- 如何處理在不同情況下依賴項(xiàng)不可用的問題?在服務(wù)啟動(dòng)時(shí)?在運(yùn)行期間?
設(shè)計(jì)和實(shí)現(xiàn)一個(gè)健壯的測試環(huán)境,確保每個(gè)依賴項(xiàng)都有自己的測試覆蓋率,并且使用專門針對(duì)環(huán)境的用例進(jìn)行測試。以下是一些推薦的測試策略:
- 使用集成測試執(zhí)行故障注入ーー驗(yàn)證系統(tǒng)能否在任何依賴關(guān)系發(fā)生故障時(shí)幸存下來。
- 進(jìn)行災(zāi)難測試以識(shí)別弱點(diǎn)或隱藏的依賴關(guān)系。記錄后續(xù)行動(dòng),以糾正發(fā)現(xiàn)的bug。
- 故意讓系統(tǒng)過載,看看它是如何退化的。無論如何,系統(tǒng)對(duì)負(fù)載的響應(yīng)都將被測試; 最好是自己執(zhí)行這些測試,而不是將負(fù)載測試留給用戶。
容量規(guī)劃
確保每個(gè)依賴項(xiàng)都得到了正確的供給,如果成本可以接受,就過度供給。如果可能,將依賴項(xiàng)的配置標(biāo)準(zhǔn)化,以限制子系統(tǒng)之間的不一致性,并避免一次性的故障模式。
檢測、故障排除和診斷問題要盡可能簡單,有效的監(jiān)測是能夠及時(shí)發(fā)現(xiàn)問題的關(guān)鍵組成部分。診斷具有嚴(yán)重依賴關(guān)系的系統(tǒng)是困難的,但總是有一個(gè)不需要操作員就可以減輕故障的方案。
期待隨著規(guī)模而來的變化,當(dāng)在一臺(tái)機(jī)器上以二進(jìn)制文件開始的服務(wù)在更大的規(guī)模上部署時(shí),可能會(huì)有許多明顯或不明顯的依賴關(guān)系。每一個(gè)規(guī)模的數(shù)量級(jí)都會(huì)暴露出新的瓶頸, 不僅僅是自己服務(wù),還有所依賴的服務(wù)。考慮一下,如果依賴項(xiàng)不能像所需要的那樣快速擴(kuò)展,將會(huì)發(fā)生什么。
還要注意,系統(tǒng)依賴關(guān)系會(huì)隨著時(shí)間的推移而發(fā)展,并且依賴關(guān)系的列表可能會(huì)隨著時(shí)間的推移而增長。在基礎(chǔ)設(shè)施方面,一個(gè)典型的設(shè)計(jì)是建立一個(gè)不需要重大變更就可以擴(kuò)展到初始目標(biāo)負(fù)載10倍的系統(tǒng)。
結(jié)束語
服務(wù)的用性并不高深莫測,它只是一個(gè)百分比的數(shù)字。服務(wù)可用性的指標(biāo)(例如99.99%)往往令人不安,但并非不可實(shí)現(xiàn)。提供超過四個(gè)9的服務(wù)可用性,不是通過超出常人的智慧,而是通過不斷地完善規(guī)則形成最佳實(shí)踐,并且全面應(yīng)用。