SUSE Harvester ストレージ検証(前編) 3ノード構築と3方式比較

はじめに

NTT西日本の平岡です。 本記事では、SUSE Harvester のストレージ機能を検証します。

Harvester は、Kubernetes を基盤として仮想マシンやストレージを統合的に管理できる HCI(Hyper-Converged Infrastructure)ソフトウェアです。内蔵の分散ブロックストレージ Longhorn に加え、外部ストレージとの連携も可能であり、ストレージ構成の選択肢が多岐にわたります。本シリーズでは、合計6つのストレージ構成を自宅ラボで実際に構築し、fio ベンチマークで横断比較します。

本検証は、筆者の自宅ラボに構築した Proxmox VE 上のネステッド仮想化環境で実施しています。そのため、ベンチマークの絶対値(MB/s や IOPS)はベアメタル環境と異なる可能性があります。本シリーズでは絶対値よりも、方式間の相対的な傾向(順位や倍率)を読み取ることを目的としています。

なお、本シリーズの検証では「ローカルディスクが最も高速」「ブロック接続がファイル共有に勝る」といった直感的な想定が、Longhorn のレプリカ同期やネステッド仮想化の影響で覆される場面がありました。アーキテクチャの想定と実測の乖離を明らかにすることも、本検証の重要なテーマです。

本シリーズは以下の前編・後編構成です。

内容
前編(本記事) Harvester の概要、3ノードクラスタの構築、3方式(Longhorn内蔵 / 外部iSCSI / 外部NFS)の構成とベンチマーク比較
後編 democratic-csi による Longhorn バイパス方式、カーネルNFS vs ユーザー空間NFS(NFS-Ganesha)の構成とベンチマーク比較、シリーズ全体のまとめ

前編で構築する3方式はいずれも Harvester の VM 起動に対応したストレージです。後編ではさらに democratic-csi による Longhorn バイパス方式や、カーネル NFS vs ユーザー空間 NFS(NFS-Ganesha)の比較にも踏み込みます。なお、後編ではイメージインポートの制約により VM ではなく Pod から fio を実行するため、前編と後編のベンチマーク絶対値は直接比較できません。方式間の相対的な傾向を読み取ってください。

対象読者

本記事は以下のような方を対象としています。

  • 仮想化基盤の導入・移行を検討しており、Kubernetes ベースの HCI に興味がある方
  • Harvester のストレージ構成の選択肢と特性を知りたい方
  • Longhorn、iSCSI、NFS の性能差を実測で比較したい方

この記事で分かること

  • Harvester で使える 3 方式(harvester-longhorn / longhorn-iscsi / nfs-csi)の構造差
  • 3 方式を同一環境で比較した fio ベンチマーク結果と、性能差が生じた主な要因
  • 後編で扱う Longhorn バイパス構成につながる論点

急ぎで結果を確認したい方は 8.3. 測定結果、考察を先に読みたい方は 8.4. 考察 をご覧ください。

1. 前提知識

1.1. Kubernetes の基本用語

Harvester の内部構造を理解するうえで必要となる Kubernetes 用語を、本記事で登場する範囲に絞って説明します。

Pod — Kubernetes におけるアプリケーションの最小実行単位です。1つ以上のコンテナをまとめたもので、Harvester では仮想マシンも Pod の中で動作します。Pod はクラスタ内のいずれかのノードに配置され、Kubernetes のスケジューラが配置先を決定します。

PVC(Persistent Volume Claim) — Pod が永続的なストレージを要求するための仕組みです。「10GB のディスクが欲しい」といった要求を宣言的に記述すると、Kubernetes が条件に合うストレージを自動的に割り当てます。Harvester では VM のディスクが PVC として管理されます。

StorageClass — PVC に対して「どのストレージバックエンドを使うか」を定義するリソースです。Harvester では、この StorageClass を切り替えることで、Longhorn 内蔵ディスク・外部 iSCSI・外部 NFS など異なるストレージ方式を選択します。

Longhorn — Kubernetes 向けの分散ブロックストレージです。Harvester に標準搭載されており、データを複数ノードにレプリカとして分散保存します。デフォルトではレプリカ数3で、3ノード全てにデータのコピーを持ちます。

CSI(Container Storage Interface) — Kubernetes が外部ストレージと連携するための標準インターフェースです。この CSI を通じて、NFS・iSCSI 等の多様なストレージを Kubernetes から利用できます。

Deployment — 指定した数の Pod レプリカを維持するリソースです。Pod が異常終了した場合、自動的に新しい Pod を起動して指定数を保ちます。Harvester API サーバや Rancher など、クラスタ全体で1つまたは少数稼働するコンポーネントが Deployment として管理されています。

DaemonSet — クラスタの全ノード(または条件を満たすノード)に1つずつ Pod を配置するリソースです。Longhorn のストレージエンジンや、CSI ドライバのノード側コンポーネントなどが DaemonSet として動作します。

namespace — Kubernetes クラスタ内でリソースをグループ分けする仕組みです。Harvester では longhorn-system(ストレージ関連)、harvester-system(Harvester 本体)など、機能ごとに namespace が分かれています。

1.2. CSI ドライバとは ― Kubernetes ストレージの標準インターフェース

本シリーズでは複数の CSI ドライバ(csi-driver-nfs、democratic-csi 等)を使用してストレージを構成します。ここでは CSI ドライバの位置づけを補足します。

CSI(Container Storage Interface)は、CNCF(Cloud Native Computing Foundation)が策定したオープンな業界標準仕様です。Kubernetes とストレージシステムの間に標準化されたインターフェースを定義し、ストレージベンダーに依存しない形でブロック/ファイルストレージを利用可能にすることを目的としています。CSI は 2018 年に Kubernetes 1.13 で正式版(GA)となりました。

CSI ドライバの役割は、Kubernetes の PVC 要求を受け取り、実際のストレージ上でボリュームの作成・削除・マウント・スナップショット等の操作を実行することです。StorageClass に「どの CSI ドライバを使うか」を指定することで、同一クラスタ上で複数のストレージバックエンドを使い分けることができます。

この関係は、Linux カーネルとディストリビューションの関係と類似しています。Linux カーネル自体は Linus Torvalds 氏と Linux Foundation のコミュニティが開発しており、各社(Red Hat、SUSE、Canonical 等)はカーネルをベースにしたディストリビューション(RHEL、SLES、Ubuntu)を提供しています。同様に、Kubernetes 自体は CNCF コミュニティが開発しており、SUSE の RKE2(Harvester の内部 Kubernetes)はその「ディストリビューション」のひとつです。CSI はこの Kubernetes の標準仕様の一部であり、特定ベンダーの独自仕様ではありません。

