1. 程式人生 > >《The Design of a Practical System for Fault-Tolerant Virtual Machines》論文研讀

《The Design of a Practical System for Fault-Tolerant Virtual Machines》論文研讀

# VM-FT 論文研讀 **說明**:本文為論文 **《The Design of a Practical System for Fault-Tolerant Virtual Machines》** 的個人理解,難免有理解不到位之處,歡迎交流與指正 。 **論文地址**:[VM-FT 論文](https://github.com/XutongLi/Learning-Notes/blob/master/Distributed_System/Paper_Reading/VM-FT/vm-ft.pdf) > 本文的總結包括論文內容以及 [**MIT6.824 Lec4**](https://pdos.csail.mit.edu/6.824/notes/l-vm-ft.txt) 中的授課內容,其中包含了論文中沒有提及的一些細節 。 *** ## 1. 前言 本論文主要介紹了一個用於提供 `容錯虛擬機器 (fault-tolerant virtual machine)` 的企業級商業系統,該系統包含了兩臺位於不同物理機的虛擬機器,其中一臺為 **primary** ,另一臺為 **backup** ,**backup** 備份了 **primary** 的所有執行 。當 **primary** 出現故障時,**backup** 可以上線接管 **primary** 的工作,以此來提供容錯 。 *** ## 2. 主/備份方法 實現容錯伺服器的一種常見方法是 `主/備份方法` 。**backup** 狀態與 **primary** 保持相同,**primary** 故障時,使用 **backup** 進行接管。並且以這種方式將故障對於 **client** 隱藏,不會丟失任何資料 。 主/備份方法中保持狀態同步的方法有以下兩種: - `State transfer`:**primary** 持續地將所有狀態( 包括 *CPU*、記憶體和 *I/O* 裝置 )變化傳送給 **backup** 。這種方法所需頻寬非常大 。 - `Replicated state machine`:將伺服器抽象為確定性狀態機 。讓 **primary** 和 **backup** 按照相同的順序執接收相同的輸入請求,對於不確定的操作使用額外的協調來保證主備狀態一致 。這種方法實現複雜,但是所需頻寬較小 。本文便是採用這種方法 。 > 確定性狀態機:多臺狀態機從相同的初始狀態開始、按照相同的順序執行相同的操作,則它們的最終狀態是一致的 。 > > 狀態機方法允許 **primary** 和 **backup** 進行更大的物理分離 。 *** ## 3. 虛擬機器的選擇 **虛擬機器 ( virtual machine )** 不是通過硬體來啟動作業系統,而是在硬體之上會呼叫一個 **Hypervisor** ,**Hypervisor** 的工作實際上就是在硬體上模擬多個虛擬計算機 。**Hypervisor** 上執行著 **GuestOS** ,再上面是應用程式 。 ![](https://img2020.cnblogs.com/blog/2035097/202007/2035097-20200705125608572-72262889.png) > **Hypervisor** 即 **virtual machine monitor** ( **VMM** ) > > **GuestOS** 即執行在虛擬機器中的作業系統,與之對應的是 **HostOS** ,指物理機裡的作業系統 。 在物理機上確保確定性執行是困難的,因為其會接收到很多不確定輸入( 如定時器中斷 ),因此可以採用虛擬機器,**Hypervisor** 對硬體進行模擬和控制,可以捕獲到這些不確定輸入的所有相關資訊,使得 **backup** 可以重放這些不確定輸入 。 > 因為我們討論的故障主要是指伺服器故障,因此不同的虛擬機器要位於不同的物理機上,否則便失去了容錯的意義 。 *** ## 4. 基本設計 ### 4.1 基本架構 ![](https://img2020.cnblogs.com/blog/2035097/202007/2035097-20200705125616934-1182521336.png) **Primary VM** 和 **Backup VM** 執行在同一網路環境中的不同物理機上,兩者可以訪問同一個 **Disk Server** ,即論文當中的 **Shared Disk** 。 只有 **Primary VM** 向外界通知自己的存在,因此所有網路輸入或其他輸入(磁碟、滑鼠、鍵盤)都進入 **Primary VM** 。 **Primary VM** 接收的所有輸入都通一個稱為 **Logging Channel** 的網路連線傳送到 **Backup VM** ,以保證兩者狀態相同 。**Backup VM** 的指令執行結果與 **Primary VM** 的結果相同,但只有 **Primary VM** 返回給 **client** 結果,**Backup VM** 的結果會被 **Hypervisor** 丟棄 。 系統使用 **Primary VM** 和 **Backup VM** 之間的心跳包和 **Logging Channel** 上的流量監控來檢測 **Primary VM** 或 **Backup VM** 是否是失效 。此外,必須確保 **Primary VM** 和 **Backup VM** 中只有一個接管執行 。 > 事實證明,這些主備虛擬機器並不適用於本地磁碟,而是會和某些磁碟伺服器進行通訊。( 論文中並未提到這點 ) ### 4.2 確定性重放 由上文可知,**VM-FT** 建模為確定性狀態機的複製 。對於一系列輸入,對 **primary** 的執行進行記錄並確保 **backup** 以相同方式執行的基本技術稱為 **確定性重放** 。 **primary** 的操作中包含了 `確定性操作` 和 `不確定性操作` 。確定性操作在 **primary** 和 **backup** 上的執行結果是相同的,不確定性操作包括: - 來自 **client** 的輸入,這些輸入可能在任何時候到達 - 非確定性指令,如隨機數生成指令、在不同時間獲得時間的指令、生成裝置唯一 *ID* 的指令等 - 多核併發,服務中的指令會在不同的核上何以某種方式交錯執行,執行順序不可預測( 本論文中的方法只針對單核處理器,不解決此問題 ) > 前兩種不確定性操作會在 **Logging Channel** 中傳送 確定性重放記錄 **primary** 的輸入和 **primary** 執行相關的所有可能的不確定性,記錄在 **log entry** 流中,傳送給 **backup** 並使其重放 : - 對於不確定的操作,將記錄足夠的資訊,確保其在 **backup** 上重新執行能夠得到相同的狀態和輸出 - 對於不確定的事件,如定時器或 *IO* 完成中斷,事件發生的確切指令會被記錄下來,重放時,**backup** 會在指令流中相同的位置重放這些事件 **log entry** 中應該包含了: - 事件發生時的指令號 - 型別,指明是網路輸入還是其他指令 - 資料:資料包裡的資料,若是不確定指令,則此資料是該指令在 **primary** 的執行結果,所以 **backup** 就可以對該指令提供與 **primary** 相同的執行結果 > 不確定性指令執行過程: > > ( 即使 **primary** 和 **backup** 在同一狀態,執行不確定性指令後也會產生不同結果 ) > > **primary**: > > - **Hypervisor** 在 **primary** 執行指令時設定中斷 > - **Hypervisor** 執行指令並記錄結果 > - 傳送結果和指令序號到 **backup** > > **backup**: > > - **Hypervisor** 讀 **log entry** ,在該指令序號處設定中斷 > - **Hypervisor** 應用從 **primary** 執行得到的結果,自己產生的結果被丟棄,從而保證主備一致 ### 4.3 輸出要求和規則 `輸出要求`:若 **primary** 發生故障後且 **backup** 接管後,**backup** 必須以一種與原 **primary** 已傳送到外部的輸出完全一致的方式執行 。 只要滿足了輸出要求,故障轉移就不會丟失外部可見的狀態或資料,**client** 也不會注意到 **server** 服務中斷或有不一致 。 可能有一種特殊情況會發生:如果 **primary** 在執行輸出操作後立即故障,**backup** 在完成接管之前,可能還未執行到同樣的輸出操作,就被其他不確定事件所影響( 如計時中斷 ),這樣 **backup** 就無法以與 **primary** 發生故障時的相同狀態上線,為此提出了 **輸出規則** 。 `輸出規則`:**primary** 必須延後將輸出傳送到外部世界的動作,直到 **backup** 已經接收並確認 與產生該輸出的操作相關 的 **log entry** 。 基於輸出規則,**primary** 和 **backup** 的互動如下圖所示: ![](https://img2020.cnblogs.com/blog/2035097/202007/2035097-20200705125629005-432201283.png) **primary** 等待來自 **backup** 的 `ACK` 時,不會停止執行,只需要延遲輸出的傳送 ( 非同步執行 )。 **一些故障發生情況**: - 如果 **primary** 在收到 **ACK** 之前故障,它不會返回結果給 **client** ,由於 **backup** 的輸出會被丟棄,所以兩者在 **client** 看來是一致的,即未收到 **server** 回覆 。 - 如果 **primary** 在傳送輸出後故障,**backup** 在接管後也執行傳送,**client** 會收到兩次輸出 。但是這種情況不會造成不良後果,因為對於 *TCP* 連線來說,它會處理重複的資料包;對於磁碟來說,會對同一塊儲存區覆蓋寫入 。 ### 4.4 檢測和響應故障 兩種 **VM** 都有可能發生故障: - 如果是 **backup** 故障,**primary** 將停止在 **logging channel** 上傳送 **log entry** ,並繼續執行 - 如果是 **primary** 故障,**backup** 會繼續重放 **log entries** ,重放結束後 **上線** 成為 **primary** ,此時,它可以向外界生成輸出 。 **VM-FT** 檢測故障的方式有 *UDP* 心跳檢測和監控 **logging channel** 中的流量以及 **backup** 傳送給 **primary** 的 **ACK** 。若心跳或日誌流量停止的時間超過了特定超時時間( 大約幾秒 ),就會宣告故障 。 這樣的故障檢測方法,在有網路故障時,容易遇到 **split-brain** 問題:即 **primary** 和 **backup** 之間通訊斷開,但此時 **primary** 還在執行,若 **backup** 此時上線,會造成兩者同時執行的問題,可能會導致資料損壞 。 為解決 **split-brain** 問題,使 **Disk Server** 支援 **atomic test-and-set** 測試,即在 **Disk Server** 上維護一個 **flag** ,第一個訪問此 **flag** 的 **VM** 會成為 **primary** ,第二個訪問此 **flag** 的 **VM** 不會上線 。( 類似於鎖 ) ### 4.5 恢復冗餘 **primary** 發生故障,**backup** 上線時,在新的物理機上建立 **backup** ,恢復冗餘,繼續進入到容錯狀態 。 **VM vSephere** 實現了一個叢集服務,用於維護管理和資源資訊。當發生故障時,叢集服務根據資源使用情況和其他約束來確定新的 **backup** 的最佳伺服器,並將其複製成為 **backup** 。其結果是,**VM-FT** 通常可以在伺服器發生故障後幾分鐘內重新建立冗餘,而在執行容錯轉移時不會產生任何明顯的中斷 。 ### 4.6 管理日誌通道 **Hypervisor** 為 **primary** 和 **backup** 分別維護了一個 **log buffer** ,如下圖所示: ![](https://img2020.cnblogs.com/blog/2035097/202007/2035097-20200705125642985-1713812573.png) - **primary** 執行時,會將 **log entry** 生成到 **log buffer** 當中 - **primary** 的 **log buffer** 會盡快將內容清除到 **logging channel** - **log entry** 一到達 **logging channel** 就會被讀到 **backup** 的 **log buffer** - **backup** 傳送 **ACK** 給 **primary** 若 **primary** 的 **log buffer** 已滿,**primary** 會等待;若 **backup** 的 **log buffer** 已空,**backup** 會等待 。 另外,為了防止 **backup** 的重放落後太多,在傳送和確認 **log entry** 的協議中,會發送附加資訊來確定 **primary** 和 **backup** 之間的 `實時執行延遲` ,通常小於 *100ms* 。若 **backup** 出現明顯的延遲,**VM-FT** 會通知排程器給它分配更少的 *CPU* 資源,從而降低 **primary** 的速度;若 **backup** 追起來了,逐漸增加 **primary** 的速度 。 ### 4.7 磁碟的記憶體訪問競爭 磁碟操作可能和 **VM** 中的應用程式或 *OS* 存在記憶體訪問競爭 。 這種競爭在網路資料包或磁碟塊到達 **primary** 時產生 。在沒有 **VM-FT** 的情況下,相關硬體會通過 **DMA** 將該資料複製到記憶體中 。若 *APP/OS* 也在同時讀取這塊記憶體。那麼對於 **primary** 和 **backup** ,由於時間上的微小差異,可能一個在 **DMA** 之前讀取,一個在 **DMA** 之後讀取,就導致了不一致 。 解決方法是使用 `bounce buffer` ,它的大小與磁碟操作訪問的記憶體大小相同 。**primary** 的 **Hypervisor** 首先複製網路資料或磁碟塊到 **bounce buffer** ,此時 **primary** 無法訪問它 ,**Hyperbisor** 中斷 **primary** 使其暫停執行,並記錄中斷的指令 。然後 **Hypervisor** 將 **bounce buffer** 中的內容複製到 **primary** 的記憶體 ,並讓其繼續執行 。通過 **logging channel** 將資料送到 **backup** 之後,**backup** 的 **Hypervisor** 在相同指令處中斷 **backup** ,將資料複製到 **backup** 的記憶體後,最後恢復 **backup** 的執行 。 *** ## 5. 機器級複製和應用級複製 ### 5.1 機器級複製 `機器級複製` 即複製了記憶體中和暫存器中的所有內容 。優點為可以在 **VM-FT** 上執行任何軟體;缺點為不夠高效 ### 5.2 應用級複製 `應用級複製` 即 **primary** 僅傳送 **high-level** 操作給 **backup** 。如資料庫,同步的狀態僅為資料庫內容,不是所有的記憶體內容,操作僅為資料庫命令( **get** 、**put** 之類 ),沒有網路包或中斷 。**GFS** 使用的也是應用級複製 。 優點:更少的細粒度同步、更低的開銷 缺點:應用程式必須理解系統的容災 *** ## 6. VM-FT 和 GFS 容錯的比較 **VM-FT** 備份的是 `計算`,可以用它為任何已有的網路伺服器提供容錯性。**VM-FT** 提供了相當嚴謹的一致性而且對 **client** 和 **server** 都是透明的。例如,你可以利用 **VM-FT** 為已有的郵件伺服器提供容錯性。 相比之下,**GFS** 只為 `儲存` 提供容錯性。因為 **GFS** 只針對一種簡單的服務提供容錯性,它的備份策略會比 **VM-FT** 更為高效:例如,**GFS** 不需要使中斷髮生在所有的副本的同一指令上。**GFS** 通常只會被用作一個對外提供完整容錯服務的系統的一部分:例如,**VM-FT** 本身也依賴了一個在主備虛擬機器間共享的有容錯性的儲存服務,而你則可以用類似於 GFS 的東西來實現這個模組( 雖然從細節上來講 **GFS** 不太適用於 **FT** )