跳到主要內容
節省工具箱Jason Tools
經驗分享

PVE VirtioFS 共用 CephFS 的實測與限制

Jason Cheng 發布 瀏覽 2 次
PVE VirtioFS 共用 CephFS 的實測與限制
本文目錄
  1. 測試環境
  2. 運作架構
  3. 檔案鎖
  4. 多台同時寫同一個檔案
  5. Cache 模式
  6. 權限與安全
  7. 遷移與快照
  8. 適用範圍
  9. 建置步驟
  10. LXC 的做法
  11. 測試方法與限制
  12. 結論
  13. 參考資料

Proxmox VE 從 8.4 開始內建 VirtioFS,可以把 PVE 主機上的目錄直接共用給 VM,VM 和 PVE 主機之間不走網路。如果這個目錄放在 CephFS 上,不同節點的 VM 就能看到同一份資料,VM 內也不需要 Ceph 金鑰。

我想知道這個組合能不能當成多台 VM 的共用儲存,所以在 PVE 9.2 的叢集上實測檔案鎖、同時寫入、cache 模式、權限與遷移,再對 virtiofsd 1.13.2 與 PVE 的原始碼查證。

結果是可以共用,但只適合共用讀取,或各 VM 寫各自的檔案。跨 VM 的檔案鎖沒有作用,多台同時寫同一個檔案會遺失資料,VM 也不能線上遷移。

測試環境

  • PVE: pve-manager 9.2.24、qemu-server 9.2.13、pve-qemu-kvm 11.0.3-4,主機核心 7.0.14-20-pve
  • VirtioFS: virtiofsd 1.13.2
  • Ceph: 19.2.6,測試專用的 CephFS cephfs-test(2026/11/01 查詢時 data 集區 16 PG、metadata 集區 8 PG,autoscaler 開啟),PVE 主機以核心用戶端掛接
  • VM: 2 台 Ubuntu 24.04,核心 6.8.0-147,2 核心、2 GB 記憶體,分別放在 pve1、pve2,測同節點時再移到同一個節點
  • 共用目錄: PVE 主機上的 /mnt/pve/cephfs-test/vm-share,VM 內掛接到 /mnt/share

本文的圖是實測輸出,節點與儲存名稱已換成範例名稱,部分測試項目與無關的訊息已略過,鎖與計數器的結果未更動。

兩台 VM 的設定、virtiofsd 參數與 VM 內的掛接
兩台 VM 的設定、virtiofsd 參數與 VM 內的掛接

運作架構

VM 開機時,PVE 會替每個 virtiofs 裝置啟動一個 virtiofsd。VM 內的檔案操作透過 virtio 交給 virtiofsd,再由它在 PVE 主機的目錄上執行。

CephFS 是由 PVE 主機掛接,VM 接觸不到 Ceph,也沒有金鑰。測試環境的主機端用 name=admin 身分掛接。

VirtioFS 共用 CephFS 的架構:檔案內容經由 CephFS 互通,鎖只留在各 VM 的核心內
VirtioFS 共用 CephFS 的架構:檔案內容經由 CephFS 互通,鎖只留在各 VM 的核心內

檔案鎖

第一個測試很單純:VM A 持有鎖,VM B 以非阻塞方式嘗試取得同一把鎖,flock 與 POSIX 鎖(lockf)都測。

情境flockPOSIX 鎖
跨節點 2 台 VM,cache=auto取得,鎖無效取得,鎖無效
跨節點 2 台 VM,cache=never取得,鎖無效取得,鎖無效
同節點 2 台 VM,cache=auto取得,鎖無效取得,鎖無效
同節點 2 台 VM,cache=never取得,鎖無效取得,鎖無效
同一台 VM 內 2 個程序被拒絕,EAGAIN被拒絕,EAGAIN
PVE 主機上的 CephFS 原生掛接,跨節點被拒絕,EAGAIN被拒絕,EAGAIN

