【OKD 5.0】 OKD Virtualization の導入とストレージ(NFS・iSCSI マルチパス)・VM 用ネットワークの設定

OKD のクラスタに仮想マシンの機能(OKD Virtualization=KubeVirt)を入れ、仮想マシンのディスク用のストレージ(NFS の CSI ドライバー、iSCSI のマルチパス)と、仮想マシンを LAN に直接つなぐネットワーク(Linux ブリッジ)を設定する手順です。あわせて、ノード障害のときに仮想マシンを別のノードで起動し直す仕組み(Node Health Check・Self Node Remediation)を入れます。

目次

この記事の前提

全体の構成

用途方式仮想マシンのディスクとしての特徴
NFS(踏み台)csi-driver-nfs(動的に作る)Filesystem・RWX。イメージの複製やディスクの追加が自動
iSCSI(踏み台、2 系統)Kubernetes 標準の iSCSI ボリューム(静的な PV)Block・RWX。ノード側でマルチパス(2 経路)。LUN は事前に作る
VM 用ネットワークノードの Linux ブリッジ br-vm(NIC ens21)+bridge CNI仮想マシンが LAN の IP アドレスを持ち、移動しても変わらない
HANode Health Check+Self Node Remediation止まったノードを再起動扱いにして、仮想マシンを別のノードで起動し直す

OKD Virtualization の導入

(1) [Ecosystem] → [Software Catalog] で「kubevirt」を検索し、[KubeVirt HyperConverged Cluster Operator](Community)をクリックします。

カタログ

(2) [Install] をクリックします。

オペレーター

(3) Update channel(stable)、Version、Installation mode(All namespaces)、Installed Namespace(kubevirt-hyperconverged、自動で作られる)を確認して [Install] をクリックします。

インストールの設定
項目説明設定値
Update channel更新の系列stable
Version入れる版1.18.1
Installation modeオペレーターが見る範囲All namespaces(既定)
Installed Namespaceオペレーターを置くプロジェクトkubevirt-hyperconverged(推奨)
Update approval新しい版を自動で入れるかAutomatic

(4) インストールが終わると、[Create HyperConverged] が押せるようになります。クリックします。

HyperConverged の作成

(5) 名前(kubevirt-hyperconverged)のまま、既定値で [Create] をクリックします。数分で、仮想マシンを動かすための部品(virt-handler など)が全ノードに配られます。

HyperConverged

(6) メニューに [Virtualization] と [Migration] が追加されます。[Bootable volumes] には、CentOS Stream 9/10・Fedora の起動用イメージが自動で取り込まれます(既定のストレージ クラスに置かれるため、先に NFS を既定にしておくと、そこに置かれます)。

起動用イメージ

NFS の CSI ドライバー

# curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/v4.11.0/deploy/install-driver.sh | bash -s v4.11.0 --
# oc adm policy add-scc-to-user privileged -z csi-nfs-controller-sa -n kube-system
# oc adm policy add-scc-to-user privileged -z csi-nfs-node-sa -n kube-system
# oc -n kube-system rollout restart deployment/csi-nfs-controller daemonset/csi-nfs-node
# cat > sc-nfs.yaml <<'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.1.190
  share: /srv/nfs/okd
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
  - nfsvers=4.1
  - hard
EOF
# oc apply -f sc-nfs.yaml

OKD では、Pod の権限を SCC(Security Context Constraints)で絞っています。CSI ドライバーはノードの /var/lib/kubelet に NFS をマウントするため、コントローラーとノードのサービス アカウントに privileged の SCC を付けないと Pod が作られません。

確認

RWX の PVC を作り、Bound になって、踏み台の /srv/nfs/okd にディレクトリーができることを確認します。

# oc -n default get pvc nfs-test
# ls /srv/nfs/okd/

期待する出力:

NAME       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS
nfs-test   Bound    pvc-1552122e-99a8-4d56-99a5-3018b2670be4   1Gi        RWX            nfs-csi
pvc-1552122e-99a8-4d56-99a5-3018b2670be4

