MD 叢集

叢集 MD 是一個用於叢集的共享裝置 RAID,它支援兩個級別:raid1 和 raid10(有限支援)。

1. 磁碟格式

每個叢集節點都使用單獨的寫意圖點陣圖。點陣圖記錄了該節點上可能已開始但尚未完成的所有寫入操作。磁碟佈局如下:

0                    4k                     8k                    12k
-------------------------------------------------------------------
| idle                | md super            | bm super [0] + bits |
| bm bits[0, contd]   | bm super[1] + bits  | bm bits[1, contd]   |
| bm super[2] + bits  | bm bits [2, contd]  | bm super[3] + bits  |
| bm bits [3, contd]  |                     |                     |

在“正常”執行期間,我們假設檔案系統確保在任何給定時間只有一個節點寫入任何給定塊,因此一個寫入請求將:

  • 設定相應的位(如果尚未設定)

  • 將寫入提交到所有映象

  • 在超時後安排清除該位。

讀取操作正常處理。檔案系統負責確保一個節點不會從另一個節點(或同一節點)正在寫入的位置讀取。

2. 用於管理的 DLM 鎖

有三組用於管理裝置的鎖:

2.1 點陣圖鎖資源 (bm_lockres)

bm_lockres 保護各個節點點陣圖。它們命名格式為 node 1 對應 bitmap000,node 2 對應 bitmap001,以此類推。當一個節點加入叢集時,它以 PW 模式獲取鎖,並在該節點作為叢集一部分的生命週期內保持此狀態。鎖資源編號基於 DLM 子系統返回的槽號。由於 DLM 的節點計數從一開始,而點陣圖槽從零開始,因此從 DLM 槽號中減去一以得到點陣圖槽號。

特定節點的點陣圖鎖的 LVB (Lock Value Block) 記錄了該節點正在重新同步的扇區範圍。其他節點不得寫入這些扇區。這在有新節點加入叢集時使用。

2.2 訊息傳遞鎖

每個節點在開始或結束重新同步以及進行元資料超級塊更新時都必須與其他節點通訊。此通訊透過三個鎖進行管理:“token”、“message”和“ack”,以及其中一個“message”鎖的鎖值塊 (LVB)。

2.3 新裝置管理

一個單獨的鎖:“no-new-dev”用於協調新裝置的新增——這必須在整個陣列中同步。通常,所有節點在此裝置上都持有併發讀鎖。

3. 通訊

訊息可以廣播到所有節點,傳送方等待所有其他節點確認訊息後才繼續。一次只能處理一條訊息。

3.1 訊息型別

有六種訊息型別進行傳遞:

3.1.1 METADATA_UPDATED

通知其他節點元資料已更新,節點必須重新讀取 md 超級塊。此操作是同步執行的。它主要用於訊號裝置故障。

3.1.2 RESYNCING

通知其他節點重新同步已啟動或結束,以便每個節點可以暫停或恢復該區域。每個 RESYNCING 訊息都標識了傳送節點即將重新同步的裝置範圍。這會覆蓋該節點之前的任何通知:每個節點一次只能重新同步一個範圍。

3.1.3 NEWDISK

通知其他節點有裝置正在新增到陣列。訊息包含該裝置的識別符號。詳見下文。

3.1.4 REMOVE

一個失敗或備用裝置正在從陣列中移除。裝置槽號包含在訊息中。

3.1.5 RE_ADD

一個失敗的裝置正在重新啟用——假設它已被確定再次工作。

3.1.6 BITMAP_NEEDS_SYNC

如果一個節點在本地停止但點陣圖不乾淨,則通知另一個節點接管重新同步的所有權。

3.2 通訊機制

DLM LVB 用於叢集節點內部通訊。為此目的使用了三種資源:

3.2.1 token

保護整個通訊系統的資源。持有 token 資源的節點被允許進行通訊。

3.2.2 message

承載通訊資料的鎖資源。

3.2.3 ack

獲取此資源意味著訊息已被叢集中所有節點確認。資源的 BAST (Batch Acknowledge State Table) 用於通知接收節點有節點想要通訊。