第二個測試是累加計數器,兩邊各加 400 次,預期結果是 800。程式每次以不截短的 r+ 模式開檔,取得鎖之後才讀取、加 1、截短、寫回並 fsync,lockf 鎖的是整個檔案。

情境flockPOSIX 鎖
CephFS 原生掛接,跨節點800800
同一台 VM 內 2 個程序800800
跨節點,cache=auto5、433、10
跨節點,cache=never63、516、89
同節點,cache=auto2713
同節點,cache=never3255

這些數字只能說明鎖沒有保護到資料,不是遺失比例。計數程式讀到空檔時會從 0 重算,所以最終值會特別低。

跨節點測試:VM A 持鎖時,VM B 仍取得鎖,計數結果遠低於 800
跨節點測試:VM A 持鎖時,VM B 仍取得鎖,計數結果遠低於 800
對照組:CephFS 原生掛接與同一台 VM 內,鎖都有效
對照組:CephFS 原生掛接與同一台 VM 內,鎖都有效

原因可以從原始碼找到。virtiofsd 1.13.2 的 getlk、setlk、setlkw 都還沒有實作,一律回傳 ENOSYS,初始化時也沒有向核心宣告支援 FUSE_POSIX_LOCKS、FUSE_FLOCK_LOCKS。我也在 PVE 主機上看過 virtiofsd --help,沒有鎖相關的選項。

在這種情況下,鎖由 VM 自己的核心處理。所以同一台 VM 內有效,另一台 VM 則完全不知道這把鎖的存在。同節點一樣失效,也符合這個解釋:問題在 virtiofs 這一層,不是 CephFS,也不是網路。

多台同時寫同一個檔案

兩邊以 O_APPEND 開檔,各寫 5000 行,每行一次 write,長度固定 89 位元組,預期 10000 行。長度不符的行列為長度異常。

情境行數長度異常行數
跨節點,cache=auto5390、5333151、256
跨節點,cache=never5177、5047、52730
同節點,cache=auto10000(1 次)264
同節點,cache=never10000(1 次)0
同一台 VM 內 2 個程序100000
CephFS 原生掛接,跨節點8071、10000、57390

跨節點 cache=never 雖然沒有長度異常,但行數少了將近一半。同節點 cache=never 只跑 1 次,無法確認兩邊是否真的重疊寫入,不能當成安全的證據。

值得注意的是 CephFS 原生掛接也出現行數不足。依原始碼,virtiofsd 在沒有開 writeback 時,會以 RWF_APPEND 把這類寫入交給 PVE 主機的檔案系統,PVE 也沒有開 writeback。所以我判斷跨節點的行數不足不全是 virtiofs 造成的,但這部分我沒有再往 Ceph 追查,目前只把它當成實測行為。

這個測試也有限制:只檢查行數與每行長度,沒有比對每筆的序號與內容,也沒有逐筆檢查 write 的回傳值,所以只能說明結果不完整,還不能斷定資料是在哪一層少掉的。

不論原因在哪一層,多台 VM 都不應該同時寫同一個檔案,記錄檔也一樣,改成每台 VM 各寫一個檔案。

Cache 模式

PVE 的 cache 有 auto(預設)、always、metadata、never 四種,這次測 auto 與 never。

依 virtiofsd 原始碼,auto 的目錄項目與屬性快取時間是 1 秒,never 是 0 秒,而且檔案一律走直接 I/O,不使用 VM 內的快取。原始碼的說明也指出,檔案內容可能被其他用戶端改動時要選 never,而這正是共用目錄的情況。

變更傳到另一台的時間

跨節點量測 VM A 完成操作後,VM B 多久看到結果,VM B 每 10 毫秒輪詢一次,時鐘同步後各測 3 輪以上。

模式新增、修改刪除
cache=auto-29~32 ms389~797 ms
cache=never-25~-12 ms-16~-5 ms

