正式部署以前,必須先回答兩組相連的問題:系統遇到不同故障時由誰負責,以及這些責任要如何落到硬體、VM、網路與服務配置。今天先分清楚叢集、複寫、高可用、備份與災難復原,再完成後續 28 天都會沿用的部署藍圖。
本文閱讀方式
- 完整理解技術與底層原理:依序閱讀全文。
- 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
- 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
- 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。
在一套同時包含 PVE、OPNsense、PostgreSQL、Patroni 與備份系統的架構中,Cluster、Replication、HA 與 Backup 經常同時出現,但它們觀察的故障和保護的對象並不相同。而這些用語會貫穿整個系列,所以今天的主要目標先釐清每個術語代表的功能,以及它們各自負責處理什麼問題。
在了解這些概念的同時,我們還會遇到 RTO、RPO、故障域、隔離(Fencing)與腦裂(Split Brain)這些名詞。它們分別用來說明服務能中斷多久、資料能損失多少、一次故障會影響多大範圍,以及系統切換時如何避免兩個節點同時接受寫入。
解釋:
叢集(Cluster)是把多個獨立節點組成一個可以共同管理或共同提供服務的整體。這些節點通常會交換狀態,並透過共同的規則決定哪些成員仍然在線、設定應該如何同步,或工作應該由哪一個節點處理。
Cluster 描述的是多個節點如何組成一個整體,本身不代表一定具備高可用、複寫、與備份。有些叢集著重集中管理,有些負責分散工作,有些則用來維持服務狀態,因此仍要確認該叢集管理的對象與實際提供的能力。
Replication 是把資料從一個節點複製到其他節點。PostgreSQL 通常透過 WAL(Write-Ahead Log,預寫式日誌)完成複寫。資料庫會先把異動寫入 WAL,再更新實際資料頁。Replica 接收並重播這些紀錄,使多個資料庫節點維持接近的內容。
Replication 能提供其他節點可使用的資料副本,但它會持續同步後續異動,因此不是用來長期保存舊資料的備份。
Quorum 是叢集做出決策前必須取得的最低多數票。它用於確認目前仍保持連線的節點,是否足以代表整個叢集做出一致決定。
以三個具有相同票數的節點來說,至少要有兩票才具備 Quorum。如果網路中斷後只剩一個節點可以互相通訊,這個節點即使本身仍在運作,也不能單獨代表整個叢集修改狀態。這能降低網路分割後,兩邊各自做出衝突決策的風險。
高可用(High Availability,HA)是當某個元件故障時,整體服務仍能繼續提供,或能在可接受的時間內恢復。它關心的是使用者最後能不能繼續使用服務。
Failover 是故障發生後的被動切換。這種切換通常發生得突然,因此要確認接手節點的資料狀態,以及舊節點是否已停止提供服務。
Switchover 則是為了維護、升級或演練而事先安排的主動切換。因為切換時間、來源與目標都可以預先確認,風險通常比故障發生後才執行的 Failover 更容易控制。
Backup 是可以獨立保存並用來還原的資料副本。它不能只存在於來源資料庫的同一顆硬碟,也不能只確認排程有執行,還要確認備份檔真的存在、內容可讀,而且能完成還原。
Replication 會持續接收最新異動,來源端的錯誤操作也可能一起被複寫,Backup 則需要保留不同時間點的資料,讓系統能回到事故發生前的狀態。兩者可以同時存在,但不能互相取代。
災難復原(Disaster Recovery,DR)則是重大事故發生後,讓整套服務重新運作的計畫。它不只包含資料庫備份,還包含 VM、設定檔、憑證、DNS、網路規則、復原順序與負責人。Backup 是 DR 使用的其中一項材料,但只有備份檔,並不等於已經具備完整的災難復原能力。
Security 用來限制誰可以從哪裡連線,以及連線後可以執行哪些操作。它包含身分驗證、權限控管、網路分區與最小權限等不同層次。
在進入故障情境以前,我們先認識一下 RTO、RPO 與 故障域。這三個名詞分別回答可以中斷多久、可以損失多少資料以及同一次故障會影響哪些設備。

