Harvester は既定で HCI ストレージ(Longhorn)を使いますが、v1.5 から外部ストレージの CSI ドライバーで仮想マシンのディスク(ブート ディスクを含む)を作れます。ここでは NFS サーバーと csi-driver-nfs を使った 3Tier 構成を作り、ライブ マイグレーション、NIC 障害、ノードの電源断(HA)を確認しました。
この記事の前提
- インストールと初期設定とネットワークの設定が終わっていること
- 参照: Third-Party Storage Support
構成と冗長化の考え方
| 項目 | 内容 |
|---|---|
| ストレージ | NFS サーバー(192.168.1.182、Rocky Linux 9)の /exports/harv |
| CSI ドライバー | csi-driver-nfs v4.11.0(プロビジョナー nfs.csi.k8s.io) |
| 経路 | 各ノードの管理ボンド mgmt-bo(ens18・ens19、active-backup)を通る |
| アクセス モード | ReadWriteMany(ライブ マイグレーションに必要) |
NFS は 1 本のセッションで使うため、経路の冗長化はノード側のボンドで行いました。iSCSI や FC のストレージでマルチパスを使うときは、ストレージ ベンダーの CSI ドライバーが対応しているかを確認してください。なお、この検証の Harvester 1.8.2 のノードには multipathd のサービスがありませんでした(systemctl is-enabled multipathd が「No such device or address」)。
NFS サーバー自体の冗長化(コントローラー 2 台の NAS など)は、ストレージ側で行います。この検証の NFS サーバーは 1 台です。
NFS の公開
実行する場所:NFS サーバー(root)
# mkdir -p /exports/harv && chmod 777 /exports/harv
# echo "/exports/harv 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check)" >> /etc/exports
# exportfs -ra
# exportfs -v | grep harv
期待する出力:
/exports/harv 192.168.1.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
CSI ドライバーの導入
Harvester(RKE2)に組み込まれている Helm コントローラーで csi-driver-nfs を入れます。スナップショットのコントローラーは Harvester に入っているため、チャートの externalSnapshotter は無効にします。
実行する場所:いずれかのノード(rancher ユーザー。kubectl は sudo /var/lib/rancher/rke2/bin/kubectl --kubeconfig /etc/rancher/rke2/rke2.yaml)
$ cat > csi-nfs-helmchart.yaml <<'EOF'
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: csi-driver-nfs
namespace: kube-system
spec:
repo: https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
chart: csi-driver-nfs
version: v4.11.0
targetNamespace: kube-system
valuesContent: |-
externalSnapshotter:
enabled: false
controller:
replicas: 1
EOF
$ kubectl apply -f csi-nfs-helmchart.yaml
$ kubectl get csidriver
期待する出力(1〜2 分後):
NAME ATTACHREQUIRED PODINFOONMOUNT ...
driver.longhorn.io true true ...
nfs.csi.k8s.io false false ...
続けて、NFS 用の VolumeSnapshotClass を作ります。Harvester はイメージから仮想マシンのディスクを作るときなどにスナップショットを使います。
$ cat > vsc-nfs.yaml <<'EOF'
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-nfs-snapclass
driver: nfs.csi.k8s.io
deletionPolicy: Delete
EOF
$ kubectl apply -f vsc-nfs.yaml
期待する出力:
volumesnapshotclass.snapshot.storage.k8s.io/csi-nfs-snapclass created
CSI ドライバーの登録(csi-driver-config)
(1) [Advanced] → [Settings] で csi-driver-config を編集し、[Add] をクリックして Provisioner に nfs.csi.k8s.io、Volume Snapshot Class Name に csi-nfs-snapclass を選んで [Save] をクリックします。ここに登録したドライバーが、ストレージ クラスの作成画面で選べるようになります。

ストレージ クラスの作成
(1) [Advanced] → [Storage Classes] で [Create] をクリックし、名前を入力して Provisioner に nfs.csi.k8s.io を入力・選択します。パラメーターはフォームに Longhorn 用の項目が残るため、[Edit as YAML] で全体を書き換えて [Create] をクリックしました。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-csi
annotations:
field.cattle.io/description: 3Tier-NFS-doc-nfs
cdi.harvesterhci.io/storageProfileVolumeSnapshotClass: csi-nfs-snapclass
cdi.harvesterhci.io/storageProfileVolumeModeAccessModes: '{"Filesystem":["ReadWriteMany"]}'
provisioner: nfs.csi.k8s.io
parameters:
server: 192.168.1.182
share: /exports/harv
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
- nfsvers=4.1
- hard
| 項目 | 説明 |
|---|---|
| parameters.server / share | NFS サーバーと公開しているパス |
| cdi.harvesterhci.io/storageProfileVolumeSnapshotClass | イメージ(CDI)からディスクを作るときに使う VolumeSnapshotClass |
| cdi.harvesterhci.io/storageProfileVolumeModeAccessModes | ディスクのボリューム モードとアクセス モード。ライブ マイグレーションには ReadWriteMany が必要 |
| mountOptions | NFS のマウント オプション。hard は NFS サーバーが応答しない間、I/O を待たせる(エラーにしない) |
(2) nfs-csi が一覧に表示されます。