時間是 VM A 操作返回後才記下的,兩台 VM 之間也還有時鐘差,所以會出現負值,這些數字只能看量級,分辨不出數十毫秒內的差異。新增與修改看不出差別,主要差別是刪除:auto 要等數百毫秒,符合 1 秒快取時間的範圍,never 則沒有明顯延遲。

這個延遲會影響實際流程。測試時曾經剛刪除檔案就立刻重建同名檔,兩輪都剛好只留下 5000 行,雜湊值也相同。我研判是另一台還開到舊檔,寫入隨舊檔消失,但沒有單獨驗證。另外 auto 下也觀察到 1 次,VM A 設定 ACL 後,VM B 馬上看到的還是舊內容。

效能

fio 只在 VM A 上執行,兩台 VM 分別放在不同節點,auto 與 never 各 2 輪。參數是 ioengine=libaio、size=512M、runtime=15、time_based、end_fsync=1,1M 的 iodepth 是 8,4K 是 16,除了標明 direct=0 的那一列都是 direct=1。循序項目是 fio 輸出換算的 MiB/s,隨機項目是 IOPS。

項目cache=autocache=never
循序寫入 1M970~1258 MiB/s1822~1937 MiB/s
循序寫入 1M(direct=0)427~448 MiB/s761~790 MiB/s
隨機寫入 4K6111~8838 IOPS41871~52010 IOPS
循序讀取 1M3090~3328 MiB/s3186~3251 MiB/s
隨機讀取 4K63553~68856 IOPS65775、25845 IOPS

這份數字有幾個限制,不能直接當成 Ceph 的速度:

  • direct=1 沒有穿透到 PVE 主機。 PVE 預設不傳 --allow-direct-io,virtiofsd 會拿掉 O_DIRECT,所以寫入與讀取都可能含 PVE 主機端的快取。要讓它生效得另外設 direct-io=1,這次沒有測。
  • 輪數少,環境也不乾淨。 叢集上同時有其他負載,每項只跑 15 秒。never 的隨機讀取有一輪 25845 IOPS 偏低,原因不明。
  • direct=0 那一列的路徑不同。 never 在 VM 內一律走直接 I/O,即使 fio 設 direct=0 也不會用 VM 的快取,所以這一列不是相同條件的比較。依 fio 文件,libaio 搭配非直接 I/O 也可能達不到指定的 iodepth。
  • never 的寫入反而較快。 這與一般預期不同,原因我沒有查明,上線前要用實際工作負載再驗證。

模式選擇與 mmap

VM 之間共用時,我會選 cache=never。理由是刪除沒有明顯延遲,也符合 virtiofsd 對共用目錄的說明,效能數字不是主要依據。

不過 never 有一個要先確認的限制:共用記憶體對應。檔案走直接 I/O 時,MAP_SHARED 要有 --allow-mmap 才能使用,否則 Linux 核心會回傳 ENODEV,而 PVE 目前沒有傳這個參數,virtiofsd 的文件也說只有獨占目錄時才應該開啟。這點是依原始碼判斷,沒有實測,應用程式有用到 mmap 的話要先驗證。

權限與安全

跨 VM 的權限測試:UID 只比對數字,VM 的 root 建立的檔案在 PVE 主機上也是 UID 0
跨 VM 的權限測試:UID 只比對數字,VM 的 root 建立的檔案在 PVE 主機上也是 UID 0
  • UID 只比對數字。 VM A 的 alice(2001)建立的檔案,在 VM B 顯示為 bob(2001),bob 也能寫入。所有 VM 的 UID、GID 要一致。
  • 一般權限與 ACL 有效。 carol(2002)寫入被拒,以 ACL 開放後就能寫入,PVE 主機上也看得到 + 標記。
  • 沒有 root squash。 VM 的 root 可以讀別人的 600 檔案,建立的檔案在 PVE 主機上是 UID 0。
  • SUID 位元會保留,而且跨 VM 有效。 VM 內預設掛接沒有 nosuid。VM A 的 root 在共用目錄放入 SUID root 的檔案,VM B 的一般使用者執行時就具有 root 權限(euid=0)。
  • VM 只看得到共用目錄。 virtiofsd 預設以 namespace 沙箱把共用目錄當成根目錄,PVE 的啟動參數沒有改這項預設值。

