home-lab 2.0 part 5: 儲存與網路效能實測 — 量化每一層抽象化的代價

home-lab 2.0 part 5: 儲存與網路效能實測 — 量化每一層抽象化的代價

大家好,好久沒有發新文章。距離上一次翻新家庭實驗室的文章 (part 4) 也過了一段時間,這陣子除了陸續把一些新服務遷移到叢集上,也一直在思考一個核心問題:我在架構設計時所做的技術選型,到底讓效能付出了多少代價?

既然要折騰,就乾脆折騰到底。這篇文章就來記錄一下,我如何利用 fio 與 iperf3 這兩個工具,量化 Proxmox 虛擬化環境中、Kubernetes 儲存與網路層的效能損耗。

TL;DR

  1. 虛擬化開銷顯著:在 4K 隨機讀取高壓力測試下,VM 內的儲存效能約剩 Host 實體的 36%。
  2. Longhorn v2 vs v1:SPDK 引擎帶來革命性提升,v2 的 IOPS 是 v1 的 2.5~3 倍,延遲降低約 60%,但代價是更多的抽象層損耗。
  3. 多一層軟體定義儲存的代價:Longhorn v2 在 VM 之上又多了一層 SDS 開銷,最終 IOPS 僅剩 Host 的 ~13%。
  4. 最終配置:TopoLVM 負責高效能負載(RWO),Longhorn v1 負責需要 RWX 共享的場景;HA 由上層應用自己實現。
  5. CNI 選型:跨節點 TCP 效能 Cilium 略勝,但 UDP 高負載場景有 25% 掉包問題;Flannel host-gw 在 UDP 穩定性上表現更好。同節點 Cilium 可達 26 Gbps+,損耗極低。

現況回顧

在 part 4 中,我選擇了 Flannel (host-gw) 作為 CNI 方案,以及 TopoLVM 作為儲存方案。當時的選型邏輯是:

  • Flannel host-gw:在同個 L2 網段中,直接利用主機路由表轉發封包,完全沒有封裝與解封裝的負擔。
  • TopoLVM:使用 Linux LVM 機制,相較於 NFS 或 Longhorn,I/O 效能更好,且支援容量感知調度。

但這些都是「理論上」的好處。我很好奇,在實際跑起來之後,這些設計抉擇到底讓我的應用付出了多少效能?於是決定實際測量一番。


工具箱:壓測武器介紹

在開始看數據前,先介紹一下這次使用的兩款工具。

1. fio (Flexible I/O Tester) — 磁碟的健身教練

fio 可以模擬各種讀寫情況,是業界最標準的儲存效能測試工具。

安裝方式:

# Debian / Ubuntu
sudo apt update && sudo apt install fio

# CentOS / RHEL / Fedora
sudo dnf install fio

# macOS
brew install fio

常用參數說明:

參數 說明
--name 測試任務的名稱
--filename 測試對象(檔案路徑或硬碟裝置如 /dev/sdb)
--rw 讀寫模式:read, write, randread, randwrite, randrw
--bs 每次讀寫的 Block Size,如 4k, 128k, 1m
--size 測試檔案的總大小,如 1g, 10g
--ioengine I/O 引擎,Linux 下用 libaio 效能最好
--direct=1 繞過系統快取,直接對硬碟進行測試
--iodepth I/O 佇列深度,數值越高對硬碟壓力越大
--numjobs 同時運行的執行緒數量
--runtime 測試持續時間(秒)
--group_reporting 匯總所有 jobs 的結果
stonewall 等待前一個任務完成後才開始執行,確保任務依序進行並分隔報告群組

常見測試範例:

# 4K 隨機讀取測試
fio --name=randread_test --filename=/path/to/test_device --direct=1 --rw=randread --bs=4k --size=4g --numjobs=4 --iodepth=32 --runtime=60 --group_reporting

# 4K 隨機寫入測試
fio --name=randwrite_test --filename=/path/to/test_device --direct=1 --rw=randwrite --bs=4k --size=4g --numjobs=4 --iodepth=32 --runtime=60 --group_reporting

# 70% 讀取 / 30% 寫入混合測試
fio --name=mixed_test --filename=/path/to/test_device --direct=1 --rw=randrw --rwmixread=70 --bs=4k --size=4g --numjobs=4 --iodepth=32 --runtime=60 --group_reporting

⚠️ 小提醒: 測試時請務必指定一個安全的路徑或不包含重要資料的裸裝置 (/dev/sdx)!寫入測試會把裡面的資料全部覆蓋喔!

如何看懂 fio 報告?

Jobs: 4 (f=4): [r(4)][100.0%][r=134MiB/s,w=0KiB/s][r=34.3k,w=0 IOPS]
...
  read: IOPS=34.3k, BW=134MiB/s (141MB/s)(8043MiB/60001msec)