本シリーズで使用する CSI ドライバはいずれもオープンソースであり、Harvester(RKE2)上で動作します。前編では csi-driver-nfs(Kubernetes SIG Storage が開発)を、後編では democratic-csi(コミュニティ開発)を使用します。エンタープライズ環境では、HPE(3PAR/Primera/Alletra)、Dell、NetApp 等のストレージベンダーが自社ストレージ向けの CSI ドライバを提供しており、同じ CSI 標準仕様に準拠しているため Harvester 上で利用可能です。

2. SUSE Harvester の概要

2.1. Harvester とは

SUSE Harvester は、Kubernetes をベースとしたオープンソースの HCI(Hyper-Converged Infrastructure)ソフトウェアです。従来の仮想化基盤(VMware vSphere、Proxmox VE 等)と同様に仮想マシンの作成・管理が可能ですが、内部的にはすべてが Kubernetes のリソースとして動作する点が大きく異なります。

従来のハイパーバイザーが「1つのソフトウェア」として仮想化機能を提供するのに対し、Harvester は複数のオープンソースコンポーネントを Kubernetes 上で統合したプラットフォームです。以下の表に主要コンポーネントとその役割を示します。

レイヤー コンポーネント 役割
OS SLE Micro(Elemental) イミュータブル設計の軽量 Linux OS。再起動で OS ファイルが事前構成済み状態に戻る
コンテナ基盤 RKE2(Kubernetes) 全コンポーネントの実行基盤。CNCF 適合性認定取得済み
仮想マシン管理 KubeVirt + QEMU/KVM VM の作成・実行・ライブマイグレーション
ストレージ Longhorn(標準搭載) 分散ブロックストレージ。外部 CSI ドライバとの併用も可能
クラスタ管理 Rancher(組み込み) Web UI の提供、マルチクラスタ管理
監視 Grafana + Prometheus メトリクス収集・可視化・アラート

この構造が Harvester の柔軟性と拡張性の源泉であり、同時に本シリーズで検証する「多様なストレージ構成の選択肢」を生み出しています。

Harvester は以下のコンポーネントで構成されています。

RKE2 — Rancher が開発する Kubernetes ディストリビューションです。Harvester のベースとなる Kubernetes クラスタを提供します。

KubeVirt — Kubernetes 上で仮想マシンを動作させるためのアドオンです。VM は virt-launcher Pod の中で QEMU/KVM プロセスとして実行されます。

Longhorn — Harvester に標準搭載される分散ブロックストレージです。各ノードのローカルディスクを束ねて、レプリケーション付きのブロックストレージを提供します。

Rancher — Kubernetes クラスタの管理ツールです。Harvester の Web UI を提供し、VM やストレージの操作画面として機能します。

2.2. VM の起動シーケンス

Harvester で VM が起動する流れを整理します。ユーザーが Web UI または API で VM の作成を指示すると、KubeVirt が VirtualMachine リソースを作成します。Kubernetes のスケジューラが配置先ノードを決定し、そのノードで稼働する virt-handler(DaemonSet)が virt-launcher Pod を起動します。virt-launcher Pod 内で QEMU/KVM プロセスが立ち上がり、Longhorn または外部ストレージから提供される PVC をディスクとしてアタッチして VM がブートします。

3. 自宅ラボの検証環境

3.1. ハードウェア・ソフトウェア

項目
Proxmox VE 8.3.0(カーネル: 6.8.12-4-pve)
CPU AMD Ryzen 5 5600G(6コア/12スレッド)
メモリ 128 GB
ストレージ local-zfs 約 5.6 TB
ネットワーク vmbr0(192.0.2.0/24)、VLAN なし
Harvester v1.7.1
外部ストレージ TrueNAS SCALE ElectricEel-24.10.2

3.2. 構成図

3.3. 検証環境詳細

Harvester ノード(VM × 3台)

ノード Virtual Machine ID(VMID) IP vCPU メモリ ディスク
harvester01 131 192.0.2.71 8 16 GB 400 GB
harvester02 132 192.0.2.72 8 16 GB 400 GB
harvester03 133 192.0.2.73 8 16 GB 400 GB

Management Virtual IP(VIP): 192.0.2.70

外部ストレージ(TrueNAS SCALE VM)

項目
VMID 102
IP 192.0.2.4
iSCSI LUN lun1〜lun3(各 100 GiB、Zvol)
NFS 共有 /mnt/zpool/pool/NFS(約 225 GB、NFSv4)

4. クラスタ構築

4.1. 事前準備

ネステッド仮想化の確認

Proxmox ホストのシェルで、ネステッド仮想化が有効であることを確認します。

# AMD CPUの場合
cat /sys/module/kvm_amd/parameters/nested
1

1 または Y が表示されれば有効です。

ISO のアップロード

Harvester の公式サイトから harvester-v1.7.1-amd64.iso(約 7.3GB)をダウンロードし、Proxmox にアップロードします。

ls -lh /var/lib/vz/template/iso/ | grep harvester
-rw-r--r-- 1 root root 7.3G Mar  8 16:50 harvester-v1.7.1-amd64.iso

IP アドレスの空き確認

for ip in 192.0.2.70 192.0.2.71 192.0.2.72 192.0.2.73; do
  ping -c 1 -W 1 $ip > /dev/null 2>&1 && echo "$ip: IN USE" || echo "$ip: available"
done
192.0.2.70: available
192.0.2.71: available
192.0.2.72: available
192.0.2.73: available

4.2. VM の作成

3台の VM を Proxmox シェルから作成します。

# harvester01
qm create 131 \
  --name harvester01 \
  --memory 16384 \
  --cores 8 \
  --cpu cputype=host \
  --bios ovmf \
  --machine q35 \
  --efidisk0 local-zfs:1,efitype=4m,pre-enrolled-keys=0 \
  --net0 virtio,bridge=vmbr0,firewall=0 \
  --scsihw virtio-scsi-single \
  --scsi0 local-zfs:400,iothread=1 \
  --ide2 local:iso/harvester-v1.7.1-amd64.iso,media=cdrom \
  --boot order=ide2 \
  --ostype l26 \
  --numa 1

harvester02(VMID 132)、harvester03(VMID 133)も同様に作成します。IP アドレスとホスト名のみ異なります。

CPU type は host を指定します。ネステッド仮想化にはホスト CPU のパススルーが必要です。3台合計で 24vCPU となり物理 12 スレッドに対してオーバーコミットになりますが、全 VM が同時に CPU を 100% 使用することはないため、検証用途では問題ありません。

qm list | grep harvester
       131 harvester01          stopped    16384              0.00 0
       132 harvester02          stopped    16384              0.00 0
       133 harvester03          stopped    16384              0.00 0

4.3. Harvester のインストール(1号機)

qm start 131

Proxmox Web UI から harvester01 のコンソールを開きます。ISO からブートし、Harvester Installer の TUI が起動します。