SUID 的部分我再用 /usr/bin/id 的複本實測,檔案由 VM A 的 root 放入,權限設成 4755,再由 VM B 的一般使用者 carol 執行:

# 共用目錄內的檔案
-rwsr-xr-x 4755 0:0 /mnt/share/suidtest/id-suid

# VM B 掛接沒有 nosuid
uid=2002(carol) gid=2002(carol) euid=0(root) groups=2002(carol)

# VM B 掛接加上 nosuid
uid=2002(carol) gid=2002(carol) groups=2002(carol)

也就是取得其中一台 VM 的 root,就能影響其他沒有加 nosuid 的 VM。加上之後檔案仍可以執行,SUID 位元也還在,只是不會取得權限,而且只保護有加的那台 VM,所以共用同一個目錄的 VM 都要加。

PVE 主機上的 CephFS 掛接選項同樣沒有 nosuid,請勿在 PVE 主機上執行共用目錄裡的檔案。這部分我只查看掛接選項與檔案權限,沒有在 PVE 主機上執行。

PVE 目前的 virtiofs 設定只有 dirid、cache、direct-io、expose-xattr、expose-acl。virtiofsd 本身的 --readonly、--translate-uid 等參數,PVE 沒有提供設定方式。

所以用途是共用讀取時,只在 VM 內用 ro 掛接並不夠,VM 的 root 可以自行重新掛接,這不是安全邊界。要強制唯讀得在 PVE 主機端處理,例如把唯讀的 bind mount 交給目錄對映(Directory Mapping),這個做法這次沒有測。

在沒有這類限制的情況下,可以把 VM 的 root 視為擁有整個共用目錄的權限,不同信任等級的 VM 不要共用同一個目錄。

遷移與快照

項目結果說明
線上遷移不支援can't migrate VM which uses local devices: virtiofs
含 RAM 的快照不支援官方文件也列為限制
休眠不支援依錯誤訊息與官方文件,沒有實測
不含 RAM 的快照本次的 VM 磁碟可以共用目錄不是 VM 磁碟,研判不含在快照內,沒有驗證
離線遷移可以實際遷移 2 秒,移過去後資料還在
記憶體熱插拔不支援依官方文件,沒有實測
線上遷移與含 RAM 快照都被擋下,關機後的離線遷移則可以完成
線上遷移與含 RAM 快照都被擋下,關機後的離線遷移則可以完成

virtiofsd 本身已有遷移相關的功能(--migration-mode 預設為 find-paths),不過 PVE 目前仍會擋下使用 virtiofs 的 VM 線上遷移。我在 2026/11/01 查閱時,PVE 的 Roadmap 列有未來解除線上遷移、含 RAM 快照與休眠的限制,規劃時可以留意之後的版本。

官方文件另外提醒,virtiofsd 當掉或沒有回應時,VM 內的掛接點會卡住,當掉的情況要等 VM 完全停止才會恢復。

適用範圍

適合的情境:

  • 多台 VM 讀取同一份資料
  • 每台 VM 只寫自己的目錄或檔案
  • 上傳、下載為主,幾乎不會同時改同一個檔案的服務,例如 FTP(這類服務這次沒有實測)

不適合的情境:

  • 多台 VM 共用的資料庫、SQLite,或任何靠檔案鎖協調跨 VM 寫入的應用
  • 多台 VM 寫同一個記錄檔
  • 需要線上遷移的 VM
  • 刪除後要立刻在另一台重建同名檔案,且使用 cache=auto 的流程
  • 不同信任等級的 VM 共用同一個目錄

需要跨 VM 的鎖或線上遷移時,可以改在 VM 內直接掛接 CephFS。鎖在原生掛接的對照組有效,但這次只在 PVE 主機上驗證,還沒有在 VM 內實測;直接掛接的權限限制可以參考我之前寫的PVE 掛接外部 Ceph 與 CT 直接掛接 CephFS。