iSCSI のマルチパス

ノードの設定(MachineConfig)

OKD のノード(CentOS Stream CoreOS)は、iscsid が無効で、multipath.conf がありません。MachineConfig で有効にします。適用するとノードが 1 台ずつ再起動します。

# MP=$(printf 'defaults {\n    user_friendly_names yes\n    find_multipaths yes\n    skip_kpartx yes\n}\n' | base64 -w0)
# cat > 99-master-iscsi-multipath.yaml <<EOF
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  name: 99-master-iscsi-multipath
  labels:
    machineconfiguration.openshift.io/role: master
spec:
  config:
    ignition:
      version: 3.4.0
    storage:
      files:
      - path: /etc/multipath.conf
        mode: 0644
        overwrite: true
        contents:
          source: data:text/plain;charset=utf-8;base64,$MP
    systemd:
      units:
      - name: iscsid.service
        enabled: true
      - name: multipathd.service
        enabled: true
EOF
# oc apply -f 99-master-iscsi-multipath.yaml
# oc get mcp master
設定説明
user_friendly_namesマルチパスのデバイスを mpatha などの名前にする
find_multipaths2 本以上の経路があるディスクだけをマルチパスにする
skip_kpartxLUN の中のパーティションをノードに割り当てない。仮想マシンのディスクとして使う LUN には必須(下の注意を参照)
role: master3 台構成で、コントロール プレーンのノードが仮想マシンも動かすため master に適用する

oc get mcp master の UPDATED が True、UPDATEDMACHINECOUNT が 3 になれば完了です(この環境では約 25 分)。

LUN と ACL(踏み台)

各ノードのイニシエーター名を ACL に登録し、LUN(20 GiB)を作ります。

# for n in master-1 master-2 master-3; do oc debug node/$n -q -- chroot /host cat /etc/iscsi/initiatorname.iscsi | grep -o 'iqn.*'; done
# targetcli /iscsi/iqn.2026-10.lab.hvdoc:okd/tpg1/acls create iqn.1994-05.com.redhat:9465dc72e18
# targetcli /iscsi/iqn.2026-10.lab.hvdoc:okd/tpg1/acls create iqn.1994-05.com.redhat:fce82bc042f5
# targetcli /iscsi/iqn.2026-10.lab.hvdoc:okd/tpg1/acls create iqn.1994-05.com.redhat:afcf50ed87dd
# targetcli /backstores/fileio create name=vm01 file_or_dev=/srv/iscsi/vm01.img size=20G sparse=true write_back=false
# targetcli /iscsi/iqn.2026-10.lab.hvdoc:okd/tpg1/luns create /backstores/fileio/vm01
# targetcli saveconfig
# echo 'vm.min_free_kbytes = 262144' > /etc/sysctl.d/90-iscsi-target.conf
# sysctl -p /etc/sysctl.d/90-iscsi-target.conf

踏み台(ターゲット)のメモリーが 8 GB だと、大きな書き込みでページ キャッシュが空きを使い切り、iSCSI の受信処理が連続した領域を確保できずに失敗しました(dmesg に「iscsi_trx: page allocation failure: order:4」、ノード側に「Logical unit communication failure」の I/O エラー)。vm.min_free_kbytes で空きを確保すると出なくなりました。

静的な PV(Block・RWX)

LUN を、2 つのポータル(2 系統)を持つ PV にします。ブロック モード(volumeMode: Block)で RWX にすると、ライブ マイグレーション中に移動元と移動先の両方のノードが同じ LUN を開けます。

# cat > pv-iscsi-vm01.yaml <<'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: iscsi-static
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: Immediate
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: iscsi-vm01
spec:
  capacity:
    storage: 20Gi
  volumeMode: Block
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: iscsi-static
  iscsi:
    targetPortal: 10.40.81.1:3260
    portals:
    - 10.40.82.1:3260
    iqn: iqn.2026-10.lab.hvdoc:okd
    lun: 0
    readOnly: false