...
  lat (usec): min=2, max=3278, avg=92.51, stdev=45.20
  • IOPS:每秒讀寫次數,越高代表處理零碎檔案能力越強。
  • BW (Bandwidth):頻寬,越高代表傳輸大檔案的速度越快。
  • lat (Latency):延遲,每次讀寫操作的平均反應時間,越低越好。

Job File 進階用法:

如果每次都要打一長串指令很麻煩。我們可以把設定寫在一個檔案裡,讓 fio 直接讀取。例如 my_test.fio:

# This is a sample FIO job file for mixed random I/O testing.
[global]
# These settings apply to all jobs defined below.
ioengine=libaio
direct=1
size=2g
runtime=60
group_reporting

[random-read-test]
# Job definition for random read test
stonewall
rw=randread
bs=4k
iodepth=64
numjobs=4

[random-write-test]
# Job definition for random write test
stonewall
rw=randwrite
bs=4k
iodepth=64
numjobs=4

然後執行:

fio --filename=/path/to/your/test_device my_test.fio

2. iperf3 — 網路頻寬的測速儀

iperf3 採用 Client-Server 架構,是測量網路吞吐量的標準工具。

基本用法:

# Server 端
iperf3 -s

# Client 端
iperf3 -c [SERVER_IP]

常用參數說明:

參數 說明
-p [埠號] 指定連接埠,預設是 5201
-t [秒數] 測試持續時間
-i [秒數] 每隔幾秒回報一次結果
-P [數量] 平行連線數量(多線程測試)
-u 切換為 UDP 模式
-b [頻寬] UDP 模式下的目標頻寬
-R 反向模式(測試下載速度)

第一部分:磁碟效能對決

測試環境

  • Host: Lenovo ThinkCentre M910x (Intel 4C8T, 8 threads)
  • Disk: KIOXIA EXCERIA G2 NVMe SSD (官方標稱 360,000 IOPS)
  • OS: Proxmox 8.x (ext4 on LVM-Thin)
  • VM: Ubuntu 24.04, VirtIO SCSI single (No cache, IO Thread enabled)

我們的目標是比較三個層級的效能:

  1. Proxmox Host (基準線):硬體的物理極限。
  2. K8s VM (Local Path Provisioner):最輕量化的 K8s 儲存方案。
  3. K8s VM (Longhorn V2):分散式儲存,帶有 SPDK V2 引擎。

參考文件:Longhorn V2 Data Engine

自動化測試腳本:all_tests.fio

經過前面的參數調校,我找出對這台 4C8T Intel CPU + ext4 檔案系統機器最有效的「黃金參數」:numjobs=8 --iodepth=32。基於此,我設計了一套完整的測試腳本:

# FIO Ultimate Challenge Script v2
# Complete storage benchmark suite

[global]
# Common parameters for all tests
ioengine=libaio
direct=1
group_reporting
time_based
runtime=60
size=4g

# ====================================================================
# Part 1: Standard Benchmarks
# ====================================================================

[4k-random-read-iops]
description="Standard 4K Random Read test to measure max IOPS"
rw=randread
bs=4k
numjobs=8
iodepth=32
stonewall

[4k-random-write-iops]
description="Standard 4K Random Write test to measure max write IOPS"
rw=randwrite
bs=4k
numjobs=8
iodepth=32
stonewall

[128k-sequential-read-throughput]
description="Standard 128K Sequential Read test for max bandwidth"
rw=read
bs=128k
numjobs=1
iodepth=1
size=10g
stonewall

[128k-sequential-write-throughput]
description="Standard 128K Sequential Write test for max write bandwidth"
rw=write
bs=128k
numjobs=1
iodepth=1
size=10g
stonewall

# ====================================================================
# Part 2: Mixed & Real-World Workload Simulations
# ====================================================================

[web-server-workload]
description="Mixed 90% Read / 10% Write, simulates a typical web server"
rw=randrw
rwmixread=90
bs=4k
numjobs=8
iodepth=32
stonewall

[database-oltp-workload]
description="Simulates an OLTP Database workload with high concurrency"
rw=randrw
rwmixread=70
bs=4k
numjobs=8
iodepth=128
size=30g
runtime=120
stonewall

[virtualization-vdi-workload]
description="Simulates a VDI workload with mixed block sizes and high concurrency"
rw=randrw
rwmixread=80
bsrange=4k-64k
numjobs=8
iodepth=256
size=30g
runtime=120
stonewall

使用方法:

# Proxmox Host
fio --filename=/root/fio.tmp all_tests.fio

# K8s VM Local Path
fio --filename=/var/mnt/local-path/testfile all_tests.fio

# K8s VM Longhorn V2
fio --filename=/var/mnt/longhorn/testfile all_tests.fio

下面是這個腳本跑出來前踩的一些坑,給大家笑一下。