注意鎖有效只是條件之一,不代表資料庫都能跨 VM 共用資料檔,仍要看應用程式是否支援。例如 SQLite 的 WAL 模式要求所有程序在同一台主機上,不能用在網路檔案系統。

建置步驟

以下先寫 WebUI 的操作位置,再附上對應的指令,兩者擇一即可。沒有介面可以操作的步驟(建立目錄、VM 內掛接)才只有指令,PVE 主機上的指令可以在「節點 → 命令列」執行。

範例名稱請依環境替換。介面名稱以 PVE 9.2.24 的繁體中文介面為準。這次實測是用指令建置,WebUI 的位置與欄位我在該版本的介面上核對過,但沒有從 WebUI 送出這些設定;離線遷移的「離線」模式只對照過介面原始碼。圖中的對話框填的是範例值,節點名稱與 IP 也已換成範例值,截圖後都沒有送出。

建立新的 CephFS 會佔用一個 MDS,測試時待命 MDS 從 3 台變成 2 台。

事前檢查

WebUI:

  1. 「資料中心 → Ceph」:確認狀態是 HEALTH_OK。
  2. 「節點 → Ceph → CephFS」:確認名稱沒有重複,同一頁的「中繼資料伺服器」要有狀態為 up:standby 的 MDS。

對應指令:

ceph health                                  # 要是 HEALTH_OK
ceph fs ls                                   # 確認名稱沒有重複

以下幾項沒有對應的 WebUI,每個會用到的節點都要裝 virtiofsd:

cp -p /etc/pve/storage.cfg /root/storage.cfg.bak-$(date +%Y%m%d)
apt install virtiofsd
ceph fs dump | grep enable_multiple          # 叢集已有 CephFS 時要確認

叢集已經有 CephFS 時,要 enable_multiple 開啟才能建立第二個,否則建立會失敗。如果顯示 0,先評估後再執行 ceph fs flag set enable_multiple true。

同時確認 VM 的應用不依賴跨 VM 檔案鎖,也不需要線上遷移。

建立 CephFS 與 PVE 儲存

WebUI:

  1. 「節點 → Ceph → CephFS → 建立 CephFS」:名稱填 cephfs-test,Placement Groups 填 16(表單預設 128),取消勾選「加入為儲存」,因為下一步要自己設定內容類型。
  2. 等同一頁的「中繼資料伺服器」出現 up:active (cephfs-test)。
  3. 「資料中心 → 儲存 → 增加 → CephFS」:ID 填 cephfs-test,「使用 Proxmox VE 管理超融合 cephFS」保持勾選,檔案系統名稱選 cephfs-test,內容只留「程式碼片段」(表單預設是「備份」,要取消),節點選 pve1、pve2。
  4. 到左側每個節點底下,確認都出現 cephfs-test 儲存且狀態正常。
建立 CephFS:取消勾選「加入為儲存」,下方可以看到各 MDS 的狀態
建立 CephFS:取消勾選「加入為儲存」,下方可以看到各 MDS 的狀態
資料中心 → 儲存 → 增加 → CephFS:選檔案系統與節點,內容只留程式碼片段
資料中心 → 儲存 → 增加 → CephFS:選檔案系統與節點,內容只留程式碼片段

對應指令:

pveceph fs create --name cephfs-test --pg_num 16 --add-storage 0
ceph fs status cephfs-test                   # 確認已有 active MDS 再往下做
pvesm add cephfs cephfs-test --fs-name cephfs-test --content snippets \
  --nodes pve1,pve2
pvesm status --storage cephfs-test
mountpoint /mnt/pve/cephfs-test              # 每個節點都要確認
  • pveceph fs create 需要有待命的 MDS,否則會直接中止。
  • --pg_num 16 是測試用的數量,正式環境要依資料量規劃。metadata 集區建立時的 PG 由 PVE 決定,是 data 集區的四分之一,最少 8。這些都是建立時的初始值,autoscaler 開啟時 PG 可能再調整,測試環境的 metadata 集區 pg_num_min 也是 16。
  • 這個儲存只是讓 PVE 負責掛接,內容類型我只開 snippets,避免被拿來放 ISO 或備份。