EOF
# oc apply -f pv-iscsi-vm01.yaml
# oc get storageprofile

期待する出力(storageprofile):

NAME           CLAIM                                                      CLONE
iscsi-static   [map[accessModes:[ReadWriteMany] volumeMode:Block]]        copy
nfs-csi        [map[accessModes:[ReadWriteMany] volumeMode:Filesystem]]   copy

iSCSI の LUN で仮想マシンのディスクを作るときの注意(この検証で起きたこと)

– イメージを LUN に複製する作業 Pod(cdi-upload)が、既定のメモリー上限(600 MB)を超えて OOMKilled になりました。HyperConverged の spec.resourceRequirements.storageWorkloads で上限を 2 GiB にしました。この項目は API の版 v1beta1 にあるため、oc patch hyperconvergeds.v1beta1.hco.kubevirt.io kubevirt-hyperconverged --type=merge -p '{"spec":{"resourceRequirements":{"storageWorkloads":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"100m","memory":"256Mi"}}}}}' のように版を指定します(版を省くと「unknown field」で無視されます)。

– 途中まで書かれた LUN の中のパーティションを、ノードのマルチパスが自動で割り当て(mpatha1〜4)、作業 Pod がデバイスを開けなくなりました(「device or resource busy」)。multipath.conf の skip_kpartx yes で防げます。

– 静的な PV は 1 つの LUN を 1 つのディスクにしか使えません。仮想マシンを増やすたびに LUN と PV を作るか、ストレージ ベンダーの CSI ドライバー(動的に LUN を作れるもの)を使います。

VM 用ネットワーク(LAN への直結)

既定の Pod ネットワーク(masquerade)につないだ仮想マシンは、移動するたびに IP アドレスが変わり、外から直接は届きません。仮想マシンを LAN(192.168.1.0/24)に直接つなぐため、各ノードに VM 用の NIC(ens21)を足し、Linux ブリッジ br-vm を作ります。NMState オペレーターがカタログに無かったため、MachineConfig で NetworkManager の設定ファイルを置きます。

# BR=$(printf '[connection]\nid=br-vm\ntype=bridge\ninterface-name=br-vm\nautoconnect=true\n\n[bridge]\nstp=false\n\n[ipv4]\nmethod=disabled\n\n[ipv6]\nmethod=disabled\n' | base64 -w0)
# PORT=$(printf '[connection]\nid=br-vm-port-ens21\ntype=ethernet\ninterface-name=ens21\ncontroller=br-vm\nport-type=bridge\nautoconnect=true\n' | base64 -w0)
# cat > 99-master-br-vm.yaml <<EOF
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  name: 99-master-br-vm
  labels:
    machineconfiguration.openshift.io/role: master
spec:
  config:
    ignition:
      version: 3.4.0
    storage:
      files:
      - path: /etc/NetworkManager/system-connections/br-vm.nmconnection
        mode: 0600
        overwrite: true
        contents:
          source: data:text/plain;charset=utf-8;base64,$BR
      - path: /etc/NetworkManager/system-connections/br-vm-port-ens21.nmconnection
        mode: 0600
        overwrite: true
        contents:
          source: data:text/plain;charset=utf-8;base64,$PORT
EOF
# oc apply -f 99-master-br-vm.yaml
# cat > nad-vm-lan.yaml <<'EOF'
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
  name: vm-lan
  namespace: default
  annotations:
    k8s.v1.cni.cncf.io/resourceName: bridge.network.kubevirt.io/br-vm
spec:
  config: |
    {
      "cniVersion": "0.3.1",
      "name": "vm-lan",
      "type": "bridge",
      "bridge": "br-vm",
      "ipam": {},
      "macspoofchk": false,
      "preserveDefaultVlan": false
    }