圖(一)RPO 的復原點與 RTO 的復原時間。
RTO 是發生故障後,服務允許中斷的最長時間。例如 RTO 設為 10 分鐘,代表從服務中斷開始,到使用者可以重新使用服務為止,目標不能超過 10 分鐘。
RTO 不是看到新的 Primary 選出就停止計時。資料庫完成切換後,HAProxy 需要更新狀態,應用程式可能需要重新連線,使用者的請求也必須恢復成功,這些時間都要計算在內。
RPO 是發生事故後,可以接受損失多少時間範圍內的資料。例如 RPO 設為 5 分鐘,代表復原後最壞可能遺失事故前 5 分鐘內寫入的資料。
資料庫有 Replica(複本)不代表 RPO 一定是 0。非同步複寫可能存在延遲,如果 Primary 故障時仍有 WAL(Write-Ahead Log,預寫式日誌)尚未傳送到 Replica,切換後就可能缺少最後一小段交易。遇到誤刪資料時,刪除操作也會被正常複寫,此時能回到哪一個時間點,取決於備份、WAL 歸檔與 PITR(時間點還原)是否可用。
RTO 與 RPO 都是事先設定的目標。實際結果仍要透過故障演練與復原測試量測。
故障域是可能被同一個故障一起影響的範圍。判斷故障域時,不能只看畫面上有幾台 VM,而要繼續往下確認它們是否共用實體主機、硬碟、交換器、電源或網路線路。
例如三台 PVE 叢集雖然在管理畫面中是三個節點,但它們都執行在同一台實體主機上。當實體主機斷電時,三個節點會一起停止,因此它們仍然位於同一個實體故障域。
在我們這個系列的實驗環境中,三台 PVE 就位於同一台實體主機上,這就是為什麼正式環境不建議這麼做。故障域所描述的是單一元件故障時,究竟會造成多大的影響。故障影響的範圍當然越小越好,但同時也會受限於成本與維護難度。
最容易混淆的地方,是這些技術經常同時出現在同一張架構圖上。接下來直接從 Primary VM 故障與資料誤刪兩種情境,觀察各層負責處理的問題。
以「Primary VM 突然關機」為例,事件會依序經過以下過程:

