OKD Virtualization で仮想マシンを作り、ライブ マイグレーション(ディスクが NFS の場合と iSCSI の LUN の場合)、iSCSI の 1 経路の障害、ノードの電源断(HA)を確認した手順と結果です。
この記事の前提
- OKD Virtualization とストレージ・ネットワークの設定が終わっていること
- 参照: KubeVirt user guide(Live Migration、DataVolumes)
コンソールでの仮想マシンの作成
(1) [Virtualization] → [VirtualMachines] で [Create] → [From InstanceType] などから作成ウィザードを開き、[Custom configuration (default)] を選んで [Next] をクリックします。作成先のプロジェクトは default です。

(2) [Guest OS] で [Other Linux] を選び、[Guest operating system type] で centos.stream9 を選びます。

(3) [Boot source] で、自動で取り込まれた起動用ボリューム(centos-stream9、nfs-csi)を選びます。仮想マシンのディスクは、このボリュームを複製して作られます。

(4) [Compute resources] で、インスタンス タイプ(CPU とメモリーの組み合わせ)を選びます。ここでは General Purpose(U シリーズ)の small(1 CPU・2 GiB)にしました。

| シリーズ | 用途 |
|---|---|
| U(General Purpose) | 汎用 |
| CX(Compute Exclusive) | CPU を専有する(性能重視) |
| M(Memory Intensive) | メモリーの多い構成 |
| O(Overcommitted) | メモリーを多めに割り当てる(集約率重視) |
(5) [Customization] では、ディスク(Storage)、NIC(Network)、配置(Scheduling)、SSH、初回の設定(Initial run)を変えられます。

(6) [Initial run] の cloud-init では、ログインするユーザー(centos)とパスワード(自動で作られる)が設定されます。

(7) [Review and create] で名前と説明を入力し、[Create VirtualMachine] をクリックします。

(8) ディスクの複製(Provisioning)の後、Running になります。

コンソールのウィザードで作った仮想マシンは、既定の Pod ネットワーク(masquerade)につながります。この NIC の IP アドレスはクラスタ内部のもので、ライブ マイグレーションのたびに変わります(この検証では 10.130.0.89 → 10.128.0.22 → 10.130.0.90)。LAN から使う仮想マシンは、次の YAML のように VM 用ネットワーク(vm-lan)につなぎます。
LAN につないだ仮想マシン(YAML)
試験用に、ディスクが NFS の仮想マシン(vm-lm01、192.168.1.196)と、iSCSI の LUN の仮想マシン(vm-iscsi01、192.168.1.197)を YAML で作ります。違いはストレージ クラスとボリューム モードだけです。
実行する場所:踏み台サーバー(root)
# cat > vm-lm01.yaml <<'EOF'
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-lm01
namespace: default
spec:
runStrategy: Always
instancetype:
name: u1.small
preference:
name: centos.stream9
dataVolumeTemplates:
- metadata:
name: vm-lm01-disk
spec:
source:
pvc:
namespace: kubevirt-os-images
name: centos-stream9-27899ea8a37b
storage:
storageClassName: nfs-csi # iSCSI のときは iscsi-static
volumeMode: Filesystem # iSCSI のときは Block
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
template:
spec:
domain:
devices:
interfaces:
- name: lan
bridge: {}
macAddress: 02:00:00:0d:01:96
networks:
- name: lan
multus:
networkName: vm-lan
volumes:
- name: rootdisk
dataVolume:
name: vm-lm01-disk
- name: cloudinitdisk
cloudInitNoCloud:
userData: |
#cloud-config
user: centos
password: P@ssw0rd-okd
chpasswd: { expire: False }
ssh_pwauth: True
networkData: |
version: 2
ethernets:
lan:
match:
macaddress: "02:00:00:0d:01:96"
set-name: eth0
addresses: [192.168.1.196/24]
gateway4: 192.168.1.1
nameservers:
addresses: [192.168.1.254]
EOF
# oc apply -f vm-lm01.yaml
# oc -n default get vmi -o wide
期待する出力:
NAME AGE PHASE IP NODENAME READY LIVE-MIGRATABLE
vm-iscsi01 106s Running 192.168.1.197 master-3 True True
vm-lm01 39m Running 192.168.1.196 master-1 True True
| 項目 | 説明 |
|---|---|
| runStrategy: Always | 常に動かす(止まったら起動し直す)。HA に必要 |
| instancetype / preference | CPU・メモリーの組み合わせと、OS に合わせた既定値(ディスクのバスなど) |
| dataVolumeTemplates | 仮想マシンと一緒に作るディスク。source の PVC(起動用イメージ)を複製する |
| accessModes: ReadWriteMany | ライブ マイグレーションに必要。LIVE-MIGRATABLE が True になる |
| interfaces.bridge + networks.multus | VM 用ネットワーク vm-lan(ノードの br-vm)にブリッジでつなぐ |
| networkData | cloud-init の固定 IP。MAC アドレスで NIC を指定する |
コンソールの [VirtualMachines] → [Virtual machines] タブに、LAN の IP アドレスが表示されます。