NFS 上のイメージと仮想マシン
(1) [Images] で [Create] をクリックし、[Basics] で URL を入力、[Storage] の Storage Class で nfs-csi を選んで [Create] をクリックします。Longhorn 以外のストレージでは、イメージは CDI(Containerized Data Importer)で取り込まれます。

(2) Active になれば使えます(この環境では約 4 分)。

(3) [Virtual Machines] で仮想マシンを作ります。手順は HCI の記事と同じで、[Volumes] の [Image] で nfs-csi のイメージを選びます。

(4) Network Data では IP アドレスを 192.168.1.176 にしました。

(5) ディスクはイメージのスナップショットから作られます。csi-driver-nfs のスナップショットは tar.gz のため、展開に時間がかかり、この環境では作成から Running まで約 4 分でした。

NFS サーバーには、ボリュームごとのディレクトリーができます(イメージ用と仮想マシンのディスク用。作成中だけ snapshot-… のディレクトリーもできます)。
実行する場所:NFS サーバー
# ls /exports/harv/
期待する出力(例):
pvc-6164bfa7-b145-4290-a2cc-82d48cce7f7c
pvc-6db398f6-bffe-4af5-a2cf-b4c98b2299af
ライブ マイグレーション
(1) 仮想マシンの︙から [Migrate] をクリックし、移動先のノードを選んで [Apply] をクリックします。

(2) 移動先のノードで Running になります。

| 項目 | 結果(3 回) |
|---|---|
| 転送(migrationState の開始〜終了) | 8 秒・7 秒・7 秒 |
| ping の最長の途切れ(0.2 秒間隔) | 8.9 秒・0.6 秒・0.6 秒 |
ディスクは共有の NFS にあるため、転送するのはメモリーだけです。1 回目の 8.9 秒の途切れは HCI でも 1 回起きており、ネットワーク側の MAC アドレスの学び直しの待ちと考えられます。
NIC 障害(NFS の経路の片側断)
仮想マシンが動いている doc-harv3 の管理ボンドの稼働側(ens18)のリンクを、仮想マシンで 2 GB を書き込んでいる最中に切りました。NFS の通信は管理ボンドを通っています。
| 項目 | 結果 |
|---|---|
| ボンド | ens19 に切り替わった |
| 書き込み | 止まらずに完了(97.1 MB/s) |
| ノードの管理 IP(.174)への ping | 1 回の欠落 |
| 仮想マシンへの ping | 1 回(0.4 秒)の欠落 |
HA(ノードの電源断)
仮想マシンが動いている doc-harv3 の電源を強制的に切りました。
(1) HCI のときと同じく、退避のマイグレーションのタイムアウトの後、別のノード(doc-harv2)で起動し直しました。

| 経過(電源断から) | 状態 |
|---|---|
| 56 秒 | ノードが NotReady |
| 267 秒 | 仮想マシンが Failed |
| 278 秒 | doc-harv2 にスケジュール |
| 284 秒 | doc-harv2 で Running |
| 320 秒 | ゲストが起動し ping が通った(途切れは 320 秒) |
NFS は複数のノードから同時にマウントできるため、ディスクの付け替え(アタッチ)の待ちはありません。時間の大半は vm-force-reset-policy の待ち(既定 15 秒+180 秒)です。
結果のまとめ
| 試験 | 3Tier(NFS) | HCI(Longhorn)※ |
|---|---|---|
| ライブ マイグレーション(転送) | 7〜8 秒 | 6〜8 秒 |
| 経路の NIC 障害 | 書き込み継続、ping の欠落 1 回 | 書き込み継続、欠落なし(ストレージ ボンド) |
| ノードの電源断(HA) | 320 秒で再起動 | 338 秒で再起動 |
※ HCI の値は前の記事です。