最初に Hardware Checks の警告が表示されます。CPU・メモリともに Harvester の推奨要件を下回っていますが、検証目的のため「Yes」で続行します。

+------------------------------------------------------------------+
| Hardware Checks                                                    |
|                                                                    |
| Only 8 CPU cores detected. Harvester requires at least 16 for     |
| production use.                                                    |
| Only 16GiB RAM detected. Harvester requires at least 32GiB for    |
| testing and 64GiB for production use.                              |
| System is virtualized (kvm) which is not supported in production.  |
|                                                                    |
| > [Yes]                                                            |
+------------------------------------------------------------------+

TUI での主要設定値は以下の通りです。

項目
Installation mode Create a new Harvester cluster
Installation role Default Role
Installation disk sda 400G
Management NIC enp6s18
IPv4 Method Static
IPv4 Address 192.0.2.71
Gateway 192.0.2.1
DNS 1.1.1.1
VIP 192.0.2.70
Cluster token <DUMMY_TOKEN>
NTP 0.suse.pool.ntp.org

ISO の取り外し(重要)

インストール完了後のカウントダウンが表示されたら、CTRL+C で再起動を一時停止し、Proxmox シェルで ISO を取り外します。

注意: CTRL+C によるカウントダウンの一時停止は、Harvester v1.7.1 のインストーラで動作を確認しています。他のバージョンでは動作が異なる可能性があります。カウントダウンを停止できない場合は、Proxmox Web UI から VM を一旦停止し、ISO を取り外してから再起動してください。

qm set 131 --ide2 none,media=cdrom --boot order=scsi0

Proxmox VM では ISO が自動的に取り外されません。ISO が接続されたまま再起動すると再び ISO からブートしてインストーラが起動し、2回目のインストーラが中途半端に実行されるとブートローダーが破損して起動不能になります。これが本構築における最大のハマりポイントでした。

Harvester のコンソールに戻り、手動で再起動します。

reboot

初回起動時は Kubernetes クラスタの初期化(RKE2、Longhorn、Rancher 等)が行われるため、Status が「Setting up」の状態が10〜20分程度続きます。最終的に Cluster / Node ともに「Ready」になれば1号機のインストールは完了です。

4.4. 2号機・3号機のインストール

2号機・3号機は既存クラスタへの join モードでインストールします。

項目 harvester02 harvester03
Installation mode Join an existing Harvester cluster Join an existing Harvester cluster
IPv4 Address 192.0.2.72 192.0.2.73
HostName harvester02 harvester03
Management address 192.0.2.70 192.0.2.70
Cluster token <DUMMY_TOKEN> <DUMMY_TOKEN>

ISO の取り外しも同様に行います。

qm set 132 --ide2 none,media=cdrom --boot order=scsi0
qm set 133 --ide2 none,media=cdrom --boot order=scsi0

4.5. クラスタ構築完了確認

ブラウザから https://192.0.2.70 にアクセスし、Web UI にログインします。

Harvester Cluster: local
Version: v1.7.1

  3 Hosts    0 Virtual Machines    0 Virtual Machine Networks
  0 Images   0 Volumes             3 Disks

Capacity
  CPU       Reserved  9.17 / 21.08    43.52%
  Memory    Reserved 12    / 47 Gi    26.29%
  Storage   Allocated 0 Ti / 1.3 Ti    0.00%
ノード State Host IP Disk State
harvester01 Active 192.0.2.71 Healthy
harvester02 Active 192.0.2.72 Healthy
harvester03 Active 192.0.2.73 Healthy

3ノードすべてが Active / Healthy で稼働しています。

5. ストレージ方式の構成

5.1. 方式1: harvester-longhorn(デフォルト)

harvester-longhorn は Harvester インストール時に自動作成される StorageClass です。各ノードのインストールディスク(/dev/sda 400GB)上に Longhorn のストレージ領域が確保され、レプリカ数3でデータが3ノードに分散保存されます。追加の設定は不要です。

5.2. 方式2: longhorn-iscsi(外部 iSCSI LUN)

構成の概要

TrueNAS SCALE の iSCSI Target から提供される LUN を、Longhorn のディスクとして追加登録する方式です。

外部 iSCSI LUN を Harvester VM に認識させる方法は2通りあります。Proxmox ホスト側で iSCSI 接続し VM に仮想ディスクとしてパススルーする方式と、VM 内部から iscsiadm で直接接続する方式です。本検証では Proxmox 側パススルーを採用しました。Harvester のベース OS(SLE Micro)は immutable OS であり、iscsiadm パッケージの永続化に追加の考慮が必要になるためです。この方式により、VM 内部からは通常の SCSI ディスク(/dev/sdb)として見え、Harvester 側の追加設定が不要になります。

TrueNAS iSCSI Target の設定

TrueNAS SCALE で Zvol を3つ作成し(各 100 GiB)、iSCSI Target に紐づけます。

+------------------------------------------------------------+
| Extent Name    | Device/File          | LUN ID |
|----------------|----------------------|--------|
| harvester-lun1 | zvol/zpool/pool/lun1 | 0      |
| harvester-lun2 | zvol/zpool/pool/lun2 | 1      |
| harvester-lun3 | zvol/zpool/pool/lun3 | 2      |
+------------------------------------------------------------+

Proxmox からの疎通確認:

root@pve:~# iscsiadm -m discovery -t sendtargets -p 192.0.2.4:3260
192.0.2.4:3260,1 iqn.2005-10.org.freenas.ctl:harvester

iSCSI LUN の接続と VM へのアタッチ

# iSCSI ログイン
root@pve:~# iscsiadm -m node -T iqn.2005-10.org.freenas.ctl:harvester \
  -p 192.0.2.4:3260 --login
Login to [...] successful.

# LUN 認識確認
root@pve:~# lsblk -d -o NAME,SIZE,MODEL,TRAN | grep -i iscsi
sdc      100G iSCSI Disk               iscsi
sdd      100G iSCSI Disk               iscsi
sde      100G iSCSI Disk               iscsi

# 各 VM にアタッチ
root@pve:~# qm set 131 --scsi1 /dev/sdc
root@pve:~# qm set 132 --scsi1 /dev/sdd
root@pve:~# qm set 133 --scsi1 /dev/sde

Harvester VM 内でディスクが認識されたことを確認します。

harvester01:~ # lsblk -d -o NAME,SIZE,MODEL
NAME   SIZE MODEL
sda    400G QEMU HARDDISK
sdb    100G QEMU HARDDISK

ディスクのフォーマットとマウント

各ノードで /dev/sdb を ext4 でフォーマットし、マウントします。

# harvester01
harvester01:~ # sudo mkfs.ext4 -F /dev/sdb
harvester01:~ # sudo mkdir -p /var/lib/harvester/extra-disks/iscsi-lun1
harvester01:~ # sudo mount /dev/sdb /var/lib/harvester/extra-disks/iscsi-lun1