階段一:初步測試 — 發現問題!

一開始我們沒有指定 --ioengine,導致 fio 使用預設的同步模式 (psync):

note: both iodepth >= 1 and synchronous I/O engine are selected, queue depth will be capped at 1
...
IO depths: 1=100.0%, 2=0.0%, 4=0.0%, ... 32=0.0%, >=64=0.0%

這就是說,不管我們把 --iodepth 設得多高,實際上都只有 1。讓我們看看這組「低壓力」數據:

測試對象 IOPS 頻寬 (BW) 平均延遲
Proxmox Host 1,257,000 4908 MiB/s 2.28 µs
K8s VM (Local Path) 27,100 106 MiB/s 145.74 µs
K8s VM (Longhorn V2) 11,000 42.9 MiB/s 361.90 µs

欸等等! 1,257,000 IOPS?這不是 SSD 的速度,這是 RAM 的速度!原因是因為我們測試路徑是 --filename=/tmp/fio/randread,而 /tmp 在 Linux 上通常是 tmpfs(記憶體檔案系統)!

這個「美麗的錯誤」讓我們意外確認了:伺服器的 CPU 和記憶體效能超級強!

階段二:CPU Bottleneck 探索 — 找出黃金參數

修正指令後,加入 --ioengine=libaio。一開始用 numjobs=4 --iodepth=32 測試,Proxmox Host 結果:

  • IOPS: 319,000
  • util=49.50%

util=49.50%?這很奇怪。明明 IOPS 和頻寬都很高,為什麼磁碟看起來只忙碌了一半的時間?

答案是:瓶頸在 CPU。看報告中的 cpu: usr=28.85%, sys=54.64%,加起來約 83%。這代表 CPU 已經拼盡全力在處理 I/O 中斷和系統呼叫,但 SSD 太快了、等不到 CPU 來餵它新任務。

這就像:披薩師傅 (SSD) 一秒能做 36 萬個披薩,但店員 (CPU) 來不及把訂單遞過去,師傅只好雙手叉腰等著。

讓我們試試看調整 numjobs 與 iodepth 的組合:

組合 IOPS BW 延遲 Util
numjobs=4, iodepth=32 319k 1247 MiB/s 397 µs 49.50%
numjobs=8, iodepth=16 352k 1377 MiB/s 361 µs 48.49%
numjobs=8, iodepth=32 400k 1564 MiB/s 635 µs 80.04%

當 numjobs=8, iodepth=32 時,IOPS 達到了巔峰的 400,000!已經超越了 KIOXIA G2 官方標稱的 360,000 IOPS!這證明了——只要 CPU 能更均勻地分配負載,SSD 的潛力還能再往上衝。

再試 numjobs=16, iodepth=32:

組合 IOPS BW 延遲 Util
numjobs=8, iodepth=32 400k 1564 MiB/s 635 µs 80.04%
numjobs=16, iodepth=32 406k 1584 MiB/s 1253 µs 49.93%

過飽和 (Oversaturation) 發生了! IOPS 幾乎沒有提升,但延遲翻倍、util 暴跌。這是因為 16 個 jobs 太多,CPU 忙著進行上下文切換,反而無法有效率地餵給 SSD。

結論:對於這個 4C8T Intel CPU + ext4 的組合,黃金參數是 numjobs=8 --iodepth=32。

階段三:完整測試結果

接下來使用 all_tests.fio 在三個平台上跑完所有測試項目。

VDI 測試說明:VDI 測試是專門設計來模擬 Hypervisor 管理大量虛擬桌面的場景,不適用於 K8s VM 環境,因此只在 Proxmox Host 上執行。

Proxmox Host 基準線

fio --filename=/root/fio.tmp all_tests.fio

K8s VM Local Path

fio --filename=/var/mnt/local-path/out.tmp all_tests.fio

K8s VM Longhorn V2 (輪詢模式)

StorageClass 關鍵參數:

parameters:
  dataEngine: v2
  backendStoreDriver: spdk
  numberOfReplicas: "1"

K8s VM Longhorn V2 (中斷模式)

StorageClass 關鍵參數:

parameters:
  dataEngine: v2
  backendStoreDriver: spdk
  v2-data-engine-interrupt: "true"
  numberOfReplicas: "1"

K8s VM Longhorn V1

StorageClass 關鍵參數:

parameters:
  dataEngine: v1
  numberOfReplicas: "1"

K8s VM Longhorn V2 RWX (NFS)

RWX 卷透過 share-manager Pod 內建的 NFSv4 伺服器實現。NFS 掛載選項使用了 nconnect=4 優化:

nfsOptions: vers=4.1,softerr,timeo=600,retrans=5,nconnect=4