EOF
# oc apply -f nad-vm-lan.yaml
項目説明
br-vm.nmconnectionブリッジ br-vm。ホスト側には IP アドレスを付けない
br-vm-port-ens21.nmconnectionens21 を br-vm のポートにする
NetworkAttachmentDefinition vm-lan仮想マシンの NIC をつなぐ先(bridge CNI で br-vm へ)。ここでは default プロジェクトに作る
resourceNamebr-vm があるノードにだけ仮想マシンを置くための目印(bridge-marker が付ける)

MachineConfig の適用で、ノードは 1 台ずつ再起動します。動いている仮想マシンは、再起動するノードからライブ マイグレーションで退避されます。

ノード障害への備え(Node Health Check・Self Node Remediation)

KubeVirt は、ノードが止まっても、そのノードの仮想マシンを自動では別のノードで起動しません(止まったノードで動き続けている可能性があり、同じディスクを二重に使うと壊れるため)。Node Health Check がノードの異常を見つけ、Self Node Remediation が「そのノードは再起動した(仮想マシンは止まった)」ことを保証すると、仮想マシンが別のノードで起動し直します。

(1) Community Operators から node-healthcheck-operator と self-node-remediation を入れます(コンソールの Software Catalog からでも、Subscription の YAML でもかまいません)。

# cat <<'EOF' | oc apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: openshift-workload-availability
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
  name: workload-availability
  namespace: openshift-workload-availability
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: node-healthcheck-operator
  namespace: openshift-workload-availability
spec:
  channel: stable
  name: node-healthcheck-operator
  source: community-operators
  sourceNamespace: openshift-marketplace
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: self-node-remediation
  namespace: openshift-workload-availability
spec:
  channel: stable
  name: self-node-remediation
  source: community-operators
  sourceNamespace: openshift-marketplace
EOF
# oc -n openshift-workload-availability get csv

期待する出力(抜粋):

node-healthcheck-operator.v0.12.0   Node Health Check Operator - Community Edition       0.12.0   Succeeded
self-node-remediation.v0.13.0       Self Node Remediation Operator - Community Edition   0.13.0   Succeeded

(2) コントロール プレーン(この構成では仮想マシンを動かすノード)を対象に NodeHealthCheck を作ります。

# cat <<'EOF' | oc apply -f -
apiVersion: remediation.medik8s.io/v1alpha1
kind: NodeHealthCheck
metadata:
  name: nhc-control-plane
spec:
  minHealthy: 51%
  selector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: Exists
  remediationTemplate:
    apiVersion: self-node-remediation.medik8s.io/v1alpha1
    kind: SelfNodeRemediationTemplate
    namespace: openshift-workload-availability
    name: self-node-remediation-automatic-strategy-template
  unhealthyConditions:
  - type: Ready
    status: "False"
    duration: 60s
  - type: Ready
    status: Unknown
    duration: 60s
EOF
# oc get nodehealthcheck nhc-control-plane -o jsonpath='{.status.phase} {.status.healthyNodes}/{.status.observedNodes}{"\n"}'

期待する出力:

Enabled 3/3
項目説明設定値
minHealthyこれより健全なノードが少ないときは復旧しない(クラスタ全体の障害で全ノードを再起動しないため)51%(3 台中 2 台)
selector対象のノードcontrol-plane のノード
remediationTemplate復旧の方法。Self Node Remediation の自動の戦略automatic-strategy
unhealthyConditionsノードの Ready が False または Unknown のまま 60 秒続いたら異常とみなす60s(検証のため短め。既定は 300s)

Self Node Remediation は、ノードのウォッチドッグ(/dev/watchdog)でノードを確実に再起動させます。この検証の仮想マシン(ノード)にはウォッチドッグ デバイスが無いため、ソフトウェアでの再起動(isSoftwareRebootEnabled)が使われます。物理サーバーでは iTCO_wdt などのウォッチドッグがあることを確認します。

次の手順

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次