# harvester02
harvester02:~ # sudo mkfs.ext4 -F /dev/sdb
harvester02:~ # sudo mkdir -p /var/lib/harvester/extra-disks/iscsi-lun2
harvester02:~ # sudo mount /dev/sdb /var/lib/harvester/extra-disks/iscsi-lun2

# harvester03
harvester03:~ # sudo mkfs.ext4 -F /dev/sdb
harvester03:~ # sudo mkdir -p /var/lib/harvester/extra-disks/iscsi-lun3
harvester03:~ # sudo mount /dev/sdb /var/lib/harvester/extra-disks/iscsi-lun3
harvester01:~ # df -h /var/lib/harvester/extra-disks/iscsi-lun1
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb         98G  2.1M   93G   1% /var/lib/harvester/extra-disks/iscsi-lun1

Longhorn ディスク登録

Proxmox からパススルーした仮想ディスクは Harvester の NDM(Node Disk Manager)に認識されず、Web UI の Add Disk に表示されません。そのため Longhorn のノード設定を kubectl patch で直接編集し、マウント済みのディレクトリを手動登録します。

patch コマンドに既存の default-disk エントリを含めないと、Longhorn がそのディスクを削除してしまうため、必ず事前に既存のディスク名を確認します。

harvester01:~ # kubectl -n longhorn-system get nodes.longhorn.io harvester01 \
  -o yaml | grep -A 5 "  disks:"
  disks:
    default-disk-f4fae7db6516d736:
      allowScheduling: true
      diskType: filesystem
kubectl -n longhorn-system patch nodes.longhorn.io harvester01 --type merge -p '
{
  "spec": {
    "disks": {
      "default-disk-f4fae7db6516d736": {
        "allowScheduling": true,
        "diskDriver": "",
        "diskType": "filesystem",
        "evictionRequested": false,
        "path": "/var/lib/harvester/defaultdisk",
        "storageReserved": 0,
        "tags": []
      },
      "iscsi-lun1": {
        "allowScheduling": true,
        "diskDriver": "",
        "diskType": "filesystem",
        "evictionRequested": false,
        "path": "/var/lib/harvester/extra-disks/iscsi-lun1",
        "storageReserved": 0,
        "tags": ["iscsi"]
      }
    }
  }
}'

harvester02、harvester03 についても同様に実行します(default-disk 名と LUN 番号をノードに合わせて変更)。

登録状態を確認します。

harvester01:~ # kubectl -n longhorn-system get nodes.longhorn.io harvester01 \
  -o yaml | grep -A 10 "iscsi-lun1:"
    iscsi-lun1:
      conditions:
      - message: Disk iscsi-lun1(/var/lib/harvester/extra-disks/iscsi-lun1) on node
          harvester01 is ready
        status: "True"
        type: Ready
      - message: Disk iscsi-lun1(/var/lib/harvester/extra-disks/iscsi-lun1) on node
          harvester01 is schedulable
        status: "True"
        type: Schedulable
ノード ディスク名 Ready Schedulable
harvester01 iscsi-lun1 True True
harvester02 iscsi-lun2 True True
harvester03 iscsi-lun3 True True

StorageClass の作成

diskSelector: "iscsi" により、タグ iscsi が付与されたディスクにのみレプリカを配置します。

kubectl apply -f - << 'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: longhorn-iscsi
provisioner: driver.longhorn.io
parameters:
  numberOfReplicas: "3"
  staleReplicaTimeout: "30"
  diskSelector: "iscsi"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
EOF
storageclass.storage.k8s.io/longhorn-iscsi created

レプリカ動作テスト

テスト PVC と Pod を作成し、3ノードへのレプリカ分散を確認します。

# テスト PVC 作成
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-iscsi-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: longhorn-iscsi
  resources:
    requests:
      storage: 10Gi
EOF

# テスト Pod 作成
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: test-iscsi-pod
  namespace: default