nconnect=4 是 NFSv4.1 的王牌參數,允許客戶端和伺服器之間建立 4 條並行的 TCP 連線,大幅提升單一掛載點的網路吞吐量。

儲存效能終極天梯榜

測試場景 👑 Host 🥈 Local Path 🥉 v2 RWO (輪詢) ⚔️ v2 RWO (中斷) 💥 v2 RWX (基於中斷) 📜 v1 RWO
4K 隨機讀取 223k
(1.1ms)
96.4k
(2.6ms)
29.5k
(8.6ms)
27.1k
(9.4ms)
17.5k
(14.5ms)
9.4k
(26.8ms)
4K 隨機寫入 168k
(1.5ms)
94.4k
(2.7ms)
26.2k
(9.7ms)
28.9k
(8.8ms)
8.8k
(29.0ms)
10.1k
(25.2ms)
128K 循序讀取 613 MB/s
(201µs)
344 MB/s
(360µs)
206 MB/s
(601µs)
151 MB/s
(821µs)
295 MB/s
(418µs)
76 MB/s
(1.6ms)
128K 循序寫入 684 MB/s
(181µs)
595 MB/s
(208µs)
217 MB/s
(574µs)
126 MB/s
(986µs)
51.4 MB/s
(2.4ms)
80 MB/s
(1.5ms)
Web 伺服器 R: 233k (1.1ms)
W: 25.9k (0.1ms)
R: 77.7k (3.0ms)
W: 8.6k (2.8ms)
R: 18.4k (12.5ms)
W: 2.0k (12.1ms)
R: 16.5k (14.0ms)
W: 1.8k (13.6ms)
R: 18.1k (12.4ms)
W: 2.0k (15.2ms)
R: 8.7k (28.0ms)
W: 0.9k (11.5ms)
資料庫 OLTP R: 134k (5.9ms)
W: 57.5k (4.1ms)
R: 57.4k (12.6ms)
W: 24.6k (12.3ms)
R: 17.3k (41.4ms)
W: 7.4k (41.1ms)
R: 15.7k (45.7ms)
W: 6.7k (45.3ms)
R: 9.5k (72.8ms)
W: 4.1k (79.5ms)
R: 6.7k (109.2ms)
W: 2.9k (97.1ms)

深度分析

1. 虛擬化開銷:Local Path 約剩 40~60%

不論是純粹的 4K 隨機讀寫,還是模擬資料庫的 OLTP 負載,Local Path 的 IOPS 大約都落在 Host 效能的 40% 到 60% 之間。

這就是「虛擬化開銷 (Virtualization Overhead)」。I/O 請求需要走過一條更長的路:

fio (VM 內) -> VM 的作業系統 -> VirtIO 驅動 -> QEMU/KVM -> Proxmox Host 的作業系統 -> 實體硬碟

每一個環節的轉換和排程,都會增加延遲。

2. Longhorn v2 vs v1:SPDK 引擎的革命性突破

這是整個測試中最令人振奮的發現!

指標 v1 (iSCSI) v2 (SPDK) 提升幅度
4K 隨機讀取 IOPS 9.4k 29.5k 3.1x
4K 隨機寫入 IOPS 10.1k 26.2k 2.6x
延遲 (OLTP Read) 109.2ms 41.4ms 60% 降低
CPU 消耗 (高負載) 3441m (~3.5 核心) ~1000m (~1 核心) 71% 降低

v1 引擎因為嚴重依賴 Linux 核心的 iSCSI 和網路堆疊來處理 I/O,需要消耗大量 CPU 資源進行上下文切換、中斷處理和封包處理。而 v2 透過 SPDK (Storage Performance Development Kit) 繞過核心,直接在使用者空間用輪詢 (Polling) 方式操作 NVMe,大幅提升了效能和 CPU 效率。

3. v2 中斷模式:犧牲 10% IOPS,換取極致 CPU 效率

Longhorn v2 提供兩種 SPDK 工作模式:

輪詢模式 (預設):

  • 披薩師傅 (SPDK 引擎) 精神高度緊張,每分每秒都死盯著出單口看有沒有新訂單。只要一有訂單,他就能在 0.01 秒內光速抓起來開始做。
  • 優點:反應超級快,延遲極低。
  • 缺點:超級消耗體力 (CPU),就算沒客人也在一直盯著。

中斷模式:

  • 披薩師傅在出單口裝了一個小鈴鐺 (Interrupt)。平常沒事的時候,他就在旁邊悠閒地備料、擦桌子(CPU 可以去做別的事情)。只有當新訂單進來,鈴鐺響了,他才會跑過去接單。
  • 優點:超級節省體力 (CPU),沒事的時候 CPU 完全可以被釋放出來。
  • 缺點:反應慢了一點點,從鈴鐺響起到跑過去接單,中間會有一個微小的延遲。

測試結果證實:

