はじめに
NTT西日本の平岡です。
前編では、Harvester 標準の harvester-longhorn、TrueNAS iSCSI LUN を Longhorn に統合した longhorn-iscsi、そして csi-driver-nfs による nfs-csi の3方式を構築し、fio ベンチマークで性能を比較しました。Longhorn のレプリカ同期が書き込みオーバーヘッドとなること、NFS/CSI が Longhorn をバイパスすることで高い転送速度を示すことを確認しています。
後編では、Longhorn を経由しない別のアプローチとして、以下の3方式を追加検証しました。
- democratic-csi: TrueNAS SCALE の iSCSI LUN を Kubernetes PVC として直接マウントする CSI ドライバです。Longhorn を経由せず、RWO アクセスを提供します。
- OpenEBS Dynamic NFS Provisioner: democratic-csi が提供する RWO PVC をバックエンドとし、その上にカーネルモード NFS サーバー Pod を自動生成することで RWX アクセスを実現します。
- nfs-ganesha-server-provisioner: 同じく democratic-csi の RWO PVC をバックエンドとしますが、NFS-Ganesha(ユーザー空間 NFS サーバー)を常駐 Pod として配置し、RWX アクセスを提供します。
3方式はいずれも最終的に TrueNAS SCALE の iSCSI LUN にデータを格納しますが、I/O パスの段数、NFS 実装の有無と種類、Pod のライフサイクルが異なります。これらの違いが性能・可用性・運用性にどう影響するかを、構成図・ベンチマーク・障害分析を通じて明らかにします。
なお、本検証は筆者の自宅ラボに構築した Proxmox VE 上のネステッド仮想化環境で実施しており、所属組織の環境は使用していません。ベンチマークの絶対値はベアメタル環境と異なる可能性がありますが、3方式間の相対的な傾向(順位・倍率)は有効な比較指標となります。
対象読者
- Harvester や KubeVirt ベースの仮想化基盤を検討している方
- democratic-csi、OpenEBS、nfs-ganesha の違いを比較したい方
- RWX ストレージの実装パターンやバックアップ制約を把握したい方
この記事で分かること
- Longhorn を経由しない 3 方式(
democratic-csi/OpenEBS Dynamic NFS Provisioner/nfs-ganesha)の構造差 - Pod 内 fio による比較結果と、RWX 実装の違いが性能や障害影響にどう表れるか
- 全 6 方式を踏まえた選定指針と、どのユースケースにどの方式が向くか
ベンチマーク結果を先に見たい方は 8. ベンチマーク、選定指針を先に見たい方は 9.2. ユースケース別の選定指針 をご覧ください。
- 1. RWO と RWX ― NFS サーバー Pod による変換パターン
- 2. 検証環境
- 3. 全体アーキテクチャ
- 4. democratic-csi の導入
- 5. OpenEBS Dynamic NFS Provisioner の導入
- 6. nfs-ganesha-server-provisioner の導入
- 7. OS イメージのインポート検証
- 8. ベンチマーク
- 9. 総合比較と選定指針
- 10. 参考情報
- 11. 商標について
- 12. 免責事項
- 13. 執筆者
1. RWO と RWX ― NFS サーバー Pod による変換パターン
Kubernetes のストレージには、アクセスモードという概念があります。RWO(ReadWriteOnce)は単一ノードからのみ読み書き可能なモード、RWX(ReadWriteMany)は複数ノードから同時に読み書き可能なモードです。
iSCSI や FC(ファイバチャネル)などのブロックストレージは、プロトコルの性質上 RWO に限定されます。ブロックデバイスは同時に複数のノードからマウントするとファイルシステムの破損を招くためです。一方、VM のライブマイグレーションや複数 Pod からの共有アクセスには RWX が必要となる場面があります。
この RWO と RWX のギャップを埋める一般的なパターンが「NFS サーバー Pod による変換」です。具体的には、RWO のブロック PVC をバックエンドとして NFS サーバー Pod を配置し、その NFS サーバー Pod がファイルシステムレベルで NFS プロトコルを提供することで、複数ノードからの同時アクセス(RWX)を実現します。

本記事で検証する3方式のうち、OpenEBS Dynamic NFS Provisioner と nfs-ganesha-server-provisioner はまさにこのパターンの実装です。両者とも democratic-csi が提供する RWO の iSCSI PVC をバックエンドとし、その上に NFS サーバー Pod を配置して RWX を実現します。違いは NFS サーバーの実装方式(カーネルモード vs ユーザー空間)と Pod のライフサイクル管理(PVC 単位 vs 共有)にあります。
なお、この「RWO ブロック PVC を NFS で RWX に変換する」という設計思想は、本検証のような OSS 構成に限らず、エンタープライズ環境でも広く採用されています。たとえば、HPE CSI Driver for Kubernetes にも NFS Server Provisioner コンポーネントが用意されており、HPE Alletra などのブロックストレージを RWX として提供する際に同様のアーキテクチャが使われています。バックエンドのストレージ製品は異なっても、「ブロック → NFS → RWX」という変換パターン自体は共通の設計手法であり、本記事の検証結果はこうしたエンタープライズ構成を検討する際の参考にもなります。
2. 検証環境
2.1. ハードウェアとインフラ構成
検証環境は前編と同一で、いずれも筆者の自宅ラボ上に構築したものです。
| 項目 | 仕様 |
|---|---|
| Proxmox VE ホスト | AMD Ryzen 5 5600G, 128 GB RAM, local-zfs 5.6 TB, Proxmox VE 8.3.0 (kernel 6.8.12-4-pve) |
| Harvester クラスタ | 3ノード (harvester01-03), 各 8 vCPU / 16 GB RAM / 400 GB disk, Management VIP 192.0.2.70, Harvester v1.7.1 |
| TrueNAS SCALE | VM ID 102, IP 192.0.2.4, TrueNAS SCALE 24.10.2, democratic-csi 用 Dataset: <your-dataset-path>, プロトコル: iSCSI |
| ネットワーク | 単一仮想ブリッジ vmbr0 (192.0.2.0/24) |
2.2. 前編で構築済みの StorageClass
| StorageClass | バックエンド | プロトコル | アクセスモード | 管理レイヤ |
|---|---|---|---|---|
| harvester-longhorn | ノードローカルディスク | ローカル | RWO/RWX | Longhorn |
| longhorn-iscsi | TrueNAS iSCSI LUN | iSCSI | RWO/RWX | Longhorn |
| nfs-csi | TrueNAS NFS 共有 | NFS | RWX | csi-driver-nfs |
2.3. 後編で追加する StorageClass
| StorageClass | バックエンド | プロトコル | アクセスモード | 管理レイヤ |
|---|---|---|---|---|
| truenas-iscsi | TrueNAS iSCSI LUN (zvol) | iSCSI | RWO | democratic-csi |
| openebs-rwx-iscsi | truenas-iscsi + カーネル NFS | iSCSI + NFS | RWX | OpenEBS + democratic-csi |
| nfs-ganesha | truenas-iscsi + NFS-Ganesha | iSCSI + NFS | RWX | nfs-ganesha + democratic-csi |
3. 全体アーキテクチャ
3.1. 3方式の I/O パス概要
ここから後編の本題に入ります。まずは全体像をつかんでいただくため、3方式の I/O パスを並べて見ていきます。
後編で検証する3方式は、すべて TrueNAS SCALE の iSCSI LUN を最終的なデータ格納先とします。違いは、Pod からデータが LUN に到達するまでの経路(I/O パス)です。
3.2. 方式1: democratic-csi(直接 iSCSI)

