home-lab 2.0 part 5: 儲存與網路效能實測 — 量化每一層抽象化的代價
大家好,好久沒有發新文章。距離上一次翻新家庭實驗室的文章 (part 4) 也過了一段時間,這陣子除了陸續把一些新服務遷移到叢集上,也一直在思考一個核心問題:我在架構設計時所做的技術選型,到底讓效能付出了多少代價?
既然要折騰,就乾脆折騰到底。這篇文章就來記錄一下,我如何利用 fio 與 iperf3 這兩個工具,量化 Proxmox 虛擬化環境中、Kubernetes 儲存與網路層的效能損耗。
TL;DR
- 虛擬化開銷顯著:在 4K 隨機讀取高壓力測試下,VM 內的儲存效能約剩 Host 實體的 36%。
- Longhorn v2 vs v1:SPDK 引擎帶來革命性提升,v2 的 IOPS 是 v1 的 2.5~3 倍,延遲降低約 60%,但代價是更多的抽象層損耗。
- 多一層軟體定義儲存的代價:Longhorn v2 在 VM 之上又多了一層 SDS 開銷,最終 IOPS 僅剩 Host 的 ~13%。
- 最終配置:TopoLVM 負責高效能負載(RWO),Longhorn v1 負責需要 RWX 共享的場景;HA 由上層應用自己實現。
- 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)
我們的目標是比較三個層級的效能:
- Proxmox Host (基準線):硬體的物理極限。
- K8s VM (Local Path Provisioner):最輕量化的 K8s 儲存方案。
- K8s VM (Longhorn V2):分散式儲存,帶有 SPDK V2 引擎。
自動化測試腳本: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 實現方式是:
- 先建立一個 RWO Longhorn 卷
- 啟動
share-managerPod,裡面跑著 NFSv4 伺服器 - 將 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 客戶端(
fioPod)的快取和預讀機制,對大檔案的循序讀取有奇蹟般的加速效果。 - 隨機寫入崩潰: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 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
折騰是沒有盡頭的,但每一次折騰,都會讓這個家庭實驗室更接近「理想中的樣子」。我們下次見!👋