spec:
  containers:
    - name: test
      image: busybox
      command: ["sleep", "3600"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: test-iscsi-pvc
EOF
# レプリカ分散確認
harvester01:~ # VOL=$(kubectl get pvc test-iscsi-pvc -n default -o jsonpath='{.spec.volumeName}')
harvester01:~ # kubectl -n longhorn-system get replicas.longhorn.io -l longhornvolume=$VOL \
  -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeID,STATE:.status.currentState
NAME                                                  NODE          STATE
pvc-9086eb3a-...-r-70d7d1e2   harvester01   running
pvc-9086eb3a-...-r-731de1e0   harvester02   running
pvc-9086eb3a-...-r-e3c18b53   harvester03   running

3つのレプリカが3ノードに分散配置されています。

# クリーンアップ
kubectl delete pod test-iscsi-pod -n default
kubectl delete pvc test-iscsi-pvc -n default

5.3. 方式3: nfs-csi(外部 NFS 共有)

NFS CSI ドライバのインストール

Harvester(RKE2)に同梱された Helm を使い、csi-driver-nfs をインストールします。

harvester01:~ # helm version
version.BuildInfo{Version:"v3.19.1", ...}

harvester01:~ # helm repo add csi-driver-nfs \
  https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
"csi-driver-nfs" has been added to your repositories

harvester01:~ # helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs \
  --namespace kube-system \
  --set controller.replicas=1
harvester01:~ # kubectl --namespace=kube-system get pods \
  --selector="app.kubernetes.io/instance=csi-driver-nfs"
NAME                                  READY   STATUS    AGE
csi-nfs-controller-75dffdcf96-zk7hj   5/5     Running   23s
csi-nfs-node-2x8jv                    3/3     Running   18s
csi-nfs-node-ljpvp                    3/3     Running   18s
csi-nfs-node-rtvcq                    3/3     Running   18s

csi-driver-nfs は Controller Pod と Node Pod の2種類で構成されますが、NFS の通信を中継するわけではありません。Controller Pod は PVC の作成・削除時に NFS サーバ上のサブディレクトリを管理し、Node Pod は各ノード上で mount コマンドを実行する仲介役です。マウント完了後のデータ読み書きは、ノードの OS カーネルが NFS クライアントとして TrueNAS に直接通信します。

StorageClass の作成

kubectl apply -f - << 'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.0.2.4
  share: /mnt/zpool/pool/NFS
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
  - nfsvers=4.1
  - hard
  - nconnect=8
EOF
storageclass.storage.k8s.io/nfs-csi created
マウントオプション 説明
nfsvers=4.1 NFSv4.1 を使用
hard NFS サーバ無応答時にリトライを継続(データ破損防止)
nconnect=8 NFS 接続を8本並列化しスループットを向上

RWX 動作テスト

harvester01 の Writer Pod と harvester02 の Reader Pod で、ノード間共有(ReadWriteMany)を確認します。

# PVC 作成
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-nfs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-csi
  resources:
    requests:
      storage: 10Gi
EOF

# Writer Pod(harvester01)
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: test-nfs-writer
  namespace: default
spec:
  nodeName: harvester01
  containers:
    - name: writer
      image: busybox
      command: ["sh", "-c", "echo 'Hello from harvester01' > /data/test.txt && sleep 3600"]
      volumeMounts:
        - name: nfs-vol
          mountPath: /data
  volumes:
    - name: nfs-vol
      persistentVolumeClaim:
        claimName: test-nfs-pvc
EOF

# Reader Pod(harvester02)
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: test-nfs-reader
  namespace: default
spec:
  nodeName: harvester02
  containers:
    - name: reader
      image: busybox
      command: ["sh", "-c", "cat /data/test.txt && sleep 3600"]
      volumeMounts:
        - name: nfs-vol
          mountPath: /data
  volumes:
    - name: nfs-vol
      persistentVolumeClaim:
        claimName: test-nfs-pvc
EOF
harvester01:~ # kubectl logs test-nfs-reader -n default
Hello from harvester01

harvester01 で書き込んだデータを harvester02 から正常に読み取れました。

TrueNAS 上でも、CSI ドライバが作成したサブディレクトリを確認できます。

root@truenas:~ # ls -la /mnt/zpool/pool/NFS/
drwxrwxrwx  3 root root    3 Mar 13 13:32 .
drwxr-xr-x  6 root root    8 Mar  8 14:34 ..
drwxrwxrwx  2 root root    3 Mar 13 13:34 pvc-e97d67fe-ab61-40a6-ae87-71f84f984682
# クリーンアップ
kubectl delete pod test-nfs-writer test-nfs-reader -n default
kubectl delete pvc test-nfs-pvc -n default

6. 3方式の比較

6.1. StorageClass 一覧

項目 harvester-longhorn longhorn-iscsi nfs-csi
バックエンド ノード内蔵ディスク TrueNAS iSCSI LUN TrueNAS NFS 共有
プロトコル ローカルディスク iSCSI(ブロック) NFS v4.1(ファイル)
Longhorn 経由 あり あり なし
アクセスモード RWX RWX RWX
レプリカ数 3 3 1(ストレージ側冗長化)
書き込み同期 3ノード同期書き込み 3ノード同期書き込み 単一書き込み
イメージ管理 Backing Image(Copy-on-Write: CoW) Backing Image(Copy-on-Write: CoW) PVC(フルコピー)
10GB VM 1台の消費容量 約30GB 約30GB 約10GB+イメージ分

6.2. I/O パスの違い

方式1(harvester-longhorn): VM のディスク I/O は、virt-launcher Pod → Longhorn Engine → 各ノードの内蔵ディスク上の Longhorn Replica という経路をたどります。書き込み時は3つの Replica すべてに同期的に書き込みが完了するまで応答を待つため、レプリカ数がそのまま書き込みレイテンシに影響します。

方式2(longhorn-iscsi): I/O パスは方式1と同様に Longhorn Engine を経由しますが、Replica の書き込み先が内蔵ディスクではなく iSCSI LUN です。iSCSI LUN は Proxmox ホストを経由して TrueNAS の ZFS 上にあり、方式1と比較してネットワーク層が1段追加されます。レプリカ同期のオーバーヘッドに加え、iSCSI のネットワークレイテンシが加算される構造です。

方式3(nfs-csi): VM のディスク I/O は Longhorn を経由せず、NFS CSI Driver が直接 TrueNAS の NFS 共有にアクセスします。Replica の概念がなく、書き込みは単一の NFS サーバに対して1回で完了します。冗長性は TrueNAS 側の ZFS(RAIDZ など)に依存します。

6.3. Backing Image と フルコピー

harvester-longhorn と longhorn-iscsi は、Longhorn の Backing Image 機能により Copy-on-Write 方式で差分のみを記録します。同じ OS イメージから複数の VM を作成しても、追加の容量消費は変更分のみです。一方、nfs-csi は Backing Image に対応しておらず、VM ごとにイメージ全体がコピーされます。

Longhorn ベースの場合、Image 登録時に longhorn-image-xxxxx という専用の StorageClass が自動生成されます。

harvester01:~ # kubectl get virtualmachineimages.harvesterhci.io -n default \
  -o custom-columns=NAME:.metadata.name,DISPLAY:.spec.displayName,STORAGECLASS:.status.storageClassName
NAME          DISPLAY                    STORAGECLASS
image-dsnm9   openSUSE-Leap-15.6-iscsi   longhorn-image-dsnm9
image-hjpz6   openSUSE-Leap-15.6         longhorn-image-hjpz6
image-xl5hb   openSUSE-Leap-15.6-nfs     nfs-csi

nfs-csi のイメージには自動生成 StorageClass はなく、OS イメージは通常の PVC として格納されます。

6.4. Harvester 公式対応 CSI ドライバ

本シリーズでは Longhorn と csi-driver-nfs を使用していますが、Harvester v1.7 では以下の4種類の CSI ドライバが検証済みとして公式にサポートされています。ストレージ構成を検討する際の参考として、機能比較を示します。

機能 Longhorn V2 LVM NFS Rook Ceph
VM イメージ保存 対応 対応 対応 対応
ライブマイグレーション 対応 非対応 対応 対応
スナップショット 対応 対応(dm-thin) 対応 対応
バックアップ(S3/NFS) 対応 非対応 非対応 非対応
導入難度 低(標準搭載) 中(実験的)

Longhorn V2 は Harvester のデフォルトストレージであり、バックアップを含むすべての機能に対応しています。LVM は実験的(Experimental)なアドオンとして提供されており、ローカルストレージのため RWX 非対応でライブマイグレーションができません。NFS は本シリーズで使用している csi-driver-nfs に相当し、外部 NFS サーバが必要です。Rook Ceph は Ceph クラスタを Harvester 上に構築する方式で、高い拡張性を持ちますが構築・運用の複雑さが高くなります。

Harvester 標準のバックアップ機能(S3/NFS への VM バックアップ)は Longhorn ボリュームのみに対応しており、他の CSI ドライバを使用する場合はバックアップ手段を別途検討する必要があります。この制約については後編で詳しく触れます。

上記4種に加え、HPE(3PAR/Primera/Alletra)、Dell、NetApp 等のエンタープライズストレージベンダーの CSI ドライバも KubeVirt 互換として動作が確認されています。

7. VM 起動とディスク格納先

7.1. VM の作成

3種類の StorageClass それぞれの OS イメージ(openSUSE Leap 15.6 Cloud Image、約 270 MB)から VM を1台ずつ作成しました。

VM 名 StorageClass CPU メモリ ディスク 状態
test-longhorn-vm harvester-longhorn 1 2 GiB 10 GiB Running
test-iscsi-vm longhorn-iscsi 1 2 GiB 10 GiB Running
test-nfs-vm nfs-csi 1 2 GiB 10 GiB Running

3方式すべてで VM が正常に起動しました。

7.2. ディスク格納先の確認

harvester-longhorn: 3つのレプリカが各ノードの /var/lib/harvester/defaultdisk に分散配置されています。

harvester01:~ # kubectl -n longhorn-system get replicas.longhorn.io \
  -l longhornvolume=pvc-b03e2931-8839-4c1d-a73f-5b317e64d8d2 \
  -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeID,DISK:.spec.diskPath
NAME                              NODE          DISK
pvc-b03e2931-...-r-605c4409   harvester01   /var/lib/harvester/defaultdisk
pvc-b03e2931-...-r-62d524a3   harvester02   /var/lib/harvester/defaultdisk
pvc-b03e2931-...-r-6e93d12d   harvester03   /var/lib/harvester/defaultdisk

longhorn-iscsi: レプリカが各ノードの iSCSI LUN(/var/lib/harvester/extra-disks/iscsi-lunX)に分散配置されています。diskSelector: "iscsi" による振り分けが正しく機能しています。

harvester01:~ # kubectl -n longhorn-system get replicas.longhorn.io \
  -l longhornvolume=pvc-a873f384-bb51-41dd-95e4-617f086a4451 \
  -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeID,DISK:.spec.diskPath
NAME                              NODE          DISK
pvc-a873f384-...-r-084daeb5   harvester03   /var/lib/harvester/extra-disks/iscsi-lun3
pvc-a873f384-...-r-408296f0   harvester01   /var/lib/harvester/extra-disks/iscsi-lun1
pvc-a873f384-...-r-98a669ad   harvester02   /var/lib/harvester/extra-disks/iscsi-lun2

nfs-csi: Longhorn を経由しないため、レプリカの概念がありません。TrueNAS の NFS 共有上に直接格納されます。

root@truenas:~ # ls -lh /mnt/zpool/pool/NFS/pvc-3f9db7a1-a3cf-41d2-8784-38b0b3cd7a23/
-rw-r--r-- 1 root root 10G Mar 13 14:20 disk.img

8. ベンチマーク

8.1. 測定方法

fio(Flexible I/O Tester)を使い、各 VM のディスク I/O パフォーマンスを測定します。

ベンチマークの位置づけ: 本検証は Proxmox VE 上のネステッド仮想化環境で実施しているため、絶対値(MB/s や IOPS)はベアメタル環境と異なる可能性があります。3方式間の相対的な傾向(順位や倍率)を読み取ることが目的です。

各テスト VM には cloud-init で fio(v3.23)をインストール済みです。

測定条件は以下のとおりです。

項目
測定日 2026年3月13日
fio バージョン 3.23
テストファイルサイズ 1 GB
測定時間 各テスト 30 秒
並列ジョブ数 1
VM vCPU 1
VM メモリ 2 GiB
VM OS openSUSE Leap 15.6(Cloud イメージ)
測定パターン ブロックサイズ 想定用途
シーケンシャル読み取り 128k 大容量ファイルの読み込み
シーケンシャル書き込み 128k ログ書き込み、ファイルコピー
ランダム読み取り 4k データベース検索、OS 起動
ランダム書き込み 4k データベース更新、VM 動作

各パターンについて、キャッシュなし(--direct=1、ストレージ純粋性能)とキャッシュあり(--direct=0、実運用に近い性能)の2モードを測定します。

共通パラメータ: --size=1G --numjobs=1 --runtime=30 --time_based --group_reporting

8.2. 測定コマンド

各 VM に SSH 接続し、以下のコマンドを実行します(キャッシュなしの例)。

echo "=== [Cache OFF] Sequential Read (128k) ===" && \
sudo fio --name=test --filename=/tmp/fio --size=1G --direct=1 \
  --bs=128k --rw=read --numjobs=1 --runtime=30 --time_based \
  --group_reporting 2>&1 | grep -E "READ:|iops|bw=" && \
echo "=== [Cache OFF] Sequential Write (128k) ===" && \
sudo fio --name=test --filename=/tmp/fio --size=1G --direct=1 \
  --bs=128k --rw=write --numjobs=1 --runtime=30 --time_based \
  --group_reporting 2>&1 | grep -E "WRITE:|iops|bw=" && \
echo "=== [Cache OFF] Random Read (4k) ===" && \
sudo fio --name=test --filename=/tmp/fio --size=1G --direct=1 \
  --bs=4k --rw=randread --numjobs=1 --runtime=30 --time_based \
  --group_reporting 2>&1 | grep -E "READ:|iops|bw=" && \
echo "=== [Cache OFF] Random Write (4k) ===" && \
sudo fio --name=test --filename=/tmp/fio --size=1G --direct=1 \
  --bs=4k --rw=randwrite --numjobs=1 --runtime=30 --time_based \
  --group_reporting 2>&1 | grep -E "WRITE:|iops|bw=" && \
sudo rm -f /tmp/fio && echo "=== Cache OFF DONE ==="

8.3. 測定結果

キャッシュなし(direct=1)— ストレージ純粋性能

test-longhorn-vm(harvester-longhorn):

=== [Cache OFF] Sequential Read (128k) ===
   READ: bw=85.7MiB/s (89.8MB/s), io=2570MiB, run=30001msec
   iops        : avg=687.41
=== [Cache OFF] Sequential Write (128k) ===
  WRITE: bw=49.5MiB/s (51.9MB/s), io=1486MiB, run=30001msec
   iops        : avg=400.34
=== [Cache OFF] Random Read (4k) ===
   READ: bw=4677KiB/s (4789kB/s), io=137MiB, run=30001msec
   iops        : avg=1170.22
=== [Cache OFF] Random Write (4k) ===
  WRITE: bw=3461KiB/s (3545kB/s), io=101MiB, run=30001msec
   iops        : avg=866.36

test-iscsi-vm(longhorn-iscsi):

=== [Cache OFF] Sequential Read (128k) ===
   READ: bw=63.5MiB/s (66.6MB/s), io=1906MiB, run=30001msec
   iops        : avg=507.92
=== [Cache OFF] Sequential Write (128k) ===
  WRITE: bw=33.0MiB/s (34.6MB/s), io=991MiB, run=30001msec
   iops        : avg=267.51
=== [Cache OFF] Random Read (4k) ===
   READ: bw=3852KiB/s (3944kB/s), io=113MiB, run=30001msec
   iops        : avg=964.64
=== [Cache OFF] Random Write (4k) ===
  WRITE: bw=3068KiB/s (3141kB/s), io=89.9MiB, run=30001msec
   iops        : avg=769.10

test-nfs-vm(nfs-csi):

=== [Cache OFF] Sequential Read (128k) ===
   READ: bw=256MiB/s (268MB/s), io=7674MiB, run=30001msec
   iops        : avg=2048.44
=== [Cache OFF] Sequential Write (128k) ===
  WRITE: bw=77.0MiB/s (81.8MB/s), io=2339MiB, run=30001msec
   iops        : avg=622.24
=== [Cache OFF] Random Read (4k) ===
   READ: bw=10.3MiB/s (10.8MB/s), io=308MiB, run=30001msec
   iops        : avg=2634.47
=== [Cache OFF] Random Write (4k) ===
  WRITE: bw=4170KiB/s (4270kB/s), io=122MiB, run=30001msec
   iops        : avg=1043.58

キャッシュなし比較表:

テスト harvester-longhorn longhorn-iscsi nfs-csi
Seq Read 128k 85.7 MiB/s(687 IOPS) 63.5 MiB/s(508 IOPS) 256 MiB/s(2048 IOPS)
Seq Write 128k 49.5 MiB/s(400 IOPS) 33.0 MiB/s(268 IOPS) 77.0 MiB/s(622 IOPS)
Rand Read 4k 4.6 MiB/s(1170 IOPS) 3.8 MiB/s(965 IOPS) 10.3 MiB/s(2634 IOPS)
Rand Write 4k 3.4 MiB/s(866 IOPS) 3.0 MiB/s(769 IOPS) 4.1 MiB/s(1044 IOPS)

キャッシュあり(direct=0)— 実運用に近い性能

test-longhorn-vm(harvester-longhorn):

=== [Cache ON] Sequential Read (128k) ===
   READ: bw=214MiB/s (224MB/s), io=6417MiB, run=30001msec
   iops        : avg=1755.83
=== [Cache ON] Sequential Write (128k) ===
  WRITE: bw=128MiB/s (135MB/s), io=3852MiB, run=30003msec
   iops        : avg=1029.61
=== [Cache ON] Random Read (4k) ===
   READ: bw=4717KiB/s (4831kB/s), io=138MiB, run=30001msec
   iops        : avg=1180.27
=== [Cache ON] Random Write (4k) ===
  WRITE: bw=24.5MiB/s (25.7MB/s), io=734MiB, run=30008msec
   iops        : avg=6276.66

test-iscsi-vm(longhorn-iscsi):

=== [Cache ON] Sequential Read (128k) ===
   READ: bw=203MiB/s (213MB/s), io=6102MiB, run=30004msec
   iops        : avg=1627.02
=== [Cache ON] Sequential Write (128k) ===
  WRITE: bw=80.5MiB/s (84.4MB/s), io=2417MiB, run=30007msec
   iops        : avg=646.47
=== [Cache ON] Random Read (4k) ===
   READ: bw=3562KiB/s (3648kB/s), io=104MiB, run=30001msec
   iops        : avg=905.57
=== [Cache ON] Random Write (4k) ===
  WRITE: bw=16.4MiB/s (17.2MB/s), io=492MiB, run=30011msec
   iops        : avg=4211.17

test-nfs-vm(nfs-csi):

=== [Cache ON] Sequential Read (128k) ===
   READ: bw=1081MiB/s (1134MB/s), io=31.7GiB, run=30049msec
   iops        : avg=8794.64
=== [Cache ON] Sequential Write (128k) ===
  WRITE: bw=357MiB/s (375MB/s), io=10.5GiB, run=30001msec
   iops        : avg=2854.86
=== [Cache ON] Random Read (4k) ===
   READ: bw=9426KiB/s (9653kB/s), io=276MiB, run=30001msec
   iops        : avg=2355.08
=== [Cache ON] Random Write (4k) ===
  WRITE: bw=31.1MiB/s (32.6MB/s), io=933MiB, run=30006msec
   iops        : avg=7946.20

キャッシュあり比較表:

テスト harvester-longhorn longhorn-iscsi nfs-csi
Seq Read 128k 214 MiB/s(1756 IOPS) 203 MiB/s(1627 IOPS) 1081 MiB/s(8795 IOPS)
Seq Write 128k 128 MiB/s(1030 IOPS) 80.5 MiB/s(646 IOPS) 357 MiB/s(2855 IOPS)
Rand Read 4k 4.7 MiB/s(1180 IOPS) 3.6 MiB/s(906 IOPS) 9.4 MiB/s(2355 IOPS)
Rand Write 4k 24.5 MiB/s(6277 IOPS) 16.4 MiB/s(4211 IOPS) 31.1 MiB/s(7946 IOPS)

8.4. 考察

想定と実測の比較

StorageClass 想定 実測結果
harvester-longhorn 最も高速と想定(ローカルディスク直接アクセス) 2位
longhorn-iscsi 中程度(ネットワーク経由 iSCSI) 3位(最も遅い)
nfs-csi 中程度(ネットワーク経由 NFS) 1位(全テストでトップ)

正直、最初は「ローカルディスク構成が素直に最も高速だろう」と考えていました。ところが実測では nfs-csi が全テストパターンで上回り、この検証で予想と異なる結果になりました。

要因1: Longhorn のレプリカ同期オーバーヘッド

harvester-longhorn と longhorn-iscsi は、書き込み時に3つのレプリカすべてへ同期的にデータを書き込みます。3つのレプリカすべてが書き込み完了を返すまでアプリケーションへの応答を待つため、書き込み性能が大きく制約されます。nfs-csi は TrueNAS 上に単一コピーを書き込むだけで完了するため、レプリカ同期のオーバーヘッドがありません。

テスト harvester-longhorn nfs-csi 倍率
Seq Write(direct=1) 49.5 MiB/s 77.0 MiB/s 1.6倍
Rand Write(direct=1) 3.4 MiB/s 4.1 MiB/s 1.2倍

要因2: iSCSI の二重ネットワーク経路

要因2: iSCSI の二重ネットワーク経路 longhorn-iscsi が最も遅い理由は、I/O パスが二重にネットワークを経由するためです。Longhorn は受け取った書き込みを3ノードのレプリカに同期しますが、各レプリカの実体は TrueNAS の iSCSI LUN 上にあります。そのため、レプリカ同期のノード間通信に加え、各ノードから TrueNAS への iSCSI 通信が発生し、ネットワークレイテンシが二重に加算されます。

StorageClass Seq Read(direct=1) harvester-longhorn 比
harvester-longhorn 85.7 MiB/s 1.0倍
longhorn-iscsi 63.5 MiB/s 0.74倍
nfs-csi 256 MiB/s 2.99倍

要因3: NFS のキャッシュ効率

キャッシュあり(direct=0)の Seq Read で nfs-csi が 1081 MiB/s を記録しています。これは物理的なネットワーク帯域(1 GbE ≒ 125 MB/s)を大幅に超過しており、OS のページキャッシュからの読み取りが大部分を占めていることを示しています。テストファイル(1 GB)が VM メモリ(2 GiB)に収まるため、どの方式でもキャッシュに載る条件は同じですが、nfs-csi ではレプリカ同期が不要なため、キャッシュフラッシュ時のオーバーヘッドも小さく、キャッシュの恩恵がより大きく現れています。

StorageClass direct=1 → direct=0(Seq Read)
harvester-longhorn 85.7 → 214 MiB/s(2.5倍)
longhorn-iscsi 63.5 → 203 MiB/s(3.2倍)
nfs-csi 256 → 1081 MiB/s(4.2倍)

direct=1 と direct=0 の差分を見ると、キャッシュ効果の寄与は nfs-csi で特に大きく、本環境ではストレージそのものの帯域だけでなく OS ページキャッシュの効き方が結果に強く影響していることが分かります。今回は単回測定であり厳密な切り分けまではできていませんが、少なくとも「NFS が常に純粋なディスク性能で勝つ」というより、「レプリカ同期の有無とキャッシュの効き方が重なって差が拡大した」と捉えるのが自然です。

要因4: 本環境固有の制約

本検証は、筆者の自宅ラボに構築した Proxmox VE 上のネステッド仮想化環境で実施しています。すべての VM が同一 Proxmox ホストの CPU・メモリ・ストレージを共有しており、仮想ネットワーク(vmbr0)経由の通信は物理 NIC を経由しません。ネステッド仮想化では I/O パスが長くなり、Longhorn のレプリカ同期コストが相対的に増大します。ベアメタル環境ではローカルディスクアクセスの優位性がより明確に出る可能性があります。

この結果から得られる重要な知見は、「Longhorn のレプリカ同期は安全性と引き換えに書き込み性能のコストがある」という点です。レプリカ数を削減する、あるいは Longhorn を経由しない構成を検討する動機がここから生まれます。

この結果を見たとき、次は「Longhorn をバイパスすると I/O パスの差がそのまま性能差として表れるのか」を切り分けてみたい、と強く感じました。後編では、Longhorn をバイパスする democratic-csi を使った構成で、この仮説を検証します。

9. トラブルシューティング

本構築で遭遇した問題とその対処法をまとめます。

ISO が取り外されず再起動後にインストーラが再起動する — Proxmox VM では ISO が自動的に取り外されません。インストール完了後のカウントダウンで CTRL+C を押し、qm set <VMID> --ide2 none,media=cdrom --boot order=scsi0 を実行してから手動で reboot します。ISO 接続のまま再起動すると、2回目のインストーラがブートローダーを破損させます。

vCPU 4コアでは Cluster が Ready にならない — Harvester のシステム Pod の CPU request 合計が4コアを超えるため、コアコンポーネントがスケジュールできません。最低8コアの割り当てが必要です。

kubectl describe pod -n harvester-system harvester-7956844fbd-2dkkt | tail -5
  Warning  FailedScheduling  default-scheduler
    0/1 nodes are available: 1 Insufficient cpu.

Web UI のパスワードが通らない — harvester01 のコンソールで Ctrl+Alt+F2 を押してシェルに切り替え、以下でリセットします。

kubectl -n cattle-system exec \
  $(kubectl -n cattle-system get pods -l app=rancher \
    --no-headers -o custom-columns=":metadata.name" | head -1) \
  -- reset-password

kubectl patch で default-disk が消失する — patch コマンドに既存の default-disk エントリを含めないと、Longhorn がそのディスクを削除します。必ず既存ディスク名を確認してから patch してください。

NFS の PVC が Pending のまま Bound にならない — TrueNAS 側の NFS エクスポートディレクトリのパーミッションが不足しています。sudo chmod 777 /mnt/zpool/pool/NFS で解決します(自宅ラボ向け設定)。

10. まとめ

本記事では、Proxmox VE 上に Harvester v1.7.1 の3ノードクラスタを構築し、3種類のストレージ方式(harvester-longhorn / longhorn-iscsi / nfs-csi)の構成とベンチマーク比較を行いました。

本検証では、Longhorn を経由しない nfs-csi が最も高い性能を示し、Longhorn のレプリカ同期が書き込み性能に影響することを確認できました。一方で、harvester-longhornlonghorn-iscsi は、性能面では不利になるものの、Longhorn の管理機能や冗長性を活用できる点が強みです。

つまり、性能を優先するなら nfs-csi、Harvester 標準の運用性や Longhorn の冗長性を重視するなら harvester-longhorn、外部 iSCSI ストレージを Longhorn に統合したいなら longhorn-iscsi という整理になります。どの方式が最適かは、性能・冗長性・容量効率のどれを優先するかによって変わります。

11. 参考情報

12. 商標について

本記事で使用している以下の名称は、各社の商標または登録商標です。

  • Proxmox、Proxmox VE は、Proxmox Server Solutions GmbH の商標です。
  • SUSE、Harvester、Longhorn は、SUSE LLC の商標または登録商標です。
  • TrueNAS、TrueNAS SCALE は、iXsystems, Inc. の商標または登録商標です。
  • AMD、Ryzen は、Advanced Micro Devices, Inc. の商標です。
  • Kubernetes は、The Linux Foundation の登録商標です。
  • HPE、HPE CSI Driver for Kubernetes は、Hewlett Packard Enterprise Development LP の商標または登録商標です。

その他、本記事に記載されている会社名、製品名は、各社の商標または登録商標です。

13. 免責事項

本記事の内容は、2026年3月時点の情報に基づいており、将来的に変更される可能性があります。本記事の内容を実施したことにより発生した損害について、筆者およびNTT西日本は一切の責任を負いかねます。本記事の内容を参考に作業を行う場合は、自己責任でお願いいたします。

本記事に記載されているコマンド、設定値、構成例は、特定の検証環境で動作を確認したものであり、すべての環境で同一の結果が得られることを保証するものではありません。本番環境への適用にあたっては、十分な検証とバックアップを実施してください。

13.1. データ損失に関する注意事項

  • 本記事で紹介する StorageClass のうち、Longhorn をバイパスする方式(nfs-csi)はノードレベルのレプリケーションを行いません。データの冗長性は外部ストレージ(TrueNAS)側の構成に依存します。
  • reclaimPolicy: Delete を設定した StorageClass では、PVC の削除に伴いデータが自動的に削除されます。意図しないデータ損失を防ぐため、重要なデータには Retain ポリシーの使用や定期的なバックアップを検討してください。
  • ネステッド仮想化環境では、Proxmox ホストの障害がすべてのゲスト VM に波及します。本検証環境はシングルホスト構成であり、本番利用には適しません。

14. 執筆者

平岡 征一朗(NTT西日本) 文教(大学)担当のシステムエンジニアです。インフラからアプリまでトラブルシュートが大好きです。

© NTT WEST, Inc.