democratic-csi は Longhorn を経由せず、Kubernetes の CSI インターフェースを通じて truenas-iscsi の RWO PVC を作成し、そのバックエンドとして TrueNAS の iSCSI LUN(zvol)を利用します。図で示したとおり、Kubernetes リソース層では「ユーザー Pod → RWO PVC」、実ストレージ層では「iSCSI Initiator → TrueNAS SCALE」という流れになっています。I/O パスは2段階(Pod → iSCSI → TrueNAS)であり、3方式の中で最も短くなります。ただしアクセスモードは RWO に限定されるため、複数ノードからの同時マウントや VM のライブマイグレーションには対応できません。
この方式は、RWX が不要で、まずは Longhorn を介さない iSCSI 直結の特性を確認したい場面に向いています。
シングルポイント: TrueNAS SCALE が停止すると、このストレージを使用するすべての Pod が I/O エラーとなります。zvol は TrueNAS 上に1コピーのみ存在し、Longhorn のようなノード間レプリケーションは行われません。データ冗長性は TrueNAS 側の ZFS 構成(ミラー、RAIDZ 等)に完全に依存します。
3.3. 方式2: OpenEBS Dynamic NFS Provisioner(カーネル NFS)

OpenEBS Dynamic NFS Provisioner は、RWX PVC が要求されると、図の Kubernetes リソース層に示した以下のリソースを自動的に作成します。
ここでいう Service は、Kubernetes 内で NFS サーバー Pod への接続先を安定して提供するためのリソースです。NFS サーバー Pod 自体は再作成や再配置で実体が変わる可能性がありますが、ユーザー Pod は Pod の実体を直接参照するのではなく、この Service を経由することで安定してアクセスできます。
- バックエンド RWO PVC(StorageClass
truenas-iscsiを使用) - その PVC をマウントした NFS サーバー Pod(knfsd / カーネルモード)
- NFS サーバー Pod を公開する Service
ユーザー Pod は Service を経由して NFS サーバー Pod に接続し、NFS サーバー Pod はバックエンド RWO PVC を介して TrueNAS SCALE の iSCSI Target を利用します。I/O パスは3段階(Pod → NFS → NFS サーバー Pod → iSCSI → TrueNAS)です。
RWX を確保しつつ、PVC ごとに独立した NFS サーバー Pod とバックエンド RWO PVC を自動管理したい場合には、この方式が有力です。
シングルポイントは2箇所あります。第一に TrueNAS SCALE の停止、第二に NFS サーバー Pod の停止です。NFS サーバー Pod は PVC ごとに1つ生成されるため、特定の PVC の NFS サーバー Pod が停止した場合、その PVC を使用するすべての Pod が影響を受けます。ただし他の PVC には影響しません。PVC を削除すると、NFS サーバー Pod・バックエンド RWO PVC・TrueNAS の zvol と iSCSI Target がすべて自動的にクリーンアップされます。
3.4. 方式3: nfs-ganesha-server-provisioner(ユーザー空間 NFS)