建立共用目錄與目錄對映

共用目錄要先建好,WebUI 沒有建立目錄的功能,在任一節點的「命令列」執行一次即可,CephFS 上的目錄各節點共用:

findmnt -t ceph /mnt/pve/cephfs-test && \
  mkdir -p /mnt/pve/cephfs-test/vm-share && \
  chown <UID>:<GID> /mnt/pve/cephfs-test/vm-share && \
  chmod 770 /mnt/pve/cephfs-test/vm-share

接著建立目錄對映(Directory Mapping)。

WebUI:

  1. 「資料中心 → 目錄對映 → 增加」:名稱填 cephfs-test,節點選 pve1,路徑填 /mnt/pve/cephfs-test/vm-share。
  2. 在清單 cephfs-test 那一列的「動作」欄按「+」圖示(增加新的主機對映),節點選 pve2,路徑相同。其他節點比照辦理。
資料中心 → 目錄對映 → 增加:填名稱、節點與路徑
資料中心 → 目錄對映 → 增加:填名稱、節點與路徑
在對映那一列按「+」,增加其他節點的路徑
在對映那一列按「+」,增加其他節點的路徑

對應指令:

pvesh create /cluster/mapping/dir --id cephfs-test \
  --map node=pve1,path=/mnt/pve/cephfs-test/vm-share \
  --map node=pve2,path=/mnt/pve/cephfs-test/vm-share
  • 擁有者與權限依應用規劃。測試時我用 777 方便驗證權限,正式環境不要這樣設。
  • 會遷移到的節點都要列在目錄對映內,也要掛接好 CephFS。
  • mkdir 前一定要確認 CephFS 已掛接,否則目錄會建在 PVE 主機的本機磁碟上。
  • 路徑建議用 CephFS 內的子目錄,不要直接指到掛接點。CephFS 沒掛接時子目錄不存在,比較不會誤把本機的空目錄共用給 VM。這只是輔助防護,目錄對映不會確認路徑屬於哪個檔案系統,這點我也沒有實測。

把 VirtioFS 接到 VM

WebUI:

  1. 選取 VM,按右上角「關機」。
  2. 「VM → 硬體 → 增加 → Virtiofs」:目錄 ID 選 cephfs-test。
  3. 勾選視窗右下角的「進階」,快取選 never,勾選「POSIX ACL」(會連帶啟用 xattr 支援)。
  4. 按右上角「啟動」。
Virtiofs 在 VM 硬體的「增加」選單內,圖中已有的 virtiofs0 是測試 cache=auto 時的設定
Virtiofs 在 VM 硬體的「增加」選單內,圖中已有的 virtiofs0 是測試 cache=auto 時的設定
目錄 ID 選目錄對映的名稱,快取選 never,勾選 POSIX ACL;右下角的「進階」要勾選才會顯示這些選項
目錄 ID 選目錄對映的名稱,快取選 never,勾選 POSIX ACL;右下角的「進階」要勾選才會顯示這些選項

之後要改設定,在「VM → 硬體」選取該列再按「編輯」。「增加」選單裡的 Virtiofs 需要節點的 Sys.Console 權限,沒有權限時會是灰色。

對應指令:

qm shutdown <vmid> --timeout 120 && \
  qm set <vmid> --virtiofs0 dirid=cephfs-test,cache=never,expose-acl=1 && \
  qm start <vmid>

virtiofsd 實際的參數在 WebUI 看不到,要在 VM 所在節點確認:

ps -ww -o args= -C virtiofsd                 # 確認 --cache=never