模式 4K Rand Read 4K Rand Write CPU (idle→load)
v2 輪詢 29.5k IOPS 26.2k IOPS ~0m → ~1000m
v2 中斷 27.1k IOPS 28.9k IOPS ~19m → ~1029m

約 10% 的 IOPS 犧牲,換來了:

  • 負載時 CPU 佔用從 1000m 降到 ~1000m(差不多)
  • 閒置時 CPU 佔用從 ~400m 降到 ~19m(幾乎完全釋放!)

這對於 CPU 資源緊張的邊緣運算環境,是一個極具價值的取捨。

4. RWX (NFS) 的代價:share-manager 是隱藏 Boss

Longhorn 的 RWX 實現方式是:

  1. 先建立一個 RWO Longhorn 卷
  2. 啟動 share-manager Pod,裡面跑著 NFSv4 伺服器
  3. 將 RWO 卷掛載進 NFS 伺服器,再透過網路分享給多個 Pod
你的多個 Pods <--- (NFS) ---> share-manager Pod <--- (直接讀寫) ---> Longhorn Volume

在 NFS 測試期間監控 kubectl top:

instance-manager          share-manager
   948m (0.95 核心)    +    1816m (1.8 核心)
          = 2764m (2.76 核心) 總 CPU 消耗

share-manager 的 CPU 消耗幾乎是 instance-manager 的兩倍! NFS 轉譯層才是 RWX 效能的真正瓶頸。

RWX 與 RWO 中斷模式的直接對比

指標 v2 RWO (中斷) v2 RWX 損耗
4K 隨機讀取 IOPS 27.1k 17.5k -35%
4K 隨機寫入 IOPS 28.9k 8.8k -70%
128K 循序讀取 151 MB/s 295 MB/s +95% (快取加速!)
128K 循序寫入 126 MB/s 51.4 MB/s -59%

有趣的發現:

  • 循序讀取反而更快:NFS 客戶端(fio Pod)的快取和預讀機制,對大檔案的循序讀取有奇蹟般的加速效果。
  • 隨機寫入崩潰:NFS 的同步寫入機制,導致 RWX 的隨機寫入效能僅剩 RWO 的 30%。

資料庫應用推薦

一般來說,MySQL、PostgreSQL 這類 OLTP 資料庫需要:

指標 建議範圍 說明
延遲 < 10ms (理想), < 50ms (可接受) 平均延遲決定交易回應速度
IOPS 1,000 - 256,000 (根據資料庫大小) 高 IOPS 能處理更多併發交易

我的應用規格

  • 資料庫類型:MySQL / PostgreSQL
  • 資料大小:約 10GB
  • QPS:約 100

分析

10GB 是一個相當小的資料庫。現在的伺服器記憶體動輒 32GB 起跳,資料庫會把熱資料幾乎完全快取在 RAM 裡。大部分讀取操作根本不會碰到硬碟。

即使是最慢的 Longhorn v1,在 OLTP 測試中也有 ~9,600 IOPS,這個數字是 100 QPS 需求的 96 倍!

最終選擇:TopoLVM + Longhorn v1

經過測試比對,我決定採用 TopoLVM + Longhorn v1 的組合,而非 Longhorn v2。理由如下:

為什麼放棄 v2?

  • v2 (SPDK) 雖然效能更好,但每一層軟體抽象都伴隨著顯著的效能損耗
  • 在 VM → Longhorn v2 → Storage Engine 的路徑中,最終 IOPS 僅剩 Host 的 ~13%
  • 多一層 SDS 開銷,對於 100 QPS 這類輕度負載而言,性價比不高

我的最終配置:

方案 職責 理由
TopoLVM 高 IOPS 需求的 PVC 後端 Local Path 等級效能,無多餘軟體層損耗
Longhorn v1 需要 RWX 共享的儲存 原生支援 ReadWriteMany,資源佔用低
應用層 HA 資料庫主從複製 自己掌控 Failover,不被 SDS 綁定

HA 策略:選擇讓上層應用自己實現高可用(例如 PostgreSQL 的 Streaming Replication、MySQL 的 Group Replication),而非依賴 Longhorn 的副本機制。這樣的好處是:

  • 不需要在 SDS 層付出額外的效能損耗
  • Failover 邏輯更透明、可控
  • 減少一層「會不會出事」的複雜度

犧牲 Longhorn 的內建副本,換來:

  • 儲存路徑更短、延遲更低
  • 架構更簡單、問題更好排查
  • 效能逼近 Local Path 等級

第二部分:網路效能對決

測試環境

  • 實體網路:1 Gbps 交換器 (USW-Ultra-210W)
  • Host A (192.168.0.120): Lenovo ThinkCentre M910x (Intel 4C8T)
  • Host B (192.168.0.121): 相同硬體規格
  • CNI: Cilium eBPF / Flannel host-gw
  • 測試工具: iperf3 (Server/Client 模式)