圖(二)pg01 所在 VM 故障後,PVE HA 與 PostgreSQL HA 各自處理的路徑。
圖中的 PVE HA 路徑是條件式能力:同節點可嘗試重新啟動 VM。若要在其他節點恢復,VM 磁碟必須可由目標節點取得,例如使用共享儲存或事先完成儲存複寫。本系列的 PostgreSQL VM 使用各節點的 local-lvm,因此資料庫跨節點接手由 Patroni 與 PostgreSQL 複寫負責,不會把 pg01 VM 直接移到其他節點。
兩條路徑可能同時發生,完成順序也不固定。PVE HA 關心的是 VM 要在哪一個節點運行。Patroni 關心的是哪一個 PostgreSQL 節點可以擔任 Primary。PVE 即使還沒有完成 VM 重啟,Patroni 也可能已經在其他資料庫節點完成角色切換。
每一層恢復的時間都不同。Patroni 顯示新 Primary,不代表 Client 已經恢復。VIP 可以 Ping,也不代表 HAProxy 已找到正確 Backend。真正驗證高可用時,必須把這些時間點拆開記錄。
這個切換過程最怕發生腦裂(Split Brain)。簡單來說,就是兩個節點因為網路中斷而無法互相確認狀態,卻都認為自己才是目前的 Primary。假設 pg01 原本負責寫入,失去叢集連線後仍繼續接受交易,另一側又把 pg02 升級成新的 Primary,應用程式便可能同時把資料寫進兩台資料庫。兩邊的資料各自變動後,不只無法靠一般複寫自動合併,也可能產生交易衝突或資料遺失。
為了避免舊 Primary 在失去資格後繼續提供服務,HA 架構會使用隔離(Fencing)機制。簡單來說,就是必須先確定舊節點不會再接受新的交易,才允許其他節點接手。系統可以關閉舊 VM、切斷它的電源、撤銷儲存存取權,或用其他方式把它排除在服務路徑之外。
再看誤刪資料:
DELETE/DROP 在 Primary Commit
→ WAL 傳到 Replica
→ Replica 重播同一個錯誤
這時 Replica 正常運作反而代表錯誤成功複寫。能處理這類事故的是備份保留、WAL 歸檔(WAL Archive)、PITR(時間點還原)與實際驗證過的復原流程
。
有了剛剛的解釋與例子我們就知道先規劃好故障與還原由誰來負責非常重要: 那麼我們先來確定一下在本系列中到底哪些組件負責什麼
| 故障 | 主要負責層 |
|---|---|
| 實體節點故障 | PVE/節點分散 |
| PostgreSQL Primary 故障 | Patroni |
| etcd Member 故障 | etcd Quorum |
| HAProxy 故障 | Keepalived |
| 誤刪資料 | pgBackRest+PITR |
RTO 與 RPO 應該根據服務的重要程度、可接受的營運損失與資料價值決定,但我們現在是實驗環境就不設定這些目標。不過我們依然要先記錄這些結果與制定好驗證方式。
| 量測項目 | 要記錄的結果 | 驗證方式 |
|---|---|---|
| DB 入口中斷時間 | 最後一次成功與第一次恢復成功之間相差幾秒 | client01 每秒寫入並記錄結果 |
| Web 入口中斷時間 | 連續失敗幾次、何時恢復 HTTP Response | 持續送出 HTTP Request |
| PITR 復原點差距 | 指定時間與實際還原資料之間相差多久 | 比對 Sequence ID 與時間戳 |
責任邊界確定後,下一步是把它轉成可以照表部署與驗證的資源配置。以下藍圖會固定主機名稱、VMID、網段、Gateway、Port、Storage 與 VM 放置位置,避免後面的防火牆、DNS、憑證與服務設定各自使用不同數值。

圖(三)Proxmox VE 虛擬化平台。
Proxmox Virtual Environment,簡稱 PVE,是以 Debian GNU/Linux 為基礎的開源伺服器虛擬化平台。它把運算、網路、儲存、權限、備份、叢集與高可用功能整合在同一套系統中,管理者可以透過 Web UI、CLI 或 REST API 操作。
PVE 可以同時管理 KVM/QEMU 虛擬機與 LXC Container。QEMU 負責提供虛擬硬體,KVM 則利用 Linux Kernel 與 CPU 的硬體虛擬化能力加速執行。每台 VM 都有自己的虛擬 CPU、RAM、Disk、Network Device 與完整作業系統,因此可以執行 Debian、Windows 或 OPNsense 等不同系統。
LXC Container 則與 Host 共用 Linux Kernel,不需要替每個 Container 啟動一套完整 Kernel,通常會比 VM 節省資源。不過 LXC 只能執行 Linux,隔離方式也和完整 VM 不同。如果後續有時間的話我也會補充一下教學和實驗。
本系列主要使用 KVM/QEMU VM,而不是 LXC Container。原因是 OPNsense、安裝在虛擬機內的 PVE 與 Debian Server 都需要清楚、獨立的作業系統邊界。
PVE 將 VM、容器、Node、儲存、網路、防火牆、使用者、權限與 Cluster 集中到同一套 Web UI,也保留 CLI 與 REST API,不需要替每一項功能另外安裝管理介面。
網路部分使用 Linux Bridge 替 VM 建立虛擬交換器,並支援 VLAN-aware Bridge、VLAN Tag、Bond 與 Firewall。儲存部分則能使用 Directory、LVM、LVM-thin、ZFS、NFS、Ceph RBD 與 CephFS。
這些功能在往後的教學裡會一一出現到時候會再詳細解釋。
多台 PVE Node 可以組成 Cluster,集中管理所有節點與 VM。若再搭配 Quorum、可由其他節點存取的 VM Disk、Watchdog/Fencing 與 HA Resource 設定,便能在節點故障後由其他節點重新啟動 VM。
PVE 也內建 VM/Container 備份排程、Snapshot、Clone、Migration、Console、權限管理與工作紀錄,並能搭配 Proxmox Backup Server 建立去重複、增量式備份。
因為底層仍是 Debian Linux,除了操作 Web UI,也能使用一般 Linux 指令檢查系統:
ip -br address
ip route
ss -lntup
systemctl status 服務名稱
journalctl -u 服務名稱
apt update
lsblk