改 cache 模式一樣要關機再開機,virtiofsd 才會用新參數。Windows VM 不要開 POSIX ACL(expose-acl),依 qemu-server 9.2.13 的原始碼,開了之後 VM 啟動時會直接報錯,要求關閉這個選項。

在 VM 內掛接

這一步在 VM 內操作,PVE 的 WebUI 沒有對應功能。硬體設定裡的目錄 ID(dirid)也是 VM 內掛接時用的名稱。

mkdir -p /mnt/share
mount -t virtiofs -o nosuid cephfs-test /mnt/share
findmnt /mnt/share

永久掛接寫入 /etc/fstab:

cephfs-test /mnt/share virtiofs rw,relatime,nosuid,nofail 0 0

目錄已經手動掛接的話,寫入後先卸除,再用 fstab 掛接一次,確認內容可以正常使用:

systemctl daemon-reload
umount /mnt/share
mount -a
findmnt /mnt/share

這段設定是文章發佈後補測的(2026/11/02,cache 為 auto)。兩台 VM 各重新開機 3 次,再各以 qm shutdown、qm start 關機後開機 1 次,這時 virtiofsd 也會重新啟動。8 次都自動掛接完成,mnt-share.mount 正常,關機後開機的那一輪也確認兩台可以互相讀寫:

TARGET     SOURCE      FSTYPE   OPTIONS
/mnt/share cephfs-test virtiofs rw,nosuid,relatime

nofail 讓 VM 在掛接失敗時仍能開機,但服務可能把資料寫進本機的 /mnt/share 空目錄,之後掛接成功又被遮住。我把 fstab 的目錄 ID 改成不存在的名稱來模擬掛接失敗,結果如下:

  • VM 開機。 開機流程沒有卡住,mnt-share.mount 是 failed,systemctl is-system-running 是 degraded。
  • 沒有防護的寫入。 寫入 /mnt/share 不會報錯,檔案落在根檔案系統。重新掛接後這個檔案就被遮住,卸除後才看得到。
  • 加上相依設定的服務。 服務沒有啟動,記錄是 Dependency failed。之後手動掛接回來,服務仍是停止狀態,要自己啟動。掛接正常時,服務開機後可以正常啟動並寫入。

依賴這個目錄的 systemd 服務,要在 [Unit] 段落加上 RequiresMountsFor=/mnt/share,改完執行 systemctl daemon-reload。這只處理啟動時的相依關係,virtiofsd 之後卡住它偵測不到。

這裡模擬的只有目錄 ID 不符,CephFS 還沒就緒或 virtiofsd 異常的情況沒有測到,正式環境的服務也請自行驗證一次。

上線前驗證

  • 重新開機,確認 fstab 自動掛接完成,findmnt /mnt/share 的選項有 nosuid。
  • 兩台互相寫入和讀取,確認內容一致。
  • 在 PVE 主機用 ls -ln /mnt/pve/cephfs-test/vm-share 查看擁有者與權限。
  • 用不同 UID 驗證允許與拒絕的情況都符合預期。
  • 對執行中的 VM 按「遷移」,視窗會列出 virtiofs 造成的錯誤,「遷移」按鈕也無法按下;指令 qm migrate <vmid> <node> --online 同樣會被擋下。維運人員要知道改用離線遷移。
  • 在「資料中心 → Ceph」確認狀態是 HEALTH_OK,指令是 ceph health。
  • 重新啟動 PVE 節點,確認 CephFS 掛接完成、VM 能正常啟動,也要確認 CephFS 還沒就緒時 VM 的行為符合預期。目錄對映只檢查路徑存在且是目錄,建立時的檢查不能保證之後每次開機都正常。
  • 將共用目錄另外納入備份,不要把 VM 備份或快照當成共用目錄的備份。還原 VM 時共用資料不會跟著回復,有一致性需求要自行協調時間點。
對執行中的 VM 開啟遷移視窗,會列出 virtiofs 相關的錯誤,遷移按鈕也無法按下
對執行中的 VM 開啟遷移視窗,會列出 virtiofs 相關的錯誤,遷移按鈕也無法按下

