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 開機時,PVE 會替每個 virtiofs 裝置啟動一個 virtiofsd。VM 內的檔案操作透過 virtio 交給 virtiofsd,再由它在 PVE 主機的目錄上執行。
CephFS 是由 PVE 主機掛接,VM 接觸不到 Ceph,也沒有金鑰。測試環境的主機端用 name=admin 身分掛接。

檔案鎖
第一個測試很單純:VM A 持有鎖,VM B 以非阻塞方式嘗試取得同一把鎖,flock 與 POSIX 鎖(lockf)都測。
| 情境 | flock | POSIX 鎖 |
|---|---|---|
跨節點 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 鎖的是整個檔案。
| 情境 | flock | POSIX 鎖 |
|---|---|---|
| CephFS 原生掛接,跨節點 | 800 | 800 |
| 同一台 VM 內 2 個程序 | 800 | 800 |
跨節點,cache=auto | 5、43 | 3、10 |
跨節點,cache=never | 63、5 | 16、89 |
同節點,cache=auto | 27 | 13 |
同節點,cache=never | 32 | 55 |
這些數字只能說明鎖沒有保護到資料,不是遺失比例。計數程式讀到空檔時會從 0 重算,所以最終值會特別低。


原因可以從原始碼找到。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=auto | 5390、5333 | 151、256 |
跨節點,cache=never | 5177、5047、5273 | 0 |
同節點,cache=auto | 10000(1 次) | 264 |
同節點,cache=never | 10000(1 次) | 0 |
| 同一台 VM 內 2 個程序 | 10000 | 0 |
| CephFS 原生掛接,跨節點 | 8071、10000、5739 | 0 |
跨節點 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 ms | 389~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=auto | cache=never |
|---|---|---|
| 循序寫入 1M | 970~1258 MiB/s | 1822~1937 MiB/s |
循序寫入 1M(direct=0) | 427~448 MiB/s | 761~790 MiB/s |
| 隨機寫入 4K | 6111~8838 IOPS | 41871~52010 IOPS |
| 循序讀取 1M | 3090~3328 MiB/s | 3186~3251 MiB/s |
| 隨機讀取 4K | 63553~68856 IOPS | 65775、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 的話要先驗證。
權限與安全

- 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 秒,移過去後資料還在 |
| 記憶體熱插拔 | 不支援 | 依官方文件,沒有實測 |

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


對應指令:
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:
- 「資料中心 → 目錄對映 → 增加」:名稱填
cephfs-test,節點選 pve1,路徑填/mnt/pve/cephfs-test/vm-share。 - 在清單
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:
- 選取 VM,按右上角「關機」。
- 「VM → 硬體 → 增加 → Virtiofs」:目錄 ID 選
cephfs-test。 - 勾選視窗右下角的「進階」,快取選
never,勾選「POSIX ACL」(會連帶啟用 xattr 支援)。 - 按右上角「啟動」。


之後要改設定,在「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 時共用資料不會跟著回復,有一致性需求要自行協調時間點。

離線遷移
WebUI:
- 選取 VM,按右上角「關機」。
- VM 關機後按「遷移」,目標節點選好,模式會顯示「離線」,再按「遷移」。
- 完成後在目標節點上按「啟動」。
對應指令:
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 依賴線上遷移,這個方案目前就不適合。
參考資料
- Proxmox VE 文件:Virtiofs
https://pve.proxmox.com/pve-docs/chapter-qm.html#qm_virtiofs - Proxmox VE 文件:CephFS
https://pve.proxmox.com/pve-docs/chapter-pveceph.html#pveceph_fs - Proxmox VE Roadmap
https://pve.proxmox.com/wiki/Roadmap - virtiofsd 專案與參數說明
https://gitlab.com/virtio-fs/virtiofsd - virtiofsd 1.13.2 原始碼:鎖的介面
https://gitlab.com/virtio-fs/virtiofsd/-/blob/v1.13.2/src/filesystem.rs - virtiofsd 1.13.2 原始碼:cache 模式與 O_APPEND
https://gitlab.com/virtio-fs/virtiofsd/-/blob/v1.13.2/src/passthrough/mod.rs - qemu-server 9.2.13 原始碼:PVE 啟動 virtiofsd 的參數與 Windows ACL 檢查
https://git.proxmox.com/?p=qemu-server.git;a=blob;f=src/PVE/QemuServer/Virtiofs.pm;hb=71ea0ac7192bb935f63cb64beb59e87d6cecd3bf - Ceph 文件:Multiple Ceph File Systems
https://docs.ceph.com/en/squid/cephfs/multifs/ - fio 文件:I/O depth
https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-depth - SQLite 文件:Write-Ahead Logging
https://www.sqlite.org/wal.html - Linux 6.8 原始碼:FUSE 直接 I/O 與 mmap
https://github.com/torvalds/linux/blob/v6.8/fs/fuse/file.c - PVE 掛接外部 Ceph 與 CT 直接掛接 CephFS
https://blog.jason.tools/2026/10/pve-external-ceph-cephfs.html