測試腳本

# iperf3-server.yaml (Anti-Affinity,確保跨節點)
apiVersion: v1
kind: Pod
metadata:
  name: iperf3-server
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: iperf3-server
        topologyKey: kubernetes.io/hostname
  containers:
  - name: iperf3
    image: networkstatic/iperf3
    args:
    - -s
    - -p
    - "5201"
    ports:
    - containerPort: 5201
---
apiVersion: v1
kind: Service
metadata:
  name: iperf3-service
spec:
  selector:
    app: iperf3-server
  ports:
  - port: 5201

基準線建立:實體機交叉測試

在進入 K8s 網路效能測試前,先建立最根本的效能基準線。兩台實體機直接透過 1 Gbps 交換器連接:

測試場景 頻寬 備註
TCP 單線程上傳 775 Mbps 0 Retr
TCP 單線程下載 830 Mbps 0 Retr
TCP 多線程上傳 (-P 4) 771 Mbps 0 Retr
TCP 多線程下載 (-P 4) 829 Mbps 0 Retr
UDP 上傳 (-b 1G) 763 Mbps 0% Loss
UDP 下載 (-b 1G) 823 Mbps ~0% Loss

發現:將 -P 從 8 降到 4(對應 4C8T CPU 核心數)後,原本大量重傳的問題完全消失,驗證了傳送端 CPU 是瓶頸。

同節點基準線:localhost vs 實體 IP

測試場景 實體 IP (192.168.x.x) localhost (127.0.0.1)
TCP 單線程上傳 39.2 Gbps 39.0 Gbps
TCP 單線程下載 39.4 Gbps 38.9 Gbps
TCP 多線程上傳 56.5 Gbps 54.6 Gbps
TCP 多線程下載 52.7 Gbps 59.0 Gbps
UDP 上傳 1.0 Gbps 1.0 Gbps
UDP 下載 1.0 Gbps 1.0 Gbps

兩者差異微乎其微,現代 Linux 核心的 loopback 介面已經過高度優化。選擇用實體 IP 測試更能模擬 K8s Pod 的網路路徑。

Cilium eBPF 效能數據

跨節點 (Inter-Node) 測試

兩台實體節點上的 Pod 之間透過 CNI 網路通訊:

測試場景 頻寬 備註
TCP 單線程上傳 905 Mbps 139 Retr
TCP 單線程下載 887 Mbps 103 Retr
TCP 多線程上傳 (-P 4) 908 Mbps 418 Retr
TCP 多線程下載 (-P 4) 892 Mbps 217 Retr
UDP 上傳 (-b 1G) 690 Mbps 25% Loss
UDP 下載 (-b 1G) 535 Mbps 0.12% Loss

同節點 (Intra-Node) 測試

同一實體節點上的兩個 Pod 之間直接透過 veth pair 通訊,流量不離開主機:

測試場景 頻寬 備註
TCP 單線程上傳 26.8 Gbps 2285 Retr
TCP 單線程下載 25.2 Gbps 895 Retr
TCP 多線程上傳 26.3 Gbps 2989 Retr
TCP 多線程下載 26.6 Gbps 4957 Retr
UDP 上傳 990 Mbps 0.97% Loss
UDP 下載 966 Mbps 0.91% Loss

Flannel host-gw 效能數據

經過 CPU 瓶頸排除後(8 核心主機擔任傳送端、4 核心主機擔任接收端),Flannel 的真實效能得以解放:

跨節點 (Inter-Node) 測試

測試場景 頻寬 備註
TCP 單線程上傳 938 Mbps 0 Retr
TCP 單線程下載 782 Mbps 273 Retr
TCP 多線程上傳 (-P 4) 944 Mbps 0 Retr
TCP 多線程下載 (-P 4) 788 Mbps 1626 Retr
UDP 上傳 (-b 1G) 877 Mbps 0.61% Loss
UDP 下載 (-b 1G) 894 Mbps 0.22% Loss

網路效能終極天梯榜

測試模式 測試場景 ① 實體機
(跨節點)
② Flannel
(跨節點)
③ Cilium
(跨節點)
④ 實體機
(同節點)
⑤ Cilium
(同節點)
TCP (單) 上傳 775 Mbps 938 Mbps 905 Mbps 39.2 Gbps 26.8 Gbps
下載 830 Mbps 782 Mbps 887 Mbps 39.4 Gbps 25.2 Gbps
TCP (多) 上傳 771 Mbps 944 Mbps 908 Mbps 56.5 Gbps 26.3 Gbps
下載 829 Mbps 788 Mbps 892 Mbps 52.7 Gbps 26.6 Gbps
UDP 上傳 763 Mbps 877 Mbps 690 Mbps
(25% Loss)
1.0 Gbps 990 Mbps
下載 823 Mbps 894 Mbps 535 Mbps 1.0 Gbps 966 Mbps