演算法如下:

  1. 接收狀態 - 所有節點在“ack”上都具有併發讀鎖

    sender                         receiver                 receiver
    "ack":CR                       "ack":CR                 "ack":CR
    
  2. 傳送方在“token”上獲取 EX (Exclusive) 鎖,傳送方在“message”上獲取 EX 鎖

    sender                        receiver                 receiver
    "token":EX                    "ack":CR                 "ack":CR
    "message":EX
    "ack":CR
    

    傳送方檢查是否仍需要傳送訊息。在等待“token”期間收到的訊息或其他事件可能已使此訊息不適合或多餘。

  3. 傳送方寫入 LVB

    傳送方將“message”從 EX 降級為 CW (Convert to Write)

    傳送方嘗試獲取“ack”的 EX 鎖

     [ wait until all receivers have *processed* the "message" ]
    
                                      [ triggered by bast of "ack" ]
                                      receiver get CR on "message"
                                      receiver read LVB
                                      receiver processes the message
                                      [ wait finish ]
                                      receiver releases "ack"
                                      receiver tries to get PR on "message"
    
    sender                         receiver                  receiver
    "token":EX                     "message":CR              "message":CR
    "message":CW
    "ack":EX
    
  4. 由“ack”上 EX 鎖的授予觸發(表示所有接收方都已處理訊息)

    傳送方將“ack”從 EX 降級為 CR (Convert to Read)

    傳送方釋放“message”

    傳送方釋放“token”

                                receiver upconvert to PR on "message"
                                receiver get CR of "ack"
                                receiver release "message"
    
    sender                      receiver                   receiver
    "ack":CR                    "ack":CR                   "ack":CR
    

4. 故障處理

4.1 節點故障

當一個節點發生故障時,DLM 會將槽號通知叢集。節點啟動一個叢集恢復執行緒。叢集恢復執行緒會:

  • 獲取故障節點的 bitmap<number> 鎖

  • 開啟點陣圖

  • 讀取故障節點的點陣圖

  • 將已設定的點陣圖複製到本地節點

  • 清除故障節點的點陣圖

  • 釋放故障節點的 bitmap<number> 鎖

  • 在當前節點上啟動點陣圖重新同步。md_check_recovery 在 recover_bitmaps 內部呼叫,然後 md_check_recovery -> metadata_update_start/finish,它將透過 lock_comm 鎖定通訊。這意味著當一個節點正在重新同步時,它會阻止所有其他節點向陣列的任何位置寫入。

重新同步過程是常規的 md 重新同步。然而,在叢集環境中執行重新同步時,需要告知其他節點哪些區域已暫停。在重新同步開始之前,節點發送 RESYNCING 訊息,其中包含需要暫停的區域的 (lo,hi) 範圍。每個節點維護一個 suspend_list,其中包含當前暫停的範圍列表。收到 RESYNCING 訊息後,節點將該範圍新增到 suspend_list。同樣,當執行重新同步的節點完成時,它會向其他節點發送帶有空範圍的 RESYNCING 訊息,其他節點會從 suspend_list 中移除相應的條目。

一個輔助函式 ->area_resyncing() 可用於檢查特定 I/O 範圍是否應該暫停。

4.2 裝置故障

裝置故障透過元資料更新例程進行處理和通訊。當一個節點檢測到裝置故障時,它不允許進一步寫入該裝置,直到所有其他節點都確認了該故障。

5. 新增新裝置

