每一個人對于什么東西是可靠的或者不可靠的都有一個直觀的概念。對于軟件來講,典型的期望包括:
????? 應(yīng)用程序提供用戶期望的功能;
????? 它可以容忍用戶犯錯或者以意想不到的方式使用軟件;
????? 在預(yù)期的負(fù)載和數(shù)據(jù)量下,其性能足以滿足所需的用例;
????? 系統(tǒng)防止任何未經(jīng)授權(quán)的訪問和濫用。
如果所有的這些東西表示“正確的工作”,那么我們就可以理解可靠性的含義,大致來講就是,“即使出了差錯,也能夠正確的工作”。
那些可能出錯的東西被稱為錯誤,而預(yù)測錯誤并能應(yīng)付的系統(tǒng)被稱為容錯。前一種說法有點(diǎn)誤導(dǎo)人:它表明我們可以使一個系統(tǒng)容忍任何可能的錯誤,事實(shí)上這是不可能的。如果整個地球(及其所有的服務(wù)器)都被黑洞吞噬,那么對該故障的容忍將需要在太空上進(jìn)行主機(jī)托管。所以說只容忍某些類型的錯誤才是有意義的。
同時請注意錯誤與失敗是不一樣的。錯誤通常被定義為系統(tǒng)的一個組件偏離其規(guī)范,而失敗則是當(dāng)系統(tǒng)作為一個整體停止向用戶提供所需的服務(wù)時。把錯誤的概率降低到零是不可能的;因此,通常最好設(shè)計(jì)一種容錯機(jī)制,以防止故障。在這本書中,我們將介紹幾種構(gòu)建可靠系統(tǒng)的技術(shù)。
在這樣的容錯系統(tǒng)中,通過不事先警告地隨機(jī)殺死單個進(jìn)程,從而增加錯誤的幾率是有意義的。許多關(guān)鍵的錯誤實(shí)際上是由于比較弱的錯誤處理;通過故意地誘發(fā)錯誤,你可以確保容錯機(jī)制不斷地被執(zhí)行和測試,這可以增加你對錯誤在自然發(fā)生時正確處理的信心。Netflix的Chaos Monkey就是這種方法的一個例子。
盡管我們通常更喜歡容忍錯誤而不是預(yù)防錯誤,但有些情況下,預(yù)防勝于治療(例如,因?yàn)闆]有治愈方法)。例如,安全問題就是這樣的:如果攻擊者破壞了系統(tǒng)并獲得了對敏感數(shù)據(jù)的訪問權(quán),那么這個事件就不能被撤銷。但是,在本書中,我們主要討論的是可以被治愈的錯誤,如下幾節(jié)所述。
硬件故障
當(dāng)我們想到導(dǎo)致系統(tǒng)故障的原因時,我們很快就會想到硬件故障。硬盤崩潰,RAM出現(xiàn)故障,電網(wǎng)停電,有人錯誤的拔掉了網(wǎng)絡(luò)電纜等。任何做過大型數(shù)據(jù)中心的人都可以告訴你,當(dāng)你擁有大量的機(jī)器時,這些事情就會發(fā)生。
據(jù)報(bào)道,硬盤有一個平均失效時間(MTTF),大約為10到50年。因此,在一個有10000個磁盤的存儲集群上,理論上平均每天都會有一個磁盤掛掉。
我們的第一反應(yīng)通常是為單個硬件組件增加冗余,以降低系統(tǒng)的故障率。磁盤可以在RAID配置中設(shè)置,服務(wù)器可能有雙電源和可熱插拔的cpu,而數(shù)據(jù)中心可能有電池和柴油發(fā)電機(jī)作為備用電源。當(dāng)一個組件掛掉時,冗余組件可以替換上去。這種方法不能完全防止硬件問題導(dǎo)致的故障,但是它很好理解,并且常??梢允挂慌_機(jī)器連續(xù)運(yùn)行數(shù)年。
直到現(xiàn)在,對于大多數(shù)應(yīng)用程序來說,硬件組件的冗余已經(jīng)足夠了,因?yàn)樗沟脝蝹€機(jī)器的整體故障已經(jīng)相當(dāng)?shù)土?。只要您能夠相?dāng)快地將備份恢復(fù)到新機(jī)器上,在大多數(shù)應(yīng)用程序中出現(xiàn)故障的停機(jī)時間并不是災(zāi)難性的。因此,只有少數(shù)對高可用性有絕對需要的應(yīng)用程序需要多機(jī)器冗余。
然而,隨著數(shù)據(jù)量和應(yīng)用程序的計(jì)算需求增加,越來越多的應(yīng)用程序開始使用更大數(shù)量的機(jī)器,這在一定程度上增加了硬件故障的發(fā)生率。此外,在一些云平臺上,如Amazon Web Services(AWS),也存在在沒有警告的情況下,虛擬機(jī)實(shí)例變得不可用,因?yàn)檫@些平臺的設(shè)計(jì)目的是優(yōu)先考慮靈活性和彈性,而不是單機(jī)可靠性。
因此,通過使用軟件容錯技術(shù)或硬件冗余來實(shí)現(xiàn)對整個機(jī)器的故障轉(zhuǎn)移。這種系統(tǒng)也有操作優(yōu)勢:如果您需要重啟機(jī)器時,單服務(wù)器系統(tǒng)需要按計(jì)劃停機(jī)(例如,應(yīng)用操作系統(tǒng)安全補(bǔ)丁),而一個能夠容忍機(jī)器失敗的系統(tǒng)可以修補(bǔ)一個節(jié)點(diǎn)而保證整個系統(tǒng)不停機(jī)(滾動升級,詳見第4章)。
軟件錯誤
我們通常認(rèn)為硬件故障是隨機(jī)而獨(dú)立的:一臺機(jī)器的磁盤故障并不意味著另一臺機(jī)器的磁盤將會掛掉。雖然可能存在弱相關(guān)性(例如,由于一個常見原因,比如服務(wù)器機(jī)架中的溫度),但是不太可能出現(xiàn)大量硬件組件同時出現(xiàn)故障的情況。
而另一類錯誤是系統(tǒng)中的系統(tǒng)錯誤。這樣的錯誤是難以預(yù)料的,而且由于它們是跨節(jié)點(diǎn)的,它們往往比硬件故障更容易導(dǎo)致系統(tǒng)故障。舉幾個例子:
?????? 一種軟件錯誤,它會導(dǎo)致應(yīng)用程序服務(wù)器的每個實(shí)例在特定的輸入錯誤的情況下崩潰。例如,由于linux內(nèi)核中的一個BUG--2012年6月30日的閏秒,導(dǎo)致許多應(yīng)用程序同時掛掉。
?????? 一個失控的進(jìn)程,占用了一些共享資源——CPU、內(nèi)存、磁盤空間或網(wǎng)絡(luò)帶寬。
?????? 系統(tǒng)依賴的服務(wù)慢下來,變得無響應(yīng)或者返回崩潰的響應(yīng)。
?????? 級聯(lián)故障,其中一個組件的小故障觸發(fā)另一個組件的錯誤,從而觸發(fā)進(jìn)一步的故障。
導(dǎo)致這類軟件故障的BUG通常會休眠很長一段時間,直到它們被一組不同尋常的環(huán)境所觸發(fā)。在這種情況下,軟件會對其環(huán)境做出某種假設(shè),而這種假設(shè)通常是正確的,但它最終會因?yàn)槟承┰蚨V埂?/p>
軟件中系統(tǒng)故障的問題可能沒有一個快速的解決方案。但很多小事情都可以幫助我們:仔細(xì)思考系統(tǒng)中的假設(shè)和交互;全面測試;進(jìn)程隔離;允許進(jìn)程崩潰和重新啟動;監(jiān)控和分析生產(chǎn)中的系統(tǒng)行為。如果系統(tǒng)被期望提供一些保證(例如,在消息隊(duì)列中,傳入消息的數(shù)量等于傳出消息的數(shù)量),它可以在運(yùn)行時不斷地檢查自己,如果發(fā)現(xiàn)有差異,則會發(fā)出警告。
人為錯誤
人們設(shè)計(jì)和構(gòu)建軟件系統(tǒng),而保持系統(tǒng)運(yùn)行的操作人員也是人。即使他們的目的非常好,人們也知道人是不可靠的。例如,一項(xiàng)大型互聯(lián)網(wǎng)服務(wù)的研究發(fā)現(xiàn),運(yùn)營商的配置錯誤是導(dǎo)致服務(wù)中斷的主要原因,而硬件故障(服務(wù)器或網(wǎng)絡(luò))導(dǎo)致的宕機(jī)比率只有10-25%。
那我們?nèi)绾问刮覀兊南到y(tǒng)可靠呢?好的系統(tǒng)通常結(jié)合了以下幾種方法:
????? 設(shè)計(jì)系統(tǒng)的時候來不斷的縮小犯錯誤的機(jī)會。例如,設(shè)計(jì)良好的抽象、api和管理界面使得做“正確的事情”變得容易,并且阻止“錯誤的事情”。但是,如果接口設(shè)計(jì)的過于嚴(yán)格,人們也許就會盡量避免使用它或者繞過它,這就降低了它們的用處,所以說,這是一種難以把握的平衡。
????? 解耦因人為錯誤可能導(dǎo)致失敗的地方。非常特殊的,可以提供一個非生產(chǎn)沙盒環(huán)境,讓人們可以在不影響真實(shí)用戶的情況下,使用真實(shí)的數(shù)據(jù)進(jìn)行安全的探索和試驗(yàn)。
????? 全面的測試,從單元測試到全系統(tǒng)集成測試和手工測試。自動化測試被廣泛使用,這很好理解,特別是對于在正常操作中很少出現(xiàn)的個別案例來說是非常有價值的。
????? 允許從人為錯誤中快速而方便地恢復(fù),從而最小化故障的影響。例如,快速回滾配置更改,逐步部署新代碼(這樣,任何意外的bug只會影響一小部分用戶),并提供重新計(jì)算數(shù)據(jù)的工具(以防原來的計(jì)算不正確)。
????? 建立詳細(xì)和清晰的監(jiān)控,例如性能指標(biāo)和錯誤率。在其他工程學(xué)科中,這被稱為遙測技術(shù)。(一旦火箭離開地面,遙測技術(shù)對于跟蹤正在發(fā)生的事情,以及對故障的理解是必不可少的。)監(jiān)控可以向我們展示早期預(yù)警信號,并允許我們檢查是否有任何假設(shè)或約束被違反。當(dāng)出現(xiàn)問題時,metrics在診斷問題時是非常寶貴的。
????? 執(zhí)行良好的管理實(shí)踐和培訓(xùn)。
可靠性有多重要?
可靠性不僅適用于核電站和空中交通控制軟件——其實(shí)更普通的應(yīng)用也期望可靠地運(yùn)行。商業(yè)應(yīng)用程序中的bug會導(dǎo)致生產(chǎn)力損失(如果數(shù)據(jù)被錯誤地報(bào)告,則會帶來法律風(fēng)險(xiǎn)),而電子商務(wù)網(wǎng)站的宕機(jī)也會造成巨大的損失,收入和聲譽(yù)都會受到損害。
即使在“非關(guān)鍵”的應(yīng)用程序中,我們也要對用戶負(fù)責(zé)。假設(shè)有一位家長在你的照片應(yīng)用程序中存儲了他們孩子的所有照片和視頻。如果數(shù)據(jù)庫突然被破壞了,他們會怎么想?他們知道如何從備份中恢復(fù)嗎?
有些情況下,我們可能會選擇犧牲可靠性以減少開發(fā)成本(例如,在開發(fā)一個未經(jīng)市場證實(shí)的原型產(chǎn)品)或運(yùn)營成本(例如,非常狹窄的利潤率)---但是我們也應(yīng)該意識到我們正在偷工減料。