ライブ マイグレーション
コンソールから
(1) 仮想マシンの [Actions] → [Migration] → [Compute] をクリックします([Storage] は、ディスクを別のストレージ クラスへ移す操作です)。

(2) [Specific Node] を選んで移動先のノードを選び、[Migrate VirtualMachine] をクリックします([Automatically selected Node] ならクラスタが選びます)。

(3) 移動が終わると、仮想マシンは移動先のノードで Running のままです。

CLI から
VirtualMachineInstanceMigration を作ると、同じ移動が始まります(virtctl migrate と同じ)。
# cat <<'EOF' | oc create -f -
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstanceMigration
metadata:
generateName: vm-lm01-mig-
namespace: default
spec:
vmiName: vm-lm01
EOF
# oc -n default get vmi vm-lm01 -o jsonpath='{.status.migrationState.sourceNode} -> {.status.migrationState.targetNode} completed={.status.migrationState.completed}{"\n"}'
期待する出力:
master-1 -> master-3 completed=true
結果
踏み台から仮想マシンの LAN の IP アドレスへ 0.2 秒間隔で ping を送りながら、3 回ずつ移動しました。
| ディスク | 移動(migrationState の開始〜終了) | ping の最長の途切れ |
|---|---|---|
| NFS(Filesystem・RWX) | 2 秒・2 秒・3 秒 | 1.2 秒・0.4 秒・0.6 秒 |
| iSCSI の LUN(Block・RWX、マルチパス) | 3 秒・3 秒・2 秒 | 0.8 秒・3.1 秒・0.6 秒 |
iSCSI の LUN の場合、移動中は移動元と移動先の両方のノードが同じ LUN にログインし、QEMU が書き込む側を切り替えます。移動が終わると、移動元のノードの iSCSI セッションは閉じられ、移動先のノードだけが 2 経路でつながっています。
実行する場所:踏み台サーバー(仮想マシンが動くノード)
# oc debug node/master-1 -q -- chroot /host sh -c 'iscsiadm -m session; multipath -ll'
期待する出力:
tcp: [3] 10.40.81.1:3260,1 iqn.2026-10.lab.hvdoc:okd (non-flash)
tcp: [4] 10.40.82.1:3260,1 iqn.2026-10.lab.hvdoc:okd (non-flash)
mpatha (360014055254428a3dce45d4a6f0fccfc) dm-0 LIO-ORG,vm01
size=20G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=50 status=active
|- 8:0:0:0 sdc 8:32 active ready running
`- 9:0:0:0 sdd 8:48 active ready running
iSCSI の 1 経路の障害
vm-iscsi01 が動いているノード(master-1)の iSCSI 1 系統目(ens19、10.40.81.x)のリンクを、仮想化基盤の側で切りました。仮想マシンの中では 1.5 GB の書き込み(dd、oflag=direct)を続けました。
マルチパスの状態(リンクを切った後):
`-+- policy='service-time 0' prio=50 status=active
|- 8:0:0:0 sdc 8:32 failed faulty offline
`- 9:0:0:0 sdd 8:48 active ready running
| 項目 | 結果 |
|---|---|
| マルチパス | 1 系統目(sdc)が failed、2 系統目(sdd)で継続 |
| 書き込み | 止まらずに完了(1.5 GB、31 秒、50.3 MB/s) |
| 仮想マシンへの ping | 欠落なし |
| リンクを戻した後 | sdc が自動で active に戻った |
HA(ノードの電源断)
vm-lm01 が動いているノード(master-3)の電源を強制的に切りました。
(1) [Compute] → [Nodes] で、master-3 が Not Ready になります。

(2) Node Health Check が異常を見つけ、Self Node Remediation がノードの復旧(SelfNodeRemediation)を始めます。止まったノードの仮想マシンは、安全に再起動したとみなせるまで待った後、別のノードで起動し直します。
# oc get snr -A
# oc get nodehealthcheck nhc-control-plane -o jsonpath='{.status.phase} {.status.healthyNodes}/{.status.observedNodes}{"\n"}'
期待する出力(復旧中):
NAMESPACE NAME AGE
openshift-workload-availability master-3-7jvbd 3m8s
Remediating 2/3
| 経過(電源断から) | 状態 |
|---|---|
| 78 秒 | ノードが NotReady |
| 152 秒 | SelfNodeRemediation が作られた(NotReady が 60 秒続いた後) |
| 289 秒 | 仮想マシンが別のノードへ(Scheduling) |
| 295 秒 | master-2 で Running |
| 331 秒 | ゲストが起動し ping が通った(途切れは 330 秒) |
Node Health Check と Self Node Remediation を入れていないと、止まったノードの仮想マシンは「Running(止まったノード)」のまま、別のノードでは起動しません。止まったノードで実は動き続けている場合に、同じディスクを 2 か所で使って壊すのを防ぐためです。
(3) 止めたノードの電源を入れると、ノードが Ready に戻り、SelfNodeRemediation が消えて、NodeHealthCheck が Enabled 3/3 に戻ります。
この検証では、電源を入れたノードが NotReady のまま戻りませんでした。 強制的な電源断でコンテナ ランタイム(CRI-O)が「正常に終了しなかった」と判断して既存のコンテナを片付け、その後の状態確認で CNI の multus-shim を呼んだまま応答しなくなりました。multus-shim は multus の Pod(デーモン)を待ちますが、その Pod は CRI-O が動かないと起動できないため、互いに待ち合う状態です。CRI-O は systemd のウォッチドッグ(1 分)で強制終了と起動を繰り返していました(
journalctl -u crioに「Watchdog timeout」「Killing process … (multus-shim)」)。次の手順で戻りました。multus の Pod が起動すると、00-multus.conf は自動で書き直されます。
実行する場所:止めたノード(
ssh core@<ノード>)
sudo systemctl stop kubelet crio
sudo mkdir -p /root/cni-bak && sudo mv /etc/kubernetes/cni/net.d/00-multus.conf /root/cni-bak/
sudo systemctl start crio && sudo systemctl start kubelet約 1 分でノードが Ready に戻りました。
結果のまとめ
| 試験 | NFS | iSCSI(マルチパス) |
|---|---|---|
| ライブ マイグレーション | 2〜3 秒、途切れ 0.4〜1.2 秒 | 2〜3 秒、途切れ 0.6〜3.1 秒 |
| 経路の障害 | (NFS は 1 経路のため試験なし) | 書き込み継続、ping の欠落なし |
| ノードの電源断(HA) | 295 秒で別ノードで起動、331 秒で ping 応答 | (同じ仕組み) |