深度分析

1. 跨節點 TCP:王道之爭

  • Flannel 上傳稱王:938/944 Mbps,0 Retr。host-gw 模式直接修改主機路由表,完全不需封裝/解封裝,是最接近實體網路原始效能的方案。
  • Cilium 下載領先:887/892 Mbps,且下載路徑的 Retr 數量顯著低於上傳。eBPF 在接收端的處理效率更優。

2. UDP 高負載:Cilium 的隱憂

Cilium 在跨節點 UDP 上傳時出現 25% 掉包,遠高於 Flannel 的 0.61%。這證明了 eBPF 資料路徑在面對超高頻率 UDP 封包時,存在著緩衝區或處理能力的瓶頸。對於 DNS、NTP 或串流等 UDP 密集型應用,這是需要注意的重點。

3. 同節點:Cilium 的主場優勢

當流量完全停留在單一實體節點內時,Cilium eBPF 展現碾壓態勢:26 Gbps+ 的速度已達到主機記憶體複製的瓶頸邊緣。這個數字意味著——對同節點的 Pod 之間通訊而言,Cilium 的損耗已經極小,應用層幾乎感受不到網路虛擬化的存在。


結語:如何在效能與功能間取得平衡

經過這一系列的量化分析,我的最終結論是 TopoLVM + Longhorn v1 + Flannel host-gw 組合:

方案 優點 缺點 適用場景
TopoLVM I/O 損耗最小、容量感知調度、動態擴容 無跨節點副本,需要依賴應用層 HA 高 IOPS 需求的關鍵有狀態應用
Longhorn v1 原生 RWX 支援、快照、備份、資源佔用低 效能比 v2 差、約剩 Host 的 5% 需要共享儲存的場景,多 Pod 掛載
Local Path 極致效能,約 Host 的 40% 無快照、無擴容、無跨節點遷移、僅 RWO 短期、測試環境
Longhorn V2 SPDK 引擎最高效能與 CPU 效率 多一層 SDS 損耗、RWX 資源消耗大 (2.7 核心) 對延遲極敏感且需要 SDS 內建 HA 的場合
Flannel host-gw UDP 穩定性極佳、效能損耗低 無進階網路策略、缺少可觀測性 所有 K8s 網路場景,UDP 密集應用

CNI 選擇思路

經過 Cross-Node 與 Intra-Node 的完整測試,我最終選擇 Flannel host-gw 作為唯一的 CNI 方案。host-gw 模式直接修改主機路由表,完全不需封裝/解封裝,是最接近實體網路原始效能的方案。UDP 掉包率僅 0.61%,對於 DNS、串流、VoIP 等 UDP 密集型應用都能提供足夠的穩定性。

核心思路:既然多一層 SDS 就多一層損耗,那就不如把 SDS 的功能退給應用層。讓專業的工具做專業的事——TopoLVM 專心處理高效儲存,Longhorn v1 專心處理 RWX 共享,而資料安全由應用層的複製機制來把關。CNI 也是同樣道理——根據應用的網路特性,選擇最合適的方案,而非一味追求功能最多最強。

在資源有限的家庭實驗室環境中,找到最符合自己需求的平衡點,才是真正的「效能大師」。


系列回顧:home-lab 2.0 的完整旅程

一路走來,home-lab 2.0 從硬體採購到效能優化,總共歷經了五篇文章的折騰。讓我們一起回顧這段旅程,以及我在每個階段所做的技術抉擇。

Part 1 — 硬體與虛擬化基礎建設

從零開始採購與組裝家庭實驗室伺服器,選擇了 Lenovo ThinkCentre M910q 與 HP ProDesk 600 G4 這兩台低功耗小機器,用 Proxmox VE 建立虛擬化叢集。那時候還不確定未來要走哪條路,所以先求「能動」再求「優化」。硬體方面也踩過不少坑——Kingfast SSD 突然暴斃、主機板 M.2 插槽有一個是壞的——這些經驗都在告訴我:家庭實驗室的第一步,永遠要把「容錯」放在心上。

Part 2 — 核心服務規劃

接著進入軟體與服務的規劃層次。用 Raspberry Pi 4 8GB 部署低功耗的核心服務——PBS、CasaOS、NPMplus、Tailscale 與 AdGuard Home。再用 ZimaBlade 跑 TrueNAS 提供 NFS 儲存。那時候對於 Kubernetes 的規劃已經有了初步的想法:Kafka、PostgreSQL、Plex、Immich 這些有狀態服務,必須要有一個能撐得住的儲存後端。於是開始研究 Longhorn,也隱約察覺到 SDS 這層抽象化的代價。

Part 3 — GitOps 與 CI/CD 流水線

