
在現代虛擬化架構中,多數人對「高可用性(High Availability, HA)」的直覺是,只要有兩台伺服器,一台倒了另一台接手就好。但實際在分散式叢集裡,雙節點是天生存在結構性缺陷的設定。
本篇記錄了在真實場域中,如何將原本隱含隱患的雙節點 Proxmox VE 叢集,透過 QNAP NAS Virtualization Station 上的輕量 Debian VM 引入第三票(QDevice / QNetd),並透過四場實兵演練(混沌測試),驗證「共用儲存與仲裁者同在一台 NAS」架構下的真實行為與 RTO。
今天的完整開源程式碼位於:pve-qdevice-on-qnap。
動手建置 QDevice 前,依照慣例進行基礎盤點。執行 pvecm nodes 列出兩台節點,畫面看起來是個健康的雙節點叢集,但查看 pvecm status 卻發現了矛盾:
Expected votes: 3
Total votes: 2
原來叢集原本有第三台節點(N150 小主機),先前已被手動關機,但仍留在 corosync.conf 的清單中。
三票要過半(Quorum)需要兩票。兩台在線時剛好過半,一旦掉任何一台,剩下的一台只有 1 票,立刻失去法定人數。這個叢集表面看起來亮綠燈,實質上完全沒有任何容錯餘裕。 而 pvecm nodes 只列出在線節點,完全掩蓋了這個風險。
深入檢查後,一共排查出三件「不會在 Web UI 首頁跳警告,卻會讓 HA 在關鍵時刻癱瘓」的問題:
| 檢查項目 | 觀察到的現象 | 實質影響與風險 |
|---|---|---|
票數一致性pvecm status vs pvecm nodes |
Expected votes: 3在線節點僅 2 台 |
一台已關機節點仍在算票。叢集無任何容錯空間,掉任一台即全毀。 |
HA 服務狀態ha-manager status |
某常開節點標註 old timestamp - dead?時間停在三個月前 |
該節點的 pve-ha-lrm 與 pve-ha-crm 曾被手動 mask。節點看似在線,但不參與 HA 投票與 Fence,上面跑的 HA 資源被徹底凍結。 |
| 磁碟放置位置檢查 VM 的虛擬磁碟 Storage | 某 HA VM 系統碟在 NFS,但 EFI 磁碟留在另一台節點的 local-lvm |
該 VM 無論在哪一台節點都無法正常漂移或開機,觸發搬移兩次失敗後直接進入 error 狀態。 |
pvecm delnode 將其正式移出叢集。(注意:未來若該機器要重回叢集,必須先清空它本機的 Corosync 設定再加回,否則開機會嘗試與舊叢集同步而產生衝突)。pve-ha-crm 與 watchdog 即刻載入,重新參與 Fencing 機制,凍結的資源也順利解凍。兩票要過半就是兩票。一旦任一節點斷線,剩下的一台只有 1 票,無法取得過半法定人數(Quorum),PVE 的 HA 守護進程為了避免發生腦裂(Split-Brain),會透過 Watchdog 直接將存活節點重開機(Self-Fence),導致全叢集服務中斷。
PVE 支援外部仲裁裝置(QDevice)。在叢集外的第三方主機上運行 corosync-qnetd,叢集內各節點運行 corosync-qdevice 與其通訊獲取選票。
將 QDevice 部署在 QNAP TS-464(Virtualization Station)上一台 1 vCPU / 1 GiB RAM 的極輕量 Debian VM,這台 NAS 同時也是叢集 NFS 共用儲存(Day 13/14 所述架構)。
| 情境 | 存活選票 | 法定人數(Quorum) | 叢集行為與結果 |
|---|---|---|---|
| 全數正常 | 節點 A、節點 B、QDevice(3 票) | 2 | 叢集正常運作 |
| NAS 重開機(QDevice 暫時離線) | 節點 A、節點 B(2 票) | 2 | 仍具備法定人數,HA 正常(VM 暫時卡在 NFS I/O) |
| 節點 A 故障(QDevice 在線) | 節點 B、QDevice(2 票) | 2 | 仍具備法定人數,節點 A 遭 Fence 後,其 VM 於節點 B 重新拉起 |
| 節點 A 故障 + NAS 同時重開 | 節點 B(僅剩 1 票) | 2 | 失去法定人數。 節點 B 觸發 Watchdog 自行重開 Fence,全數停擺 |
| 兩台節點正常,手動停用 QDevice | 節點 A、節點 B(2 票) | 2 | 正常運作,證明平日 QDevice 為備用輔助票 |
📌 核心架構思考:為什麼把第三票放在 NAS 上?
第四列顯示了此架構的致命弱點:NAS 維護窗口等同於「單節點故障即全倒」的脆弱窗口。
但在許多場域的實務配置中,幾乎所有 VM 都依賴 NAS 提供的 NFS 儲存或儲存設備。當 NAS 重開時,所有 VM 本來就會因為 I/O 阻塞而凍結。將仲裁者綁定在 NAS 上,是讓第三票與叢集本來就依賴的單點「共用同一個失效網域」,而不是在架構中徒增另一台隨時可能單獨故障的小實體機。
當運行 HA 資源的節點失去法定人數時,pve-ha-lrm 會失去資源鎖(Lock)。系統預設的軟體 Watchdog(softdog,若有伺服器 BMC 則可使用硬體 watchdog)會在約 60 秒後強制重啟節點。這是保護資料一致性的刻意設計,確保共用儲存不會被兩台節點同時寫入而造成損壞。
規格定義檔可參考 qnetd-vm.md,重點配置如下:
pvecm qdevice setup 即可,不應占用備份資源。timedatectl set-local-rtc 1
corosync-qnetd 監聽 TCP 5403,配置 nftables 防火牆僅允許兩台 PVE 節點連入。金鑰交換時需短暫開啟 root SSH 登入,初始化完畢後立刻關閉。為了簡化部署、平日監控與演練觀測,編寫了三支核心 Shell Script:
pve-qdevice-on-qnap/
├── setup.sh # 叢集自動化加入 QDevice(含相依套件安裝與防呆檢查)
├── check.sh # 叢集健康度與法定人數巡檢腳本(回傳標準 Exit Code)
└── record.sh # 演練黑盒子紀錄工具(配合 systemd-run 收集觀測數據)
setup.sh:具備冪等性(Idempotent)的初始化工具初期版本單純以 pvecm nodes 計算節點數,在先前的故障環境下誤判為 2 節點而繼續執行。新版改為直接解析 /etc/pve/corosync.conf:
corosync-qdevice 時,改用 PVE 自帶的 /etc/pve/nodes/<node>/ssh_known_hosts 與 HostKeyAlias 避免主機金鑰報錯。pvecm nodes 會出現一筆 ID 為 0 的虛擬節點,避免二次重跑時將其誤計為第 3 台實體節點。check.sh:監控與告警的核心哨兵將繁複的 pvecm status 與 ha-manager status 濃縮為監控系統可理解的單一指標。定義三種 Exit Code:
rc=0 (Healthy):三票到齊,HA 運作正常。rc=1 (Degraded):具有 Quorum,但已失去冗餘能力。涵蓋四種亞健康狀態:
error 狀態的 HA VM。rc=2 (Critical):失去 Quorum,叢集面臨自毀風險。💡 維運策略:監控告警在指標為
1(Degraded)時就必須即刻通知工程師,絕不能等到2(全倒)才響鈴。
record.sh:演練時的黑盒子紀錄器演練故障時往往伴隨 SSH 中斷與連線跳脫。透過 systemd-run 將 record.sh 丟入背景執行,每 2 秒記錄時間戳記、check.sh 回傳碼、當前票數與特定 HA VM 狀態,以精準量測 RTO。
在一台專用的測試用小 VM(512 MiB RAM,磁碟掛載於 NFS)內執行每秒一次的 dsync 磁碟寫入,以精確捕捉磁碟 I/O 凍結與故障轉移時間窗。
詳細紀錄請見 drills.md。
| 演練項目 | 實施方式 | 預期反應 | 實測觀測紀錄 |
|---|---|---|---|
| 1. NAS 重開 | 透過 QuTS hero 介面重新開機儲存兼仲裁者 NAS | Quorum 維持 Yes,check.sh 轉為 rc=1,NFS 上的 VM 寫入暫停,待 NFS 恢復後自動解凍,本機磁碟 VM 不受影響。 |
Quorum 全程維持 Yes。NFS 中斷 8 分 07 秒,VM 寫入空窗共 593 秒(含 NFSv4.1 Grace Period)。QDevice 在 NFS 恢復 2 分 39 秒後上線。 |
| 2. 節點 A 斷電 | 於節點 A 執行 sysrq o 強制硬關機(非正常 Shutdown) |
節點 B 維持 Quorum,節點 A 上的 HA 資源經 Watchdog Fence 後於節點 B 重新開機,耗時約 2 分鐘。 | Quorum 維持 Yes。測試 VM 於 3 分鐘後在節點 B 成功啟動,VM 內磁碟寫入空窗共 205.8 秒(含開機自身耗時 26 秒)。 |
| 3. 節點 A 斷電+ NAS 重開 | 觸發 NAS 重開,確認 NFS 斷線瞬間立刻對節點 A 執行斷電 | 節點 B 失去 Quorum,約 60 秒後節點 B 觸發 Watchdog 自行重開,全域停擺直到 NAS 與 QDevice 復原。 | 符合預期。節點 A 斷電後 51 秒,節點 B 因失去 Quorum 遭 Watchdog 強制重啟。直到 NAS 與 QDevice 開機完成,節點 B 重新獲得 Quorum 並拉起 VM,整體中斷時間 11 分 15 秒。 |
| 4. 手動關閉 QDevice | 在 QDevice VM 內部執行 poweroff,隨後由 QuTS hero 重新啟動 |
Quorum 維持 Yes,check.sh 轉為 rc=1,測試 VM 運作完全不受影響。 |
關機 5 秒內 check.sh 轉為 1(票數 2/3)。重開後 VM 核心載入 9 秒 qnetd 就緒,再過 9 秒狀態完全復原(rc=0)。 |
原本預期 2 分鐘的切換時間,實測延長到了 3 分鐘,時間花在兩把鎖的釋放:
[00:00] 節點 A 斷電 (sysrq o)
│
[00:03] 節點 B 的 corosync 宣告 token 逾時
│
[00:08] 節點 B 與 QDevice 組成新 Quorum(法定人數從未中斷)
│
[01:57] 節點 B 的 pve-ha-crm 取得 Master 鎖(因 A 剛好是 Master,需等過期)
│
[02:57] 節點 B 取得 節點 A 的 Agent 鎖(確認節點 A 徹底被 Fence)
│
[03:00] 測試 VM 於節點 B 開始 Booting
│
[03:26] VM 內部產生第一筆 dsync 寫入
📌 維運提醒:規劃 HA 業務的 RTO 時,若故障節點恰巧為 HA Master,必須將「Master 鎖過期」加上「Agent 鎖過期」時間算入,基準線應設為 3 分鐘 + VM 自身開機時間。此外,
sysrq o斷電後主機無法以 WoL 喚醒,演練前務必確保現場有專人可手動按下電源按鈕。
⚠️ 絕對禁忌:
pvecm expected 1在演練 3 的情境下,單節點失去 Quorum 時,輸入
pvecm expected 1可強行解除節點凍結。但除非你親自在機房拔掉另一台節點的電源線,否則嚴禁使用! 若另一台節點其實還活著,兩台節點各自獨立掛載 NFS 寫入同一顆虛擬磁碟,將造成毀滅性的資料損壞(腦裂)。
PVE 實踐 HA 的基石是共用儲存或快速複寫。原本設想採取雙層儲存:
pvesr 定期非同步複寫。但盤點後發現兩台節點安裝時均採用了預設的 LVM-thin,而 pvesr 只支援 ZFS。在不進行節點重灌的前提下,目前全數 VM 均運行於 NAS NFS 上。這也讓的架構更具單一性:NAS 本身就是所有 VM 的 SPOF(單一故障點),因此將 QDevice 放在同台 NAS 上並不會創造新的單點脆弱性。
在 QNAP Virtualization Station 建立輕量 Debian VM 並安裝 corosync-qnetd 後,登入節點 A 執行:
git clone https://github.com/ivanusto/pve-qdevice-on-qnap && cd pve-qdevice-on-qnap
./setup.sh 192.168.x.<QDevice_IP>
# 驗證狀態(預期 rc=0, votes=3/3, quorate=Yes)
./check.sh; echo "Exit Code: $?"
在存活節點(節點 B)透過 systemd-run 啟動背景紀錄:
systemd-run --unit=drill2 /root/pve-qdevice-on-qnap/record.sh drill2 vm:<Test_VM_ID>
# 演練結束後停止並查看完整數據日誌
systemctl stop drill2
cat /root/drills/drill2.log
若未來節點架構變更,可隨時在任一 PVE 節點優雅移除第三票:
pvecm qdevice remove
高可用性不只是把叢集串起來看著儀表板亮綠燈。透過深層盤點與四場實戰故障演練,應該對於雙節點搭配 QDevice 的極限有更多的了解,也能夠知道失效窗口以及真實切換時鐘延遲(RTO)大概是怎樣的情形。
當確認了所有核心 VM 均座落於 NAS NFS 共用儲存並加固後,接下來最核心的防線,就是老生常談的備份與還原機制囉。
Day 16 將探討:Proxmox VE 備份與還原實戰,利用 HDP for Business beta 建立多版本排程、保留原則與全災難還原演練。