圖(四)PVE Web UI 資源階層
圖(四)是 PVE Web UI 左側的資源階層。最上層的 Datacenter 代表整個管理範圍。下一層是實際執行工作負載的 PVE Node,Node 底下則會顯示 VM、Container、Storage 與 Network 等資源。
本系列示範設備為:
CPU:12 Core
RAM:48GB
Storage:500GB
實體主機:一台
唯一實體主機先安裝 PVE,稱為 L0,再於 L0 內建立三台嵌套 PVE 與一台 OPNsense:

圖(五)OPNsense、三台 PVE 節點與主要常駐服務 VM 的簡化放置關係。ca01、client01 與臨時建立的 pg-restore01 未畫入,完整配置以下表為準。
因此,畫面中的 VMID 101~110 屬於 L0。正式服務 VM 使用 201~261,可丟棄的測試與隔離還原 VM 則使用 900~999,這些 VM 都位於 pve01~pve03 組成的 L1 Cluster。
PVE 讓管理者替 VM 設定 Socket、Core、CPU Type、RAM、Ballooning 與磁碟,但填入的數字不代表主機效能會憑空增加。多台 VM 同時繁忙時,仍會競爭同一台 Host 的 CPU、RAM 與 I/O,所以在設計每台 VM 與服務時,要先預估最壞情況,避免服務不穩定。
實驗環境 L0 的分配為:
| L0 VMID | 名稱 | vCPU | RAM | 磁碟 | 用途 |
|---|---|---|---|---|---|
| 101 | pve01 | 4 | 12GB | 80GB System+40GB Ceph OSD | PVE 節點 1 |
| 102 | pve02 | 4 | 12GB | 80GB System+40GB Ceph OSD | PVE 節點 2 |
| 103 | pve03 | 4 | 12GB | 80GB System+40GB Ceph OSD | PVE 節點 3 |
| 110 | fw01 | 2 | 3GB | 16GB | OPNsense |
實驗環境下我們會稍微超配 vCPU,因為多數服務平時負載很低。但這種作法不建議帶到正式環境中。Lab 中的 Ceph Recovery、PostgreSQL 壓力測試、備份與大量套件更新也要錯開執行。
好的命名從名字就看得出功能:
pve Proxmox VE 節點
fw Firewall
proxy Nginx/HAProxy/Keepalived
app Web Backend
pg PostgreSQL/Patroni/etcd
jump SSH Bastion
client 測試用戶端
monitor 監控
backup 備份 Repository
不要使用 server1、test2 或 newvm 這類日後無法判斷用途的名稱。
| VMID | VM |
|---|---|
| 101 | pve01 |
| 102 | pve02 |
| 103 | pve03 |
| 110 | fw01 |
| VMID 範圍 | 用途 |
|---|---|
| 200~209 | Proxy/Load Balancer |
| 210~219 | Application |
| 220~229 | PostgreSQL/Database |
| 230~239 | Bastion |
| 240~249 | 測試 Client |
| 250~259 | Monitoring |
| 260~269 | Backup |
| 900~999 | 可刪除的測試/隔離還原 VM |
| 9000 以上 | Template。本系列使用 9001 |
同一個 PVE Cluster 內的 VMID 必須唯一。PVE 的 pmxcfs 會在節點間即時同步 Cluster 設定,也會檢查重複 VMID。VM 從 pve01 遷移到 pve02 時,仍然使用原本的 VMID。Proxmox Cluster File System
PVE 使用 Linux Network Stack。Linux Bridge 也就是虛擬交換器,實體 NIC 與 VM 的虛擬 NIC 都可以接到同一座 Bridge。常見名稱是 vmbr0、vmbr1,但數字只是名稱,不代表固定用途。後面實際配置的時候再來詳細講解。
本系列先把 VLAN 分成:
| 區域 | VLAN | 網段 | Gateway | 用途 |
|---|---|---|---|---|
| Management | 10 | 10.77.10.0/24 |
10.77.10.1 |
PVE 管理 |
| Service | 20 | 10.77.20.0/24 |
10.77.20.1 |
Proxy、App、Client、Monitor、VIP |
| Database | 30 | 10.77.30.0/24 |
10.77.30.1 |
ca01、PostgreSQL、Patroni、etcd |
| Backup | 40 | 10.77.40.0/24 |
10.77.40.1 |
pgBackRest Repository |
| Bastion DMZ | 50 | 10.77.50.0/24 |
10.77.50.1 |
jump01 |
| OpenVPN | 非 VLAN | 10.77.60.0/24 |
10.77.60.1 |
遠端 VPN Client |
| Ceph | 獨立網路 | 10.77.70.0/24 |
不設定 | Storage 流量 |
| Corosync | 獨立網路 | 10.77.80.0/24 |
不設定 | PVE Cluster 通訊 |
VLAN 10~50 的 Gateway 由 OPNsense 提供,跨 VLAN 流量也經過 OPNsense。Ceph 與 Corosync 使用沒有 Gateway 的封閉網路,不拿來上網。
| 用途 | L0 Bridge | L1 PVE 網卡 | L1 設定 |
|---|---|---|---|
| 建置與救援 | vmbr0 |
net0 |
vmbr0,連接現有路由器 |
| VLAN 10~50 | vmbr2 |
net1 |
vmbr1,設定為 VLAN-aware Bridge。Management 使用 vmbr1.10 |
| Ceph | vmbr4 |
net2 |
vmbr2,只承載 Ceph 流量 |
| Corosync | vmbr5 |
net3 |
第四張網卡直接設定 Corosync IP,不另外建立 Bridge |
L0 的 Bridge 是用來替三台 L1 PVE 提供四條彼此分離的虛擬線路。進入 L1 後,再由各自的 Bridge 或網路介面接手。vmbr2 在 L0 代表 VLAN Trunk,在 L1 則代表 Ceph,名稱相同不表示用途相同,判斷時要先確認目前位於哪一層。
本系列的 L1 VM 安排如下:
| VMID | VM | 初始節點 | vCPU | RAM | 磁碟 | Storage | 服務 |
|---|---|---|---|---|---|---|---|
| 201 | proxy01 | pve01 | 2 | 1GB | 16GB | local-lvm | Nginx、HAProxy、Keepalived |
| 202 | proxy02 | pve02 | 2 | 1GB | 16GB | local-lvm | Nginx、HAProxy、Keepalived |
| 211 | app01 | pve02 | 1 | 1GB | 12GB | ceph-vm | Flask Backend 1 |
| 212 | app02 | pve03 | 1 | 1GB | 12GB | ceph-vm | Flask Backend 2 |
| 221 | pg01 | pve01 | 2 | 2GB | System 24GB 以上+Data 16GB | local-lvm | PostgreSQL、Patroni、etcd01 |
| 222 | pg02 | pve02 | 2 | 2GB | System 24GB 以上+Data 16GB | local-lvm | PostgreSQL、Patroni、etcd02 |
| 223 | pg03 | pve03 | 2 | 2GB | System 24GB 以上+Data 16GB | local-lvm | PostgreSQL、Patroni、etcd03 |
| 224 | ca01 | pve01 | 1 | 512MB | 8GB | local-lvm | step-ca Lab Internal CA |
| 231 | jump01 | pve03 | 1 | 1GB | 8GB | local-lvm | SSH Bastion |
| 241 | client01 | pve03 | 1 | 1GB | 8GB | local-lvm | Web/SQL 測試 |
| 251 | monitor01 | pve02 | 2 | 2GB | 24GB | local-lvm | Prometheus、Grafana、Alertmanager |
| 261 | backup01 | pve01 | 2 | 2GB | System+32GB Repository | local-lvm | pgBackRest |
| 904 | pg-restore01 | 臨時放置 | 2 | 2GB | 16GB Data Disk | local-lvm | 隔離 PITR Restore |
proxy、app 與 PostgreSQL 分散到不同節點,是為了避免同一個 L1 PVE 停止時,同一組服務的所有成員一起消失,這樣就達不到高可用的目的了。後面介紹 PVE HA 時再完整說明。
本系列統一使用 lab.home,不混用 home.lab 或 lab.internal。
| 主機 | Management | Ceph | Corosync |
|---|---|---|---|
| pve01 | 10.77.10.11 |
10.77.70.11 |
10.77.80.11 |
| pve02 | 10.77.10.12 |
10.77.70.12 |
10.77.80.12 |
| pve03 | 10.77.10.13 |
10.77.70.13 |
10.77.80.13 |
| 名稱 | FQDN | IP |
|---|---|---|
| Web VIP | web.lab.home |
10.77.20.10 |
| DB RW VIP | db-rw.lab.home |
10.77.20.11 |
| DB RO VIP | db-ro.lab.home |
10.77.20.12 |
| proxy01/02 | proxy01.lab.home/proxy02.lab.home |
10.77.20.21/.22 |
| app01/02 | app01.lab.home/app02.lab.home |
10.77.20.31/.32 |
| client01 | client01.lab.home |
10.77.20.41 |
| monitor01 | monitor01.lab.home |
10.77.20.51 |
| ca01 | ca01.lab.home |
10.77.30.10 |
| pg01~03 | pg01.lab.home~pg03.lab.home |
10.77.30.11~.13 |
| pg-restore01 | pg-restore01.lab.home |
10.77.30.21 |
| backup01 | backup01.lab.home |
10.77.40.11 |
| jump01 | jump01.lab.home |
10.77.50.11 |
三組 VIP 由 proxy01、proxy02 上的 Keepalived 持有,不配置在 OPNsense。OPNsense 只需把獲准流量送到固定 VIP,不必在 VIP 換到另一台 Proxy 時修改目的 IP。
lab.home 只供內部名稱解析。公開網站另外使用自己持有的正式網域,例如 app.example.com。這筆公開 DNS 記錄會在 Day 27 交由 Cloudflare 代理,DNS 查詢回覆 Cloudflare 的 Anycast 位址,而不是直接公開 10.77.20.10 或其他私有位址。
| 來源 | WAN Port | 目的地 | 用途 |
|---|---|---|---|
| 外部 VPN Client | UDP 1194 | fw01 | OpenVPN |
| Cloudflare 公布的 Proxy IP 網段 | TCP 443 | Web VIP 10.77.20.10:443 |
公開 Web Origin |
| 經核准的管理來源 | TCP 45222 | jump01 的 TCP 22 | Bastion SSH 通訊埠轉送(Port Forward) |
這裡只記錄必須從 WAN 進入的服務。Cloudflare 橘雲只代理 Web 流量,不會替 OpenVPN 或一般 SSH 提供相同代理。公開 Web 的 TCP 443 也不接受任意來源直接連到 Origin。內部服務使用的 Port 會在部署對應服務及建立防火牆規則時再說明,避免把內部相依關係和公開入口混在一起。
同一次部署中的節點必須使用一致版本,發生問題時才知道應該對照哪一份文件,如果後續要升級系統記得這份文件也要同步更改:
| 元件 | 本系列版本 |
|---|---|
| Proxmox VE | proxmox-ve_9.2-1.iso |
| PVE Base | Debian 13 trixie |
| Ceph | 19.2 Squid |
| Debian Guest | Debian 13 Trixie Generic Cloud Image |
| OPNsense | 26.7 DVD ISO |
| PostgreSQL | 18,使用 PGDG Repository |
後續更改 IP、WAN Port 或 VMID 時,先修改規劃,再同步修改實際設定,避免圖、指令與環境使用不同數值。
下一篇正式開始 Lab:安裝 Proxmox VE,完成 L0 與三台 L1 PVE,並在 OPNsense 尚未部署前先透過現有路由器完成更新與後續建置。