在這個階段建立了 Fedora VM 跑 Forgejo 搭配 PostgreSQL 18,並用 Ansible + GoReleaser + Forgejo Actions 打造了完整的 CI/CD 流程,實作了 Glance 的 server-side Todo 功能。這個階段讓我深刻體會到「寫程式」和「部署程式」是兩件完全不同的事——一個良好的 GitOps 流程,可以讓整個家庭實驗室的維護成本大幅降低。硬體運算有其極限,但良好的軟體架構可以讓同樣的硬體發揮更大的價值。

Part 4 — Talos Linux:向維運說再見

從 kubeadm / k3s 跳槽到 Talos Linux 是這個系列最「離經叛道」的決定。Talos 把整個 Kubernetes 控制平面都容器化了,而且作業系統本身通過 API 管理,根本不需要 SSH 進去折騰。一開始對這個概念很不適應,總覺得「看不到,摸不到」的系統怎麼能讓人安心?但實際用下去之後,那種「作業系統升級不需要擔心設定檔被覆寫」、「升級過程有完整的roll back保障」的安心感,是傳統 Linux 完全比不上的。於是家裡的 K8s 叢集,正式進入了「免維運」時代。

Part 5 — 量化每一層抽象化的代價

來到了這篇文章的核心——用 fio 與 iperf3 實際測量每一層技術抽象所付出的效能代價。

「每一層抽象都有代價」——這句話聽起來像是廢話,但只有在真正測量過數據之後,才能具體說出「代價是多少」。例如:

  • VM 虛擬化:Local Path 的 4K 隨機 IOPS 大約是 Host 的 40%,這數字看似悲劇,但對多數應用來說仍然遠遠足夠。
  • SDS 軟體定義儲存 (Longhorn):每一層 SDS 抽象讓 IOPS 再損耗 40%~60%,相較於 Local Path,最終只剩 Host 的 ~13%。但代價換來了快照、備份、跨節點遷移,以及最重要的——RDMA 功能。
  • CNI 網路虛擬化:跨節點 TCP 流量在 Flannel host-gw 模式下幾乎沒有封裝損耗,UDP 掉包率僅 0.61%。host-gw 直接利用主機路由表轉發,是最接近實體網路效能的 CNI 方案。

這些數據讓我明白:不是「功能越多越好」,而是「選擇最符合應用需求的方案」才是真正的效能大師。

最終架構總結

經過五篇文章的折騰,最終的 home-lab 2.0 架構如下:

┌─────────────────────────────────────────────────────┐
│                   User Access Layer                 │
│         (Tailscale, Nginx, Certbot, Cloudflare)     │
└──────────────────────┬──────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────┐
│               Kubernetes Cluster (Talos)            │
│                                                     │
│  ┌─────────────────────────────────────────────────┐│
│  │           Flannel host-gw                       ││
│  │   UDP / DNS / Streaming / VoIP                  ││
│  └─────────────────────────────────────────────────┘│
│                                                     │
│  ┌─────────────────────────────────────────────────┐│
│  │           Storage Layer                         ││
│  │                                                 ││
│  │   ┌────────────────┐  ┌────────────────────────┐││
│  │   │   TopoLVM      │  │   Longhorn v1          │││
│  │   │   (RWO)        │  │   (RWX + Snapshots)    │││
│  │   │                │  │                        │││
│  │   │ 高 IOPS 需求    │  │  需要多 Pod 共享存取    │││
│  │   │ PostgreSQL /   │  │  NFS-based RWX         │││
│  │   │ MySQL / Kafka  │  │  Immich / Plex shared  │││
│  │   └────────────────┘  └────────────────────────┘││
│  │                                                 ││
│  │   Application-Layer HA (沒有 SDS 副本損耗!)      ││
│  │   PostgreSQL Streaming Replication              ││
│  │   MySQL Group Replication                       ││
│  └─────────────────────────────────────────────────┘│
└──────────────────────┬──────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────┐
│              Proxmox Virtualization                 │
│                                                     │
│    ┌──────────────┐      ┌──────────────┐           │
│    │  Host A      │      │  Host B      │           │
│    │  M910q 4C8T  │      │  M910q 4C8T  │           │
│    │  KIOXIA G2   │      │  KIOXIA G2   │           │
│    └──────────────┘      └──────────────┘           │
│                                                     │
│    ┌──────────────┐      ┌──────────────┐           │
│    │  Host C      │      │  ZimaBlade   │           │
│    │  ProDesk     │      │  TrueNAS     │           │
│    │  4C8T        │      │  NFS Storage │           │
│    └──────────────┘      └──────────────┘           │
└─────────────────────────────────────────────────────┘

折騰是沒有盡頭的,但每一次折騰,都會讓這個家庭實驗室更接近「理想中的樣子」。我們下次見!👋