為了新增新裝置,所有節點都必須“看到”要新增的新裝置。為此,使用以下演算法:

  1. 節點 1 發出 mdadm --manage /dev/mdX --add /dev/sdYY,該命令發出 ioctl(ADD_NEW_DISK,其中 disc.state 設定為 MD_DISK_CLUSTER_ADD)

  2. 節點 1 傳送帶有 uuid 和槽號的 NEWDISK 訊息

  3. 其他節點發出帶有 uuid 和槽號的 kobject_uevent_env(步驟 4、5 可以是一個 udev 規則)

  4. 在使用者空間中,節點搜尋磁碟,可能使用 blkid -t SUB_UUID=””

  5. 根據是否找到磁碟,其他節點發出以下任一命令:ioctl(ADD_NEW_DISK,其中 disc.state 設定為 MD_DISK_CANDIDATE 且 disc.number 設定為槽號) 或 ioctl(CLUSTERED_DISK_NACK)

  6. 如果找到裝置,其他節點釋放“no-new-devs”(CR)上的鎖

  7. 節點 1 嘗試在“no-new-dev”上獲取 EX 鎖

  8. 如果節點 1 獲得鎖,它會在將磁碟標記為 SpareLocal 後傳送 METADATA_UPDATED

  9. 如果未獲得鎖(獲取“no-new-dev”鎖),則操作失敗併發送 METADATA_UPDATED。

  10. 其他節點通過後續的 METADATA_UPDATED 獲得磁碟是否已新增的資訊。

6. 模組介面

md 核心可以向叢集模組發出 17 個回撥。理解這些可以很好地概述整個過程。

6.1 join(nodes) 和 leave()

這些在帶有叢集點陣圖的陣列啟動和陣列停止時呼叫。join() 確保叢集可用並初始化各種資源。叢集中只有前 'nodes' 個節點可以使用該陣列。

6.2 slot_number()

報告叢集基礎設施建議的槽號。範圍從 0 到 nodes-1。

6.3 resync_info_update()

這會更新儲存在點陣圖鎖中的重新同步範圍。起始點隨著重新同步的進行而更新。終點始終是陣列的末尾。它不會傳送 RESYNCING 訊息。

6.4 resync_start(), resync_finish()

這些在重新同步/恢復/重塑開始或停止時呼叫。它們更新點陣圖鎖中的重新同步範圍,併發送 RESYNCING 訊息。resync_start 報告整個陣列正在重新同步,resync_finish 報告沒有重新同步。

resync_finish() 還會發送一個 BITMAP_NEEDS_SYNC 訊息,允許其他節點接管。

6.5 metadata_update_start(), metadata_update_finish(), metadata_update_cancel()

metadata_update_start 用於獲取元資料的獨佔訪問許可權。如果獲得訪問許可權後仍需要更改,metadata_update_finish() 將向所有其他節點發送 METADATA_UPDATE 訊息,否則可以使用 metadata_update_cancel() 釋放鎖。

6.6 area_resyncing()

這結合了兩種功能。

首先,它會檢查是否有任何節點當前正在給定扇區範圍內重新同步任何內容。如果發現任何重新同步,則呼叫者將避免在該範圍內寫入或讀取平衡。

其次,當節點恢復進行時,它報告所有區域都為 READ 請求而重新同步。這避免了叢集檔案系統和叢集 RAID 處理節點故障時的競爭條件。

6.7 add_new_disk_start(), add_new_disk_finish(), new_disk_ack()

這些用於管理上述新磁碟協議。當新增新裝置時,在將其繫結到陣列之前呼叫 add_new_disk_start(),如果成功,則在裝置完全新增後呼叫 add_new_disk_finish()。

當裝置響應先前的請求而新增,或者當裝置被宣告為“不可用”時,會呼叫 new_disk_ack()。

6.8 remove_disk()

當備用或失敗裝置從陣列中移除時呼叫此函式。它會導致 REMOVE 訊息傳送到其他節點。

6.9 gather_bitmaps()

這會向所有其他節點發送 RE_ADD 訊息,然後從所有點陣圖中收集點陣圖資訊。然後使用此組合點陣圖來恢復重新新增的裝置。

6.10 lock_all_bitmaps() 和 unlock_all_bitmaps()

當將點陣圖更改為無時,會呼叫這些函式。如果一個節點計劃清除叢集 RAID 的點陣圖,它需要確保沒有其他節點正在使用該 RAID,這透過鎖定叢集內的所有點陣圖鎖來實現,並且這些鎖也會相應地解鎖。

7. 不支援的功能

MD 叢集目前不支援一些功能:

  • 更改 array_sectors。