離線遷移

WebUI:

  1. 選取 VM,按右上角「關機」。
  2. VM 關機後按「遷移」,目標節點選好,模式會顯示「離線」,再按「遷移」。
  3. 完成後在目標節點上按「啟動」。

對應指令:

qm shutdown <vmid> --timeout 120 && \
  qm migrate <vmid> <目標節點> && \
  ssh root@<目標節點> "qm start <vmid>"

遷移後從外部確認 IP 與服務連接埠,再到 VM 內確認 /mnt/share 已掛接、資料還在。

測試環境要移除時,請依官方文件的 Destroy CephFS 步驟進行,刪除後資料無法復原。

LXC 的做法

LXC 不支援 VirtioFS,我用主機掛接加 bind mount 測試,PVE 主機與 CT 可以互相讀到對方寫的檔案。

使用預設 ID 對應時,非特權 CT 內的 UID 0 對應到主機的 100000,所以測試時共用目錄先 chown 100000:100000,實際的擁有者要依 CT 的對應與應用程式帳號規劃。CT 內也不需要 Ceph 金鑰。bind mount 的內容不會被 vzdump 備份,共用目錄要另外備份。

這部分只驗證到互通與權限。CT 遷移、節點故障接手、多個 CT 同時寫入、檔案鎖與 ACL 都還沒測。

測試方法與限制

過程中有幾批數據被我排除,以免得出錯誤結論:

  • cache 沒有切換的整輪。 第一次重跑時 cache 沒有真的切換,也沒有遷移,整批丟棄。之後每輪前後都核對 VM 所在節點與 virtiofsd 的 cache 參數。
  • VM 剛開機的時鐘。 可見延遲出現 -780 ms 這類不可能的值,該批延遲作廢。重測前先等 90 秒並確認 NTP 已同步。同節點的延遲沒有重測,所以本文不採用。
  • 看不出交錯的輪次。 跨節點 auto 與 never 各有一輪得到 10000 行,兩輪的雜湊完全相同,內容沒有交錯,也沒有留下能確認兩邊同時寫入的記錄,所以不當成沒有遺失的證據。之後要重測,應該加上起跑同步與每行的時間記錄。
  • 寫到舊檔的輪次。 就是前面提到剛好 5000 行的兩輪。
  • 卡住的一輪。 跨節點 never 的 O_APPEND 有一輪卡住約 2 小時 45 分,VM 內的程式已結束,但 SSH 沒有關閉。該輪數據不採用。後續 4 輪(包含上面排除的那一輪 10000 行)沒有重現,原因不明,不能排除和 VirtioFS 有關。

沒有測到的項目:

  • PVE 節點重新啟動後的自動掛接與 VM 啟動
  • 從 WebUI 送出的建置流程(這次用指令建置,介面只核對位置與欄位)
  • mmap
  • HA 故障移轉、節點故障、MDS 故障切換
  • 實際應用程式(FTP、Web 等)
  • 在 VM 內直接掛接 CephFS
  • 長時間穩定性與大量小檔案

結論

VirtioFS 加 CephFS 最大的好處是 VM 內不用設定 Ceph,也拿不到金鑰,對只需要讀取共用資料,或各寫各的 VM 來說很方便。

但它的語意和原生 CephFS 不同。檔案鎖不會跨 VM,這點在現有的 virtiofsd 上沒有選項可以調整。應用程式如果靠鎖協調跨 VM 的寫入,就有資料損毀的風險,而且取得鎖時不會報錯。我的做法是先確認應用程式有沒有用到檔案鎖,再決定要用 VirtioFS 還是在 VM 內直接掛接 CephFS。

另外要接受的是維運上的取捨:VM 不能線上遷移,節點維護時就要排關機時間。如果 VM 依賴線上遷移,這個方案目前就不適合。

參考資料

Jason Cheng

Jason Cheng

企業開源技術顧問,專注架構規劃與實務導入

關於作者 →

延伸閱讀