天天看點

軟體系統設計(2)-可靠性

每人都有直覺認識,即什麼代表可靠或不可靠。對于軟體,典型的期望包括:

  • 應用程式執行使用者所期望的功能
  • 可容忍使用者出現錯誤或不正确的軟體使用方法
  • 性能可應對典型場景、合理負載壓力和資料量
  • 系統可防止任何未經授權的通路和濫用

上述目标都支援才算 “正常工作”,那麼我們可以認為可靠性代表:即使發生某些錯誤,系統仍可繼續正常工作。

可能出錯的事情稱為錯誤( fault )或故障,系統可應對錯誤稱為容錯(fault tolerant)或彈性(resilient)。 前面那個詞略顯誤導 :似乎暗示系統可容忍各種可能的故障類型,這顯然不可能。誇張的,如果整個地球(及其上的所有伺服器)都被黑洞吞噬,要在這個級别容錯就意味着必須在宇宙範圍内進行系統備援。試想,這将是天價預算。是以,容錯總是指特定類型故障,系統才更有實際意義。

故障與失效(failure)不完全一緻。故障通常被定義為元件偏離其正正常格,而失效意味系統作為 體停止,無法向使用者提供所需服務。我們不可能将故障機率降到0,是以通常設計容錯機制來避免從故障引發系統失效。

在這種容錯系統,為了測試,可有意提高故障機率,例如随機殺程序,確定系統仍保持健壯。很多關鍵bug正是由于錯誤處理不當造成的。以此,持續檢驗、測試系統容錯機制,增加對真實發生故障時應對的信心。

雖然傾向于容忍故障而非預防故障,但存在“預防勝于治療”的情況,安全問題就是,例如若攻擊者破壞系統竊取敏感資料,則該事件造成的影響顯然無法撤銷。而本系列針對那些影響可以被消除的故障類型。黑客大佬操作,不在談論範圍。

2.1 硬體故障

考慮系統故障時,硬體故障易想到硬碟崩潰,記憶體故障,停電,甚至拔網線。任何與大型資料中心合作過的人都可以告訴你,當有很多機器時,這類事情遲早發生。

研究證明硬碟平均無故障時間( MTTF )約為 10~50 年。是以,在一個包括10000 個磁盤的存儲叢集中,應預期平均每天有1個磁盤故障。

第一反應通常是為硬體添加備援來減少系統故障率。 例如對磁盤配置RAID ,伺服器配備雙電源,甚至熱插拔CPU,資料中心添加備用電源、發電機等。

當一個元件故障,備援元件可快速接管,之後再更換失效元件。這并不能完全防止硬體故障所引發的失效,但還是普遍采用,且在實際中确實可以讓系統持續運作數年。

最近,硬體備援方案對大多數應用場景還夠,使得單台機器完全失效機率降非常低。隻要可以将備份迅速恢複到新機器,故障停機時間在大多數應用中并非災難性。而多機備援則隻對少數關鍵應用更有意義,對于這些應用,高可用性絕對必要。

但随資料量和應用計算需求增加,更多應用可運作在大規模機器,随之來的硬體故障率呈線性增長。例如,對某些雲平台(AWS),由于系統強調總體靈活性與彈性,而非單台機器可靠性,虛拟機執行個體會在事先無告警情況下出現無法通路問題。是以,通過軟體容錯來容忍多機失效,成為新方案或至少成為硬體容錯的補充。這樣的系統更具操作便利性,例如當需要重新開機時為 os 打更新檔,可以每次給一個節點打更新檔後重新開機,無需同時下線整個系統。

硬體故障之間一般互相獨立:一台機器的磁盤故障不意味着另一台機器的磁盤也失效。除非存在某種弱相關(例如共性原因,伺服器機架溫度過高),不然不太會大量硬體元件同時失效。

2.2 軟體錯誤

系統内的軟體問題。這些故障事先更加難以預料,而且因為節點之間由軟體關聯,因而往往會導緻更多系統故障,如:

  • 由于軟體錯誤,導緻當輸入特定值時,應用伺服器總崩潰。 如 2012.6.30發生閏秒,由于Linux核心中的bug ,導緻了很多應用程式在該時刻發生挂起
  • 應用程序使用了某些共享資源如CPU 、記憶體、磁盤或網絡帶寬 ,但卻不幸失控跑飛了
  • 系統依賴于某些服務,但該服務突然變慢,甚至無響應或開始傳回異常響應
  • 級聯故障,其中某元件的小故障觸發另一個元件故障,進而引發更多系統問題

導緻軟體故障的bug通常會長時間處于引而不發的狀态,直到碰到特定觸發條件。這意味着系統軟體其實對使用環境存在某種假設,而這種假設多數情況都可以滿足,但特定情況下,假設不再成立。

軟體系統問題有時無快速解決辦法,隻能仔細考慮很多細節,包括認真檢查依賴的假設與系統之間的互動 ,進行全面測試,程序隔離,允許程序崩潰并自動重新開機,反複評估,監控并分析生産環節的行為表現等。如果系統提供某些保證,例如,MQ 的輸出消息數量=輸入消息的數量, 則能不斷檢查确認,若發現差異則告警。

2.3 人為失誤

設計和建構軟體系統是由人完成, 也由人運維,是人就無法做到萬無一失。例如,一項針對大型網際網路服務的調查發現,運維者的配置錯誤居然是系統下線的首要原因,而硬體問題(伺服器或網絡)僅在10%~25%的故障中有所影響。

  • 以最小出錯方式設計系統

    例如,精心設計的抽象層、 API 以及管理界面,使“做正确的事”很輕松,但搞壞很複雜。但若限制過多,人們就會想法繞過它,這會抵消其正面作用。是以根本還是 trade off

  • 盡力分離最易出錯的地方、易引發故障的接口

    尤其是提供一個功能齊全但非生産用的沙箱環境,讓人能放心嘗試、體驗,包括導人真實資料,即使故障,也不會影響真實使用者。

  • 充分測試,從各單元測試到全系統內建測試以及手動測試

    自動化測試已被廣泛使用,對于覆寫正常操作中很少出現的邊界條件等很重要

  • 出現人為失誤時,提供快速的恢複機制以減少故障影響。例如,快速復原配置改動,滾動釋出新代碼(這樣任一錯誤僅影響小部分使用者),并提供校驗資料的工具(防止舊的計算方式錯誤)
  • 設定詳細清晰的監控子系統,包括性能名額和錯誤率。在其他行業稱為遙測(Telemetry), 一旦火箭離地,遙測對跟蹤運作和了解故障很重要。監控能給我們發送告警信号,并檢查是否存在假設不成立或違反限制條件等。這些檢測名額對診斷問題很有用。
  • 推行管理流程并定期教育訓練

    重要且複雜,看每個團隊管理者自己規劃。

2.4 可靠性的重要性

繼續閱讀