nfs-ganesha-server-provisioner は、Helm インストール時に単一の NFS-Ganesha Pod を常駐させます。この Pod はユーザー空間で NFS プロトコルを処理し、図で示した共有バックエンド RWO PVC(本検証では 50 GiB、StorageClass truenas-iscsi)をマウントします。これは、OS イメージのインポート可否も確認しつつ、RWX PVC を複数作成して比較できるだけの余地を確保するための検証用サイズです。ユーザーが RWX PVC を作成すると、この共有バックエンド RWO PVC 上にサブディレクトリが作成されます。
I/O パスの段数は OpenEBS と同じ3段階ですが、NFS 実装がカーネルモード(knfsd)ではなくユーザー空間(NFS-Ganesha)である点が異なります。knfsd は Linux カーネル内で動作し、Linux から通常のファイルシステムとして見えるストレージをそのまま NFS で公開するのに向いています。一方、NFS-Ganesha は通常のアプリケーションとしてユーザー空間で動作するため、カーネル空間との間で処理の受け渡しが増えやすく、一般にスループットではカーネルモードに劣る傾向があります。その代わり、ユーザー空間ソフトウェアとして実装の自由度が高く、バックエンド構成の選択肢を広げやすいという特徴があります。
単一の共有基盤で RWX を提供し、PVC ごとに Pod 数が増える構成を避けたい場合には、この方式は有力な選択肢になります。
シングルポイントは2箇所あります。TrueNAS SCALE の停止に加え、NFS-Ganesha Pod の停止があります。OpenEBS と異なり、NFS-Ganesha Pod は共有バックエンド RWO PVC を利用する単一 Pod であるため、この Pod が停止するとすべての RWX PVC が同時にアクセス不能となります。障害の影響範囲が OpenEBS より広い点に注意が必要です。PVC を削除した場合はサブディレクトリのみが削除され、共有バックエンド RWO PVC と NFS-Ganesha Pod は存続します。
3.5. 3方式の構造比較
| 比較項目 | democratic-csi | OpenEBS NFS | nfs-ganesha |
|---|---|---|---|
| StorageClass | truenas-iscsi | openebs-rwx-iscsi | nfs-ganesha |
| アクセスモード | RWO | RWX | RWX |
| 主な I/O 経路 | ユーザー Pod → RWO PVC → iSCSI → TrueNAS SCALE | ユーザー Pod → Service → NFS サーバー Pod → バックエンド RWO PVC → iSCSI → TrueNAS SCALE | ユーザー Pod → NFS-Ganesha Pod → 共有バックエンド RWO PVC → iSCSI → TrueNAS SCALE |
| NFS 実装 | なし | カーネルモード(knfsd) | ユーザー空間(NFS-Ganesha) |
| Service | なし | あり | なし |
| NFS サーバー Pod / NFS-Ganesha Pod | なし | バックエンド RWO PVC ごとに1つ自動生成 | 全 PVC で1つ共有(常駐) |
| バックエンド RWO PVC | 1 PVC = 1 zvol | 1 PVC = 1 zvol(PVC ごと) | 1つの共有バックエンド RWO PVC(50 GiB) |
| TrueNAS リソース | RWO PVC 数分の zvol + iSCSI Target | バックエンド RWO PVC 数分の zvol + iSCSI Target | 1つの zvol + 1つの iSCSI Target |
| PVC 削除時の動作 | zvol + iSCSI Target 自動削除 | NFS サーバー Pod + バックエンド RWO PVC + zvol + iSCSI Target を全自動削除 | サブディレクトリのみ削除 |
| Single Point of Failure(SPOF) | TrueNAS SCALE | TrueNAS SCALE + 該当 NFS サーバー Pod | TrueNAS SCALE + NFS-Ganesha Pod(全 PVC に影響) |
| VM ライブマイグレーション | 不可(RWO) | 可能(RWX) | 可能(RWX) |
| コンテキストスイッチ | 最小 | 少 | 多(ユーザー空間 NFS) |
| リソース消費 | 最小 | PVC 数に比例して増加 | 単一 Pod で一定 |
| プロジェクト | コミュニティ | CNCF Sandbox | Kubernetes SIG Storage |
3.6. PVC のライフサイクル
Kubernetes に馴染みのない読者のために、PVC(PersistentVolumeClaim)のライフサイクルを簡単に整理します。PVC は Kubernetes におけるストレージの要求単位であり、従来の仮想化基盤における仮想ディスク(例: vSphere の VMDK ファイル)に相当する概念です。
PVC のライフサイクルは、大きく4つのフェーズに分かれます。まず「作成(Provisioning)」では、ユーザーが PVC マニフェストを適用すると、StorageClass に紐づいた CSI ドライバがバックエンドストレージ上にボリュームを自動作成します。本検証では democratic-csi が TrueNAS 上に zvol を作成する処理がこれに該当します。vSphere でいえば、データストア上に VMDK ファイルが作成される段階です。
次に「バインド(Binding)」では、作成されたボリューム(PV: PersistentVolume)と PVC が紐づけられます。PVC のステータスが Pending から Bound に変わり、利用可能な状態になります。
「マウント(Mounting)」では、Pod が起動する際に PVC で指定されたボリュームがノード上にマウントされます。iSCSI の場合はノードの iSCSI Initiator がターゲットに接続し、NFS の場合は NFS クライアントがマウントを行います。vSphere でいえば、VM に仮想ディスクをアタッチする操作に相当します。
最後に「削除(Reclaiming)」では、PVC が削除された際のボリュームの扱いが StorageClass の reclaimPolicy によって決まります。Delete ポリシーの場合、PVC の削除と同時にバックエンドのボリュームも自動削除されます。Retain ポリシーの場合、PVC を削除してもバックエンドのボリュームは保持されます。
ただし、削除時の実際の挙動は StorageClass の内部構造によって異なります。democratic-csi や openebs-rwx-iscsi のように PVC とバックエンドボリュームの対応が明確な構成では、Delete により TrueNAS 上の zvol や iSCSI Target まで自動削除されます。一方、nfs-ganesha のように複数の PVC が1つの共有バックエンド RWO PVC を利用する構成では、個別の PVC を削除しても共有バックエンド全体は削除されず、該当サブディレクトリのみが削除されます。
本検証の StorageClass はすべて Delete ポリシーを使用していますが、どこまで削除されるかは上記の構造差に依存します。重要なデータを扱う場合は Retain ポリシーの採用や定期的なバックアップの実施を検討してください。
4. democratic-csi の導入
4.1. democratic-csi とは
democratic-csi は、TrueNAS の API を利用して zvol の作成・削除・iSCSI ターゲットの公開を自動化する CSI ドライバです。Longhorn を経由せず、Kubernetes の PVC 要求に応じて TrueNAS 上に直接 zvol を作成し、iSCSI 経由でノードにマウントします。
Harvester の標準構成では、外部 iSCSI LUN は Longhorn のディスクとして追加されます(前編の longhorn-iscsi)。この場合、Longhorn のレプリカ同期が行われるため、データ冗長性が確保される代わりに書き込みオーバーヘッドが発生します。democratic-csi はこのレプリカ同期をバイパスするため、書き込み性能の向上が期待できますが、データ冗長性は TrueNAS 側の ZFS 構成に完全に依存することになります。
4.2. Helm リポジトリの追加とインストール
Harvester ノード上で Helm が利用可能であることを確認します。
helm version
version.BuildInfo{Version:"v3.19.1", GitCommit:"...", GitTreeState:"clean", GoVersion:"go1.23.8"}
democratic-csi の Helm リポジトリを追加します。
helm repo add democratic-csi https://democratic-csi.github.io/charts/ helm repo update
4.3. TrueNAS API 設定
democratic-csi は TrueNAS の REST API を使用して zvol と iSCSI ターゲットを操作します。TrueNAS SCALE の Web UI から API キーを発行し、values ファイルに設定します。主要な設定項目は以下のとおりです。
driver: iSCSI プロトコルを指定httpConnection.host: TrueNAS の IP アドレス(192.0.2.4)httpConnection.apiKey: TrueNAS API キーiscsi.targetPortal: TrueNAS の iSCSI ポータル(192.0.2.4:3260)zfs.datasetParentName: zvol の作成先(例:<your-dataset-path>)
4.4. Helm インストール
helm install truenas-iscsi democratic-csi/democratic-csi \ --namespace democratic-csi \ --create-namespace \ -f democratic-csi-values.yaml
Pod の起動を確認します。
kubectl get pods -n democratic-csi
controller Pod と node Pod(各ノードに1つ)が Running となれば成功です。
4.5. StorageClass の確認
Helm インストールにより、StorageClass truenas-iscsi が自動生成されます。
kubectl get sc truenas-iscsi
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE truenas-iscsi org.democratic-csi.iscsi Delete Immediate true 30s
4.6. 動作確認: PVC の作成とマウント
テスト用の PVC を作成します。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-iscsi-pvc spec: accessModes: - ReadWriteOnce storageClassName: truenas-iscsi resources: requests: storage: 5Gi
kubectl apply -f test-iscsi-pvc.yaml kubectl get pvc test-iscsi-pvc
PVC が Bound となったことを確認します。TrueNAS の Web UI で、zvol と iSCSI ターゲットが自動生成されていることも併せて確認してください。
テスト Pod をデプロイして書き込みと読み取りを行い、動作を確認した後にクリーンアップします。
kubectl delete pvc test-iscsi-pvc
PVC の削除に伴い、TrueNAS 上の zvol と iSCSI ターゲットが自動的に削除されることを確認します。
5. OpenEBS Dynamic NFS Provisioner の導入
5.1. なぜ NFS Provisioner が必要か
democratic-csi が提供する truenas-iscsi は RWO アクセスに限定されます。iSCSI LUN はブロックデバイスであり、同時に複数ノードからマウントすることはできません。しかし Harvester 上で VM のライブマイグレーションを行うには RWX(複数ノード同時読み書き可能)なストレージが必要です。
OpenEBS Dynamic NFS Provisioner は、この問題を解決するために RWO PVC の上に自動的にカーネルモード NFS サーバー Pod を生成し、RWX アクセスを提供する仕組みです。
5.2. インストール手順
Helm リポジトリを追加します。
helm repo add openebs https://openebs.github.io/charts helm repo update
NFS Provisioner のみを有効化する values ファイル(openebs-nfs.yaml)を作成します。Mayastor、LVM Local PV、ZFS Local PV、LocalPV Provisioner などの不要なコンポーネントは無効化し、StorageClass の自動作成も無効にします。
engines: replicated: mayastor: enabled: false local: lvm: enabled: false zfs: enabled: false openebs-crds: csi: volumeSnapshots: enabled: false localpv-provisioner: enabled: false nfs-provisioner: enabled: true storageClass: enabled: false
この values ファイルの目的は、OpenEBS を総合ストレージ基盤として導入するのではなく、Dynamic NFS Provisioner だけを最小構成で利用することです。
5.3. Harvester Admission Webhook の回避
OpenEBS の Helm チャートはデフォルトで複数の StorageClass を作成しようとしますが、Harvester の Admission Webhook は CDI アノテーションのない StorageClass を拒否します。この問題を回避するため、Helm テンプレートをファイルに出力し、StorageClass の定義を除外してから適用します。
# マニフェスト全体を出力 helm template openebs-nfs openebs/openebs \ --namespace openebs \ --values openebs-nfs.yaml > openebs-all.yaml
Python 3 で StorageClass の定義を除外します。
python3 -c "
import sys
docs = open('openebs-all.yaml').read().split('---')
for doc in docs:
if 'kind: StorageClass' not in doc:
print('---')
print(doc)
" > openebs-no-sc.yaml
除外結果を確認します。StorageClass が含まれていなければ問題ありません。
grep "kind: StorageClass" openebs-no-sc.yaml # 出力なしであれば OK
マニフェストを適用します。
kubectl apply -f openebs-no-sc.yaml
NFS Provisioner の Pod が Running であることを確認します。
kubectl get pods -n openebs
NAME READY STATUS RESTARTS AGE openebs-nfs-nfs-provisioner-xxxxx-xxxxx 1/1 Running 0 3m
補足として、openebs-nfs-localpv-provisioner や openebs-nfs-ndm-* といった Pod が起動する場合がありますが、本構成では使用しません。Helm チャートの内部依存により作成されるものであり、Dynamic NFS Provisioner の動作自体には影響しません。
5.4. StorageClass の作成
Admission Webhook を回避して手動で StorageClass を作成します。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: openebs-rwx-iscsi annotations: openebs.io/cas-type: nfsrwx cdi.harvesterhci.io/storageProfileVolumeModeAccessModes: '{"Filesystem":["ReadWriteMany"]}' cas.openebs.io/config: | - name: NFSServerType value: kernel - name: BackendStorageClass value: truenas-iscsi provisioner: openebs.io/nfsrwx reclaimPolicy: Delete allowVolumeExpansion: true
BackendStorageClass に truenas-iscsi を指定し、カーネルモード NFS を使用します。NFSServerType: kernel は knfsd(カーネル NFS サーバー)を意味します。
kubectl apply -f openebs-rwx-iscsi-sc.yaml kubectl get sc openebs-rwx-iscsi
5.5. 動作確認: RWX PVC の作成
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-openebs-rwx spec: accessModes: - ReadWriteMany storageClassName: openebs-rwx-iscsi resources: requests: storage: 5Gi
kubectl apply -f test-openebs-rwx.yaml kubectl get pvc test-openebs-rwx
PVC が Bound となったことを確認します。バックエンドで以下のリソースが自動生成されます。
- NFS サーバー Pod(カーネルモード knfsd)
- バックエンド RWO PVC(StorageClass
truenas-iscsi) - TrueNAS 上の zvol と iSCSI ターゲット
5.6. 3ノード同時書き込みテスト
DaemonSet を使用して、3つの Harvester ノードから同一 PVC に同時書き込みを行います。
apiVersion: apps/v1 kind: DaemonSet metadata: name: rwx-test spec: selector: matchLabels: app: rwx-test template: metadata: labels: app: rwx-test spec: containers: - name: writer image: busybox command: ["/bin/sh", "-c"] args: - hostname > /mnt/data/rwx-test-$(hostname).txt && sleep 3600 volumeMounts: - name: shared mountPath: /mnt/data volumes: - name: shared persistentVolumeClaim: claimName: test-openebs-rwx
kubectl apply -f rwx-test-daemonset.yaml
各ノードの Pod からファイルの存在を確認します。
kubectl exec -it $(kubectl get pod -l app=rwx-test -o jsonpath='{.items[0].metadata.name}') -- ls /mnt/data/
rwx-test-harvester01.txt rwx-test-harvester02.txt rwx-test-harvester03.txt
3ノードすべてから書き込まれたファイルが確認できました。RWX アクセスが正常に機能していることが実証されています。
5.7. クリーンアップと自動削除の確認
kubectl delete daemonset rwx-test kubectl delete pvc test-openebs-rwx
PVC の削除に伴い、以下が自動的にクリーンアップされます。
- OpenEBS NFS サーバー Pod
- NFS Service
- バックエンド RWO PVC
- TrueNAS 上の zvol と iSCSI ターゲット
TrueNAS の Web UI で zvol と iSCSI ターゲットが削除されていることを確認します。全リソースの自動ライフサイクル管理が正常に動作しています。
6. nfs-ganesha-server-provisioner の導入
6.1. OpenEBS との構造的な違い
nfs-ganesha-server-provisioner は OpenEBS と同じく RWX を提供しますが、アーキテクチャが根本的に異なります。OpenEBS は PVC ごとに NFS サーバー Pod を生成する「1 PVC = 1 NFS サーバー」モデルであるのに対し、nfs-ganesha は単一の常駐 NFS-Ganesha Pod がすべての RWX PVC を処理する「N PVC = 1 NFS サーバー」モデルです。
この違いは運用に大きな影響を与えます。
OpenEBS の場合、PVC 数が増えると NFS サーバー Pod の数も比例して増加し、クラスタのリソース消費が増えます。一方、ある PVC の NFS サーバー Pod が停止しても、他の PVC は影響を受けません。障害の影響範囲が PVC 単位に限定されます。
nfs-ganesha の場合、PVC 数が増えても NFS-Ganesha Pod は1つのまま一定であり、リソース消費は安定します。しかし、この単一 Pod が停止するとすべての RWX PVC が同時にアクセス不能となります。障害の影響範囲が広いため、この Pod の可用性が運用上の重要な関心事となります。
6.2. インストール手順
Helm リポジトリを追加します。
helm repo add nfs-ganesha-server-and-external-provisioner \ https://kubernetes-sigs.github.io/nfs-ganesha-server-and-external-provisioner/ helm repo update
values ファイル(nfs-ganesha.yaml)を作成します。
persistence: enabled: true storageClass: truenas-iscsi size: 50Gi storageClass: name: nfs-ganesha defaultClass: false reclaimPolicy: Delete allowVolumeExpansion: true
persistence.storageClass に truenas-iscsi を指定することで、NFS-Ganesha のバックエンドとして democratic-csi 経由の iSCSI LUN を使用します。size: 50Gi はバックエンド PVC のサイズであり、この PVC 上にすべての RWX PVC のデータがサブディレクトリとして格納されます。
helm install nfs-ganesha nfs-ganesha-server-and-external-provisioner/nfs-ganesha-server-and-external-provisioner \ --namespace nfs-ganesha \ --create-namespace \ -f nfs-ganesha.yaml
6.3. Pod とバックエンド PVC の確認
kubectl get pods -n nfs-ganesha
NFS-Ganesha Pod が Running であることを確認します。
kubectl get pvc -n nfs-ganesha
バックエンド PVC(50 GiB、StorageClass truenas-iscsi、RWO)が Bound であることを確認します。TrueNAS 上に対応する zvol と iSCSI ターゲットが1つ生成されています。
6.4. 動作確認: RWX PVC の作成
2つの RWX PVC を作成して動作を確認します。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-ganesha-1 spec: accessModes: - ReadWriteMany storageClassName: nfs-ganesha resources: requests: storage: 5Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-ganesha-2 spec: accessModes: - ReadWriteMany storageClassName: nfs-ganesha resources: requests: storage: 3Gi
kubectl apply -f test-ganesha-pvcs.yaml kubectl get pvc test-ganesha-1 test-ganesha-2
2つの PVC が Bound となります。ここで重要なのは、バックエンド PVC は依然として1つ(50 GiB)のままであるという点です。新しい zvol や iSCSI ターゲットは追加生成されません。OpenEBS との構造的な違いがここに明確に表れています。
6.5. 3ノード同時書き込みテスト
OpenEBS と同様に DaemonSet を使用して3ノード同時書き込みを検証します。
kubectl apply -f rwx-test-daemonset-ganesha.yaml
kubectl exec -it $(kubectl get pod -l app=rwx-test-ganesha -o jsonpath='{.items[0].metadata.name}') -- ls /mnt/data/
rwx-test-harvester01.txt rwx-test-harvester02.txt rwx-test-harvester03.txt
3ノードからの同時書き込みが成功しました。
6.6. クリーンアップ時の動作の違い
kubectl delete daemonset rwx-test-ganesha kubectl delete pvc test-ganesha-1 test-ganesha-2
PVC を削除すると、バックエンド PVC 上のサブディレクトリのみが削除されます。NFS-Ganesha Pod、バックエンド PVC、TrueNAS 上の zvol と iSCSI ターゲットはすべて存続します。これは OpenEBS の全自動クリーンアップとは対照的であり、nfs-ganesha の「共有バックエンド」モデルによる設計上の違いです。
NFS-Ganesha 自体を撤去するには、helm uninstall を実行する必要があります。
7. OS イメージのインポート検証
7.1. 検証目的
Harvester で VM を作成するには、OS イメージを StorageClass に登録する必要があります。前編では harvester-longhorn、longhorn-iscsi、nfs-csi の3方式でイメージ登録が成功しました。後編の3方式でも同様にイメージ登録が可能かを検証します。
7.2. 検証結果
テスト対象の OS イメージは前編と同一の openSUSE Leap 15.6 Minimal VM(約 270 MB、qcow2 形式)です。
| StorageClass | イメージインポート | 結果 | 備考 |
|---|---|---|---|
| truenas-iscsi | URL 指定 | 失敗 | CDI アノテーションが Block PVC のみ対応。Filesystem PVC が必要なイメージインポートに非対応。 |
| openebs-rwx-iscsi | URL 指定 | 失敗 | qcow2 → raw 変換後のサイズ(約 877 MiB)が PVC の実効容量(約 822 MiB、NFS ファイルシステムオーバーヘッド約 55 MiB を差し引き)を超過。 |
| nfs-ganesha | URL 指定 | 成功 | バックエンド PVC が 50 GiB と大きいため、変換後のイメージサイズを十分に収容可能。 |
7.3. 失敗原因の分析
democratic-csi(truenas-iscsi)の失敗は、CDI(Containerized Data Importer)が iSCSI PVC を Block モードで認識することに起因します。Harvester のイメージインポートは Filesystem モードの PVC を必要とするため、democratic-csi の iSCSI PVC は直接利用できません。
OpenEBS(openebs-rwx-iscsi)の失敗は、サイズ計算のミスマッチに起因します。ユーザーが指定する PVC サイズ(例: 1 GiB)に対し、NFS ファイルシステムのメタデータとオーバーヘッドが約 55 MiB を消費するため、実効容量が不足します。PVC サイズを大きく指定すれば回避可能ですが、イメージごとに適切なサイズを見積もる必要があります。
nfs-ganesha の成功は、50 GiB の共有バックエンド PVC に十分な空き容量があったためです。ただし、イメージを大量に登録すると 50 GiB の容量を圧迫する可能性があります。OpenEBS 側で想定どおりに進まなかったことで、NFS オーバーヘッドや実効容量の見積もりが想像以上にシビアだと実感できたのは、この検証で得られた大きな学びでした。
7.4. 検証から得られた知見
イメージインポートの可否は StorageClass の技術的な制約に依存します。VM のデータストアとしての利用とイメージインポートは異なる要件を持つため、すべての StorageClass がイメージインポートに対応するとは限りません。実運用では、イメージインポート用の StorageClass と VM データストア用の StorageClass を分けて設計することも有効な選択肢です。
8. ベンチマーク
8.1. 測定条件
本ベンチマークでは、前編の VM 内測定とは異なり、Pod 内から直接 fio を実行しています。当初は前編と同じ VM 方式を予定していましたが、truenas-iscsi が VM イメージのインポートに非対応、openebs-rwx-iscsi が容量不足でインポートに失敗したため、3方式の条件を揃えるために Pod 方式に統一しました。
VM 経由の場合は QEMU の仮想ディスクエミュレーション層が追加されるため絶対値は異なりますが、このオーバーヘッドは3方式すべてに等しく加わるため、方式間の相対比較には影響しません。
本来のユースケースは Harvester 上の VM ディスクとしての利用ですが、後編では3方式すべてで同一条件の VM を用意できなかったため、測定対象を Pod 内 fio に統一しました。そのため、本節の結果は「Harvester 上の VM そのものの絶対性能」ではなく、「各 StorageClass のバックエンド構成差が相対的な I/O 特性にどう表れるか」を見るためのものとして解釈してください。
結果として、VM 方式で揃えられなかったこと自体が、各方式の制約を整理するうえで大きな収穫でした。
| 項目 | 値 |
|---|---|
| 測定ツール | fio(コンテナイメージ: xridge/fio:latest) |
| テストファイルサイズ | 1 GB |
| 実行時間 | 30秒/テスト |
| ジョブ数 | 1 |
| PVC サイズ | 10 GiB(各方式) |
| 実行ノード | harvester01(同一ノードに統一) |
| I/O パターン | Sequential Read 128k, Sequential Write 128k, Random Read 4k, Random Write 4k |
| キャッシュモード | direct=1(キャッシュ無効)、direct=0(キャッシュ有効) |
ネステッド仮想化環境での測定であるため、絶対値はベアメタル環境と異なる可能性があります。本ベンチマークの目的は3方式間の相対的な性能傾向を把握することにあります。
8.2. PVC 作成
3つの StorageClass それぞれに PVC(10 GiB)を作成しています。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: bench-democratic namespace: default spec: accessModes: ["ReadWriteOnce"] storageClassName: truenas-iscsi resources: requests: storage: 10Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: bench-openebs namespace: default spec: accessModes: ["ReadWriteMany"] storageClassName: openebs-rwx-iscsi resources: requests: storage: 10Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: bench-ganesha namespace: default spec: accessModes: ["ReadWriteMany"] storageClassName: nfs-ganesha resources: requests: storage: 10Gi
8.3. 測定コマンド
キャッシュなし(direct=1)の測定コマンドです。
echo "=== [Cache OFF] Sequential Read (128k) ===" && \ fio --name=test --filename=/data/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) ===" && \ fio --name=test --filename=/data/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) ===" && \ fio --name=test --filename=/data/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) ===" && \ fio --name=test --filename=/data/fio --size=1G --direct=1 \ --bs=4k --rw=randwrite --numjobs=1 --runtime=30 --time_based \ --group_reporting 2>&1 | grep -E "WRITE:|iops|bw=" && \ rm -f /data/fio && echo "=== Cache OFF DONE ==="
キャッシュあり(direct=0)の測定コマンドです。
echo "=== [Cache ON] Sequential Read (128k) ===" && \ fio --name=test --filename=/data/fio --size=1G --direct=0 \ --bs=128k --rw=read --numjobs=1 --runtime=30 --time_based \ --group_reporting 2>&1 | grep -E "READ:|iops|bw=" && \ echo "=== [Cache ON] Sequential Write (128k) ===" && \ fio --name=test --filename=/data/fio --size=1G --direct=0 \ --bs=128k --rw=write --numjobs=1 --runtime=30 --time_based \ --group_reporting 2>&1 | grep -E "WRITE:|iops|bw=" && \ echo "=== [Cache ON] Random Read (4k) ===" && \ fio --name=test --filename=/data/fio --size=1G --direct=0 \ --bs=4k --rw=randread --numjobs=1 --runtime=30 --time_based \ --group_reporting 2>&1 | grep -E "READ:|iops|bw=" && \ echo "=== [Cache ON] Random Write (4k) ===" && \ fio --name=test --filename=/data/fio --size=1G --direct=0 \ --bs=4k --rw=randwrite --numjobs=1 --runtime=30 --time_based \ --group_reporting 2>&1 | grep -E "WRITE:|iops|bw=" && \ rm -f /data/fio && echo "=== Cache ON DONE ==="
8.4. 測定結果
8.4.1. キャッシュなし(direct=1)― ストレージ純粋性能
OS のページキャッシュをバイパスし、ストレージへの直接 I/O で測定した結果です。
| テスト | truenas-iscsi | openebs-rwx-iscsi | nfs-ganesha |
|---|---|---|---|
| Seq Read 128k | 332 MiB/s(2,655 IOPS) | 220 MiB/s(1,761 IOPS) | 212 MiB/s(1,694 IOPS) |
| Seq Write 128k | 159 MiB/s(1,276 IOPS) | 162 MiB/s(1,293 IOPS) | 42.6 MiB/s(341 IOPS) |
| Rand Read 4k | 19.4 MiB/s(4,963 IOPS) | 15.8 MiB/s(4,046 IOPS) | 15.6 MiB/s(3,993 IOPS) |
| Rand Write 4k | 17.6 MiB/s(4,513 IOPS) | 15.0 MiB/s(3,849 IOPS) | 2.9 MiB/s(743 IOPS) |
8.4.2. キャッシュあり(direct=0)― 実運用に近い性能
OS のページキャッシュを利用した測定結果です。
| テスト | truenas-iscsi | openebs-rwx-iscsi | nfs-ganesha |
|---|---|---|---|
| Seq Read 128k | 1,097 MiB/s(8,787 IOPS) | 346 MiB/s(2,768 IOPS) | 355 MiB/s(2,844 IOPS) |
| Seq Write 128k | 373 MiB/s(5,005 IOPS) | 371 MiB/s(3,057 IOPS) | 197 MiB/s(1,576 IOPS) |
| Rand Read 4k | 17.7 MiB/s(4,693 IOPS) | 14.5 MiB/s(3,699 IOPS) | 15.2 MiB/s(3,887 IOPS) |
| Rand Write 4k | 257 MiB/s(127,085 IOPS) | 170 MiB/s(60,885 IOPS) | 94.6 MiB/s(32,303 IOPS) |
8.5. 考察
8.5.1. 想定と実測の比較
I/O パスの段数と NFS 実装の違いに基づいて想定した順位と、実測結果を比較します。
| 方式 | 想定 | 実測結果 |
|---|---|---|
| democratic-csi | 最速(iSCSI 直接、2段パス) | 1位(ほぼ全テストでトップ) |
| OpenEBS NFS | 2位(カーネル NFS、3段パス) | 2位 |
| nfs-ganesha | 最遅(ユーザー空間 NFS、3段パス) | 3位(特に Write が顕著に遅い) |
I/O パスの段数どおりの順位となり、前編の nfs-csi のような「想定外の結果」は出ませんでした。これは今回の3方式がすべて TrueNAS の同一 iSCSI バックエンドを使っており、差異が NFS レイヤの有無と実装方式のみであるためです。Longhorn のレプリカ同期のような変動要因がなく、アーキテクチャの違いが素直に結果に反映されています。
8.5.2. NFS レイヤのオーバーヘッド
democratic-csi は iSCSI ブロックデバイスに直接アクセスするため、NFS プロトコルの解析やデータコピーが一切発生しません。キャッシュなしのシーケンシャル読み取りで比較すると、この差が明確です。
| 方式 | スループット | democratic-csi 比 |
|---|---|---|
| democratic-csi | 332 MiB/s | 1.00 倍 |
| OpenEBS NFS | 220 MiB/s | 0.66 倍 |
| nfs-ganesha | 212 MiB/s | 0.64 倍 |
読み取りでは OpenEBS NFS と nfs-ganesha の差は小さく(220 vs 212 MiB/s)、NFS プロトコル処理そのもののオーバーヘッドはカーネル実装でもユーザー空間実装でも読み取りに関しては大差がないことを示しています。
8.5.3. ユーザー空間 NFS の書き込みペナルティ
書き込み性能では nfs-ganesha が大きく劣ります。特にキャッシュなしの結果で顕著です。
| テスト | democratic-csi | OpenEBS NFS | nfs-ganesha | ganesha / democratic 比 |
|---|---|---|---|---|
| Seq Write | 159 MiB/s | 162 MiB/s | 42.6 MiB/s | 0.27 倍 |
| Rand Write | 17.6 MiB/s | 15.0 MiB/s | 2.9 MiB/s | 0.16 倍 |
nfs-ganesha のシーケンシャル書き込みは democratic-csi の約 1/4、ランダム書き込みは約 1/6 です。一方で OpenEBS NFS のシーケンシャル書き込みは democratic-csi とほぼ同等(162 vs 159 MiB/s)です。
この差はカーネル空間とユーザー空間の処理方式の違いに起因します。OpenEBS NFS が使用するカーネル NFS(knfsd)はシステムコール内でデータを処理するため、ユーザー空間とカーネル空間の間のコンテキストスイッチやデータコピーが発生しません。nfs-ganesha はユーザー空間プロセスとして動作するため、すべての I/O でこれらのオーバーヘッドが加わります。書き込みではデータの永続化保証(fsync)が必要なため、このコストがより顕著に現れています。
8.5.4. キャッシュの効果
キャッシュあり(direct=0)では3方式とも性能が向上しますが、恩恵の度合いは方式によって異なります。
| 方式 | Seq Read(direct=1 → 0) | Rand Write(direct=1 → 0) |
|---|---|---|
| democratic-csi | 332 → 1,097 MiB/s(3.3 倍) | 4,513 → 127,085 IOPS(28.2 倍) |
| OpenEBS NFS | 220 → 346 MiB/s(1.6 倍) | 3,849 → 60,885 IOPS(15.8 倍) |
| nfs-ganesha | 212 → 355 MiB/s(1.7 倍) | 743 → 32,303 IOPS(43.5 倍) |
democratic-csi はブロックデバイスであるため OS のページキャッシュとの親和性が最も高く、キャッシュあり時のシーケンシャル読み取りで 1,097 MiB/s を記録しています。NFS 方式ではクライアント側のキャッシュに加え NFS サーバー側の同期動作が介在するため、キャッシュの効果が限定的です。
ランダム書き込み(Cache ON)で3方式とも IOPS が桁違いに上昇しているのは、OS のページキャッシュが小さなランダム書き込みをメモリ上にバッファリングし、バックグラウンドでまとめてフラッシュするためです。特に democratic-csi の 127,085 IOPS、OpenEBS NFS の 60,885 IOPS は「ストレージの実力」というより「OS キャッシュの効果」を測っている点に留意が必要です。
8.5.5. 前編ベンチマーク(VM 方式)との関係
前編では VM 内から fio を実行し、後編(本記事)では Pod 内から実行しているため、絶対値の直接比較はできません。VM 方式では QEMU の仮想ディスクエミュレーション層が追加されるため、一般にスループットは低下します。
ただし、前編で確認された傾向 ― NFS/CSI 経由の外部ストレージが Longhorn のレプリカ同期を伴わないために高い転送速度を示すこと ― は、後編の結果とも整合しています。Longhorn のレプリカ同期をバイパスする方式は、一貫して書き込み性能の向上が確認されています。一方、このバイパスはデータの冗長性を外部ストレージ(TrueNAS の ZFS 構成)に委ねることを意味しており、性能と冗長性のトレードオフであることに変わりはありません。
8.5.6. ネステッド仮想化環境における留意事項
本検証は Proxmox VE 上のネステッド仮想化環境で実施しているため、すべての I/O は Proxmox ホストの仮想化レイヤを経由します。この追加レイテンシにより、ベアメタル環境と比較して絶対値は低下する傾向があります。しかし、3方式すべてが同一のネステッド仮想化環境で測定されているため、方式間の相対的な比較(順位・倍率)は有効な指標です。
また、テストファイル 1 GB に対して Pod が動作するノードのメモリは 16 GiB であり、cache-on(direct=0)時に OS ページキャッシュへのヒット率が高くなります。cache-off(direct=1)の結果をストレージの実力として参照し、cache-on は実運用時のキャッシュ効果を含む参考値として位置づけるのが適切です。
9. 総合比較と選定指針
9.1. 前編・後編 全6方式の横断比較
前編の3方式と後編の3方式を合わせた全6つの StorageClass について、主要な特性を横断的に比較します。
| 比較項目 | harvester-longhorn | longhorn-iscsi | nfs-csi | truenas-iscsi | openebs-rwx-iscsi | nfs-ganesha |
|---|---|---|---|---|---|---|
| バックエンド | ノードディスク | TrueNAS iSCSI LUN | TrueNAS NFS 共有 | TrueNAS iSCSI zvol | TrueNAS iSCSI zvol | TrueNAS iSCSI zvol |
| プロトコル | ローカル | iSCSI | NFS | iSCSI | iSCSI + NFS | iSCSI + NFS |
| 管理レイヤ | Longhorn | Longhorn | csi-driver-nfs | democratic-csi | OpenEBS + democratic-csi | nfs-ganesha + democratic-csi |
| アクセスモード | RWO/RWX | RWO/RWX | RWX | RWO | RWX | RWX |
| レプリカ数 | 3 | 3 | 1(TrueNAS 依存) | 1(TrueNAS 依存) | 1(TrueNAS 依存) | 1(TrueNAS 依存) |
| 書き込み同期 | 3ノード同期書き込み | 3ノード同期書き込み | 単一書き込み | 単一書き込み | 単一書き込み | 単一書き込み |
| イメージ管理 | Backing Image(Copy-on-Write: CoW) | Backing Image(Copy-on-Write: CoW) | PVC フルコピー | インポート不可 | インポート不可 | PVC(サブディレクトリ) |
| 10GB VM 1台の消費容量 | 約30GB | 約30GB | 約10GB+イメージ分 | —(VM非対応) | —(VM非対応) | 約10GB+イメージ分 |
| VM ライブマイグレーション | 可能 | 可能 | 可能 | 不可 | 可能 | 可能 |
| Single Point of Failure(SPOF) | なし(3レプリカ、ノード障害耐性あり) | TrueNAS | TrueNAS | TrueNAS | TrueNAS + NFS Pod | TrueNAS + NFS-Ganesha Pod |
| I/O パス段数 | 1段 | 2段 | 2段 | 2段 | 3段 | 3段 |
| Longhorn レプリカ同期 | あり | あり | なし | なし | なし | なし |
9.2. ユースケース別の選定指針
StorageClass の選定は、要件に応じて使い分けるのが現実的です。すべてのユースケースに対応する単一の最適解は存在しません。ここで言いたいのは Longhorn が不適切だということではなく、要件によっては Longhorn を経由しない構成の方が適する場面もある、という点です。
Harvester 標準の harvester-longhorn は、3レプリカによるノードレベル冗長性と Backing Image(CoW)による効率的なイメージ管理を備えており、外部ストレージを持たない環境や、データ保全を最優先する場合に適しています。ただし、レプリカ同期による書き込みオーバーヘッドがあります。
longhorn-iscsi は TrueNAS の大容量ストレージを Longhorn に統合する方式であり、ノードローカルディスクの容量を拡張する目的に適しています。Longhorn の冗長性と管理機能をそのまま利用できますが、iSCSI ネットワークホップとレプリカ同期の二重のオーバーヘッドにより、6方式の中で最も低い性能となる傾向があります。
nfs-csi は Longhorn を完全にバイパスし、TrueNAS の NFS 共有を直接マウントします。I/O パスが短く、OS ページキャッシュの恩恵も大きいため高い性能を示します。構成もシンプルで追加の Pod が不要ですが、冗長性は TrueNAS の ZFS 構成に依存します。
truenas-iscsi(democratic-csi)は Longhorn をバイパスした iSCSI 直接接続であり、NFS レイヤもないため後編の3方式の中で最も低レイテンシです。ただし RWO 制約があり、VM ライブマイグレーションには対応しません。イメージインポートにも非対応であるため、用途は限定的です。
openebs-rwx-iscsi は democratic-csi の性能をベースに RWX を追加する方式です。PVC ごとに NFS サーバー Pod が自動生成・削除されるため、ライフサイクル管理が自動化されています。障害影響が PVC 単位に限定される点は運用上の利点ですが、PVC 数の増加に伴うリソース消費に注意が必要です。
nfs-ganesha は、本検証条件の範囲ではリソース消費が一定で、イメージインポートにも成功した方式でした。ただし単一の NFS-Ganesha Pod がすべての PVC を処理するため、この Pod の停止は全 PVC に波及します。また書き込み性能がユーザー空間 NFS のオーバーヘッドにより大きく低下する点、バックエンド PVC の容量(50 GiB)が上限となる制約もあります。
9.3. 検証全体のまとめ
本検証シリーズでは、SUSE Harvester v1.7.1 上で利用可能なストレージ構成を6方式にわたって構築・比較しました。
前編では Harvester 標準の Longhorn を軸とした3方式を検証し、Longhorn のレプリカ同期が書き込み性能に与える影響と、NFS/CSI によるバイパスが性能を向上させることを確認しました。
後編では Longhorn を使用しない3方式を追加検証し、democratic-csi による直接 iSCSI 接続、OpenEBS によるカーネル NFS の自動生成、nfs-ganesha によるユーザー空間 NFS の常駐配置をそれぞれ構築しました。各方式の I/O パス構造、Single Point of Failure(SPOF)、リソース消費、PVC ライフサイクル、イメージインポートの可否を比較し、ユースケースに応じた選定指針を整理しています。特に OpenEBS の容量見積もりでつまずいた経験は、設計段階で「理論上の容量」と「実効容量」を分けて考える重要性を再認識するきっかけになりました。
6方式の中に「万能な正解」はありません。性能・冗長性・運用性・対応機能のバランスを考慮し、ワークロードの要件に合わせて組み合わせることが現実的な設計判断となります。
9.4. バックアップに関する制約と代替手段
Harvester には標準の VM バックアップ機能が搭載されていますが、この機能は Longhorn ボリュームのみを対象としています。具体的には、harvester-longhorn および longhorn-iscsi の StorageClass に格納された VM データはバックアップ・リストアが可能ですが、democratic-csi、OpenEBS、nfs-ganesha など Longhorn を経由しない StorageClass に格納されたデータは Harvester 標準のバックアップ機能では保護できません。
この制約を踏まえ、Longhorn 以外の StorageClass を使用する場合は、以下の代替手段を検討する必要があります。
1つ目の代替手段は、ゲストエージェント方式によるバックアップです。VM 内にバックアップエージェントをインストールし、ゲスト OS のファイルシステムレベルでデータを保護します。この方式はストレージの StorageClass に依存しないため、本記事で検証したすべての方式で利用可能です。ただし、VM ごとにエージェントのインストールと設定が必要であり、Kubernetes リソース(PVC 定義や VM メタデータ等)自体のバックアップは範囲外となります。
2つ目の代替手段は、Kubernetes ネイティブのバックアップ製品を利用する方法です。たとえば Veeam Kasten(旧 Kasten K10)は、Kubernetes のリソース(Pod、PVC、ConfigMap、Secret 等)と PVC データを一括してバックアップ・リストアするデータ管理プラットフォームであり、公式ドキュメントでも SUSE Virtualization(Harvester)上の VM バックアップとリストアに関する情報が公開されています。一方で、Kubernetes や KubeVirt 向けのバックアップ製品は他にも存在しますが、Harvester に対する公式サポート範囲や制約は製品ごとに異なります。採用にあたっては、各ベンダーの最新ドキュメントとサポートポリシーを確認する必要があります。
実運用においては、これらの手段を併用する構成が有効です。ゲストエージェント方式で VM 内部のアプリケーションデータやデータベースを保護し、Veeam Kasten で Kubernetes リソース(VM 定義、PVC、ネットワーク設定等)を包括的に保護することで、ストレージ障害・VM 障害・クラスタ障害のそれぞれに対応できる多層的なバックアップ体制を構築できます。
10. 参考情報
10.1. 公式ドキュメント
- Proxmox VE 8.3 リリース情報: https://pve.proxmox.com/mediawiki/index.php?title=Downloads#Proxmox_Virtual_Environment_8.3_.28ISO_Image.29
- Proxmox VE Storage: LVM: https://pve.proxmox.com/pve-docs/pve-storage-lvm-plain.html
- Harvester 公式ドキュメント: https://docs.harvesterhci.io/v1.7.1
- Longhorn 公式ドキュメント: https://longhorn.io/docs/
- TrueNAS SCALE ドキュメント: https://www.truenas.com/docs/scale/
- Kubernetes CSI 仕様: https://kubernetes-csi.github.io/docs/
- Harvester VM Backup, Snapshot & Restore: https://docs.harvesterhci.io/v1.7/vm/backup-restore/
- Veeam Kasten: Protecting SUSE Virtualization (Harvester) VMs and Images: https://docs.kasten.io/latest/usage/harvester_protection/
- Veeam Kasten: SUSE Virtualization (Harvester) VM Backup and Restore Support Limitations: https://docs.kasten.io/latest/usage/harvester_limitations/
10.2. GitHub リポジトリ
- democratic-csi: https://github.com/democratic-csi/democratic-csi
- OpenEBS Dynamic NFS Provisioner: https://github.com/openebs/dynamic-nfs-provisioner
- nfs-ganesha-server-and-external-provisioner: https://github.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner
- NFS-Ganesha 公式: https://nfs-ganesha.github.io/
11. 商標について
本記事で使用している以下の名称は、各社の商標または登録商標です。
- 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 の商標または登録商標です。
- Veeam、Veeam Kasten は、Veeam Software Group GmbH の商標または登録商標です。
- OpenEBS、OpenEBS Dynamic NFS Provisioner は、OpenEBS プロジェクトの名称です。
その他、本記事に記載されている会社名、製品名は、各社の商標または登録商標です。
12. 免責事項
本記事の内容は、2026年4月時点の情報に基づいており、将来的に変更される可能性があります。本記事の内容を実施したことにより発生した損害について、筆者およびNTT西日本は一切の責任を負いかねます。本記事の内容を参考に作業を行う場合は、自己責任でお願いいたします。
本記事に記載されているコマンド、設定値、構成例は、特定の検証環境で動作を確認したものであり、すべての環境で同一の結果が得られることを保証するものではありません。本番環境への適用にあたっては、十分な検証とバックアップを実施してください。
12.1. データ損失に関する注意事項
- 本記事で紹介する StorageClass のうち、Longhorn をバイパスする方式(democratic-csi、OpenEBS、nfs-ganesha、nfs-csi)はノードレベルのレプリケーションを行いません。データの冗長性は外部ストレージ(TrueNAS)側の構成に依存します。
reclaimPolicy: Deleteを設定した StorageClass では、PVC の削除に伴いデータが自動的に削除されます。意図しないデータ損失を防ぐため、重要なデータにはRetainポリシーの使用や定期的なバックアップを検討してください。- nfs-ganesha のバックエンド PVC を削除または NFS-Ganesha を
helm uninstallすると、そのバックエンド上のすべての RWX PVC のデータが失われます。 - ネステッド仮想化環境では、Proxmox ホストの障害がすべてのゲスト VM に波及します。本検証環境はシングルホスト構成であり、本番利用には適しません。
13. 執筆者
平岡 征一朗(NTT西日本) 文教(大学)担当のシステムエンジニアです。インフラからアプリまでトラブルシュートが大好きです。