ゼロからはじめるOpenShift Virtualization(3)共有ストレージの作成(NFS CSIドライバーの構築)

Red Hatでソリューションアーキテクトをしている田中司恩(@tnk4on)です。 この連載はvSphere環境上にOpenShift Container Platform(以下、OpenShift)およびOpenShift Virtualizationの環境を構築する方法を解説するシリーズです。

第3回は共有ストレージの作成について解説します。今後の連載予定は下記の通りです。

(2024/7/17追記:2つ目のディスクを/mnt/nfsにマウントする手順を追加しました)

(2024/7/23追記:NFS CSIドライバーを使用する内容に変更しました。以前の内容は番外編として別記事にしました。)

(2024/9/29追記:NFS CSIドライバーのアップデート方法を追加しました)

(2024/10/2追記:踏み台サーバーに2つ目のディスクを追加するコマンドを修正しました)

-目次-


共有ストレージの作成概要

本記事ではOpenShift Virtualizationに必要な共有ストレージの作成を目的として、下記の内容を紹介します。

  1. OpenShift Virtualizationに必要なストレージ
  2. 踏み台サーバー上のNFSサーバー設定
  3. NFS CSIドライバーの設定
  4. イメージレジストリの設定

OpenShiftの標準サブスクリプションであるOpenShift Container Platform(OCP)やその下位エディションのOpenShift Kubernetes Engine(OKE)*1は複数ノードから書き込み可能な共有ストレージの機能は標準搭載していません。 通常はOpenShiftクラスター外部のストレージ製品を利用する形態でこれを構成します。 対して、OCPの上位エディションのOpenShift Platform Plus(OPP)*2にはOpenShift Data Foundation(ODF)*3が含まれます。このODFを使用することでOpenShiftをHCIで構成し共有ストレージを提供できるようになります(vSphereで言うところのvSANと同様のことができるとイメージください)。 ODFはOpenShiftに統合されたストレージであるため、実稼働環境における推奨されたストレージの選択です。

しかしながら、ODFはその構成上非常に多くのリソースを使用します。潤沢なリソースがない環境での利用は難しいため、今回の検証環境でODFは利用しません。 今回は1台のRHEL(または他のLinux等でも可)で構成できるNFSを利用したシンプルな共有ストレージを作成することとします。 なお、このストレージはあくまで検証用途であり、実稼働環境での利用はできませんのでご注意ください。*4

前提条件

  • 連載第1回および第2回の作業を完了していることとします
  • 全体の構成や各パラメーターなどは連載第1回を参照してください
  • 作業用端末のプロンプトは%とします
  • 踏み台サーバーのプロンプトは$とします
  • 踏み台サーバーの作業は明示的に記載がある場合を除いて、作業ディレクトリ${HOME}/workで作業することとします

第2回の作業完了後の構成
第2回の作業完了後の構成

1. OpenShift Virtualizationに必要なストレージ

1-1 ストレージのアクセスモードについて

OpenShift Virtualizationに必要なストレージの要件についてはOpenShiftのインストールドキュメントに記載があります。 その一節にライブマイグレーションについての要件の記載があります。

ライブマイグレーションには共有ストレージが必要です。OpenShift Virtualizationのストレージは、ReadWriteMany(RWX)アクセスモードをサポートし、使用する必要があります。

第12章 ベアメタルへのインストール | Red Hat Product Documentation

ReadWriteMany(RWX)モードとは、複数のノードからの読み書き(ReadWrite)に対応したアクセスモードのことです。 対してReadWriteOnce (RWO)とは、1つのノードから読み書きに対応したアクセスモードです。

RWOとRWXのアクセスモードの違い
RWOとRWXのアクセスモードの違い

アクセスモードは他にもReadOnlyMany(ROX)やReadWriteOncePod(RWOP)がありますが本記事では詳細な解説は省略します。 OpenShiftが対応しているアクセスモードの詳細については下記ドキュメントの「表3.1 アクセスモード」を参照ください。

3.3. 永続ボリューム | Red Hat Product Documentation

OpenShiftではブロック、ファイル、オブジェクトの3種類のストレージタイプに対応しており、RWXモードで利用可能なのはファイルかオブジェクトのタイプです。

OpenShiftで推奨されるストレージのタイプ

第7章 スケーラビリティとパフォーマンスの最適化 | Red Hat Product Documentation

今回は構築が簡単なNFSを使ったファイルストレージを共有ストレージとして使用します。

1-2 永続ストレージ

OpenShiftがサポートするストレージのタイプは大別して2つのカテゴリーに大別されます。

  • 一時ストレージ:Podとコンテナーが短期間のローカルストレージの操作に使用し、再起動や障害でデータが失われることを想定したストレージ
  • 永続ストレージ:Podとコンテナーがデータの永続性を保証し、再起動や障害後もデータが保持されるストレージ

OpenShift Virtualizationでは仮想マシンのディスクが永続化されている必要があるため、使用するストレージのタイプは永続ストレージになります。 本記事では一時ストレージについては触れませんので、詳細は下記ドキュメントを参照ください。

1.2. ストレージタイプ | Red Hat Product Documentation

1-3 永続ボリュームの動的プロビジョニング

OpenShiftでPodやコンテナで永続ストレージを利用する場合は永続ボリューム(PV)を利用します。 PVはOpenShiftクラスタ内で永続的なストレージリソースを提供するためのオブジェクトです。 また、PVはクラスタ全体で利用可能なストレージを抽象化し、コンテナのライフサイクルとは独立してデータを保存できます。

PVの動的プロビジョニングとは、必要なときに必要な量のストレージを自動的に割り当てる機能です。これにより、管理者は事前に大量のストレージを確保する必要がなくなり、効率的なリソース利用が可能となります。

[PVの動的プロビジョニングメリット]

  • 自動化: 手動でのストレージ割り当ての手間が省ける。
  • 効率化: 必要な分だけストレージを割り当てるため、無駄が少ない。
  • スケーラビリティ: 必要に応じてストレージを増減できるため、システムのスケールアップ・ダウンが容易。

手動プロビジョニングと動的プロビジョニングのPV作成のフローを図で表したものが下記になります。

手動プロビジョニングによるPV作成のフロー
手動プロビジョニングによるPV作成のフロー

動的プロビジョニングによるPV作成のフロー
動的プロビジョニングによるPV作成のフロー

1-4 Container Storage Interface (CSI)

Container Storage Interface (CSI)は、異なるコンテナーオーケストレーションシステム間でコンテナーストレージを管理するためのAPI仕様です*5。 ストレージベンダーはCSIに準拠したプラグイン(ドライバー)を開発することで、他のコンテナーオーケストレーションシステムでもプラグインに対応したストレージが機能するようにします。 CSIはストレージを共通化した仕様で抽象化することにより、接続に必要な手順を一律化し、コンテナーオーケストレーションシステムが多様なストレージに対応が可能となります。

OpenShiftはKubernetesを拡張したものであり、純粋なアップストリーム版のKubernetes(いわゆるVanilla Kubernetes)と同様にCSIドライバーを利用できます。 このCSIドライバーを利用することでPVの動的プロビジョニングを行うことができます。

2. 踏み台サーバー上のNFSサーバー設定

今回の検証環境の構成では永続ストレージの元となるNFSサーバーは踏み台サーバー上で実行します。 そのために、まずはLinuxでNFSサーバーを実行する基本機能として、NFSのエクスポートを行います。

踏み台サーバー上のNFSサーバー設定後の構成
踏み台サーバー上のNFSサーバー設定後の構成

2-1 NFSサーバーの設定

NFSをエクスポートする基点となるディレクトリを作成します。 このディレクトリはOSの起動ディスクとは別のディスク上に作成することとします。 第1回で踏み台サーバーの仮想マシンに2つ目のディスクを追加していない場合は追加してください。 govcコマンドで実行する場合は下記のコマンドを使用します。

踏み台サーバーに2つ目のディスクを追加します。

$ node=bastion
$ govc vm.disk.create \
    -vm ${node} \
    -name "${node}/${node}_1.vmdk" \
    -size 500G \
    -thick=false \
    -ds=NVMe

ディスクデバイスを確認します。 下記のように2つ目のディスクが追加されました。

$ govc device.ls -vm ${node} | grep disk
disk-1000-0   VirtualDisk                104,857,600 KB
disk-1000-1   VirtualDisk                524,288,000 KB

2つ目のディスクをOS上で使用できるようにし、/mnt/nfsにマウントします。 下記のコマンドを実行します。(2つ目のディスクが/dev/sdbにあることとします)

$ sudo parted /dev/sdb --script mklabel gpt
$ sudo parted /dev/sdb --script mkpart primary ext4 0% 100%
$ sudo mkfs.ext4 /dev/sdb1
$ sudo mkdir -p /mnt/nfs
$ echo '/dev/sdb1  /mnt/nfs  ext4  defaults  0  0' | sudo tee -a /etc/fstab
$ sudo systemctl daemon-reload
$ sudo mount -a

正常にマウントされているか確認します。

$ df -h /mnt/nfs
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/sdb1        492G   28K  467G    1% /mnt/nfs

次に/etc/exportsファイルにNFSエクスポート用の設定を追記します。

$ sudo vi /etc/exports
/mnt/nfs 192.168.1.0/24(rw,async,no_subtree_check,no_root_squash)

exportfs -rコマンドを使用して設定の検証を行います。設定に問題がなければ何も出力はされません。

$ sudo exportfs -r
$

たとえば、エクスポートするディレクトリが見つからない場合は下記のエラーが出ます。

$ sudo exportfs -r
exportfs: Failed to stat /mnt/nfs: No such file or directory

NFSエクスポートの設定に問題がなければファイアーウォールの設定を行います。 NFSで使用するポートをファイアーウォール設定に追記します。

$ sudo firewall-cmd --add-service nfs --permanent 
$ sudo firewall-cmd --reload

NFSサービスを自動起動サービスに登録し、同時に起動します。

$ sudo systemctl enable --now nfs-server

これでNFSサーバーの設定が完了しました。

3. NFS CSIドライバーの設定

踏み台サーバー上に構築したNFSサーバーを利用してPVの動的プロビジョニングを行うため、OpenShiftにNFS CSI driver for Kubernetes(以下、NFS CSIドライバー)をインストールします。

NFS CSIドライバー導入後の構成
NFS CSIドライバー導入後の構成

3-1 NFS CSIドライバーについて

OpenShiftでPVの動的プロビジョニングを行うためにはストレージとそれに対応したCSIドライバーが必要です。 今回構築したストレージはRHELで立てた汎用的なNFSサーバーなので専用のCSIドライバーがありません。 そこで今回は、汎用的なNFSサーバーをバックエンドに使用しPVの動的プロビジョニングを行うことができるNFS CSIドライバーを利用します。

このNFS CSIドライバーはKubernetes CSIでホストされているリポジトリの1つです。OSSで開発されておりApache-2.0 licenseに従い無償で利用できます。

kubernetes-csi/csi-driver-nfs: This driver allows Kubernetes to access NFS server on Linux node.

NFS CSIドライバーはfsGroupPolicy、Volume Snapshot、Volume Cloningに対応しています。

実稼働環境ではストレージベンダーなどの製品をご利用ください。

3-2 NFS CSIドライバーの動作

NFS CSIドライバーの動作の流れは下記の通りです

NFS CSIドライバーの動作
NFS CSIドライバーの動作

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

NFS CSIドライバーをインストールする流れは下記のとおりです。

  1. Helmのインストール
  2. NFS CSIドライバーのインストール

現時点ではコマンドの詳細を深く理解する必要はありません。まずはおまじない的にコマンドをコピペして実行してください。

(1)Helmのインストール

NFS CSIドライバーのインストールでHelm*6を使用します。 Helmはパッケージマネージャーの1つで、アプリケーションをパッケージ(Helmではチャートという)としてKuberenetes上に簡単にデプロイできる仕組みです。 今回の構成では踏み台サーバーにHelmをインストールします。Helmのインストールドキュメントは下記です(本記事ではRed Hatが提供しているHelmのバイナリを利用します)。

6.2. Helm のインストール | Red Hat Product Documentation

x86_64の環境にHelmをインストールするコマンド例は下記のとおりです。

$ sudo curl -L https://mirror.openshift.com/pub/openshift-v4/clients/helm/latest/helm-linux-amd64 -o /usr/local/bin/helm
$ sudo chmod +x /usr/local/bin/helm

Helmのインストール後、helmコマンドを実行して動作確認を行います。

$ helm version
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/user/work/auth/kubeconfig
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/user/work/auth/kubeconfig
version.BuildInfo{Version:"v3.14.4+37.el9", GitCommit:"7163c12e65c8c0a40334c0a4b514b991ea37aa21", GitTreeState:"clean", GoVersion:"go1.21.9 (Red Hat 1.21.9-2.el9_4)"}

KUBECONFIG環境変数がセットされていて、かつkubeconfigファイルのパーミッションが所有者以外から読み込み可能になっているとコマンドの出力に警告が出ます。 気になる方はchmodコマンドでパーミッションを変更してください。

$ chmod 600 auth/kubeconfig

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

NFS CSIドライバーはOpenShiftにインストールします。初めにチャートリポジトリを追加し、その後にインストールを行います。

helm repo addコマンドでNFS CSIドライバーのチャートリポジトリを追加します。

実行コマンド

$ helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts

実行コマンドと結果

$ 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
  • ~/.config/helmディレクトリが作成され、参照するリポジトリの情報が保存されます

helm installコマンドでNFS CSIドライバーをインストールします。 --versionオプションを使ってバージョンを指定します。(記事執筆時点での最新版はv4.8.0)

実行コマンド

$ helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.8.0
  • --namespace kube-system:インストール先のネームスペースにkube-systemを指定
  • --version v4.8.0:インストールするバージョンを指定。指定するバージョンはNFS CSIドライバーのリリースページ*7を参照のこと

helm installコマンドを実行すると下記のような応答が出力されます

実行コマンドと結果

$ helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.8.0
NAME: csi-driver-nfs
LAST DEPLOYED: Fri Jul 19 10:40:55 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The CSI NFS Driver is getting deployed to your cluster.

To check CSI NFS Driver pods status, please run:

  kubectl --namespace=kube-system get pods --selector="app.kubernetes.io/instance=csi-driver-nfs" --watch

NFS CSIドライバーが正常にインストールできたかどうかは次の手順で確認します。

(追記)NFS CSIドライバーのアップデート

NFS CSIドライバーのリリースページ*8で新しいバージョンがリリースされた場合は、helm repo updateを行うことで新しいバージョンのインストールを行うことができます。 下記はチャートリポジトリのアップデートを行い、v4.9.0をインストールする手順です。

実行コマンド

$ helm repo update
$ helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.9.0

実行コマンドと結果

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "csi-driver-nfs" chart repository
Update Complete. ⎈Happy Helming!⎈

$ helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.9.0
NAME: csi-driver-nfs
LAST DEPLOYED: Thu Sep 26 23:18:01 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The CSI NFS Driver is getting deployed to your cluster.

To check CSI NFS Driver pods status, please run:

  kubectl --namespace=kube-system get pods --selector="app.kubernetes.io/instance=csi-driver-nfs" --watch

(3)NFS CSIドライバーのインストールの確認

OpenShiftのWebコンソールから「管理者向け表示」のプルダウンを選択し「開発者」を選択します。 これにより開発者向け表示(Developerパースペクティブ)に切り替えることができます。

開発者向け表示(Developerパースペクティブ)に切り替える
開発者向け表示(Developerパースペクティブ)に切り替える

左のメニューから「トポロジー」を選択します。プロジェクトのプルダウンメニューから「すべてのプロジェクト」を選択します。 「デフォルトプロジェクトの表示」スイッチをオンにします。

トポロジーメニューのプロジェクト表示
トポロジーメニューのプロジェクト表示

「デフォルトプロジェクトの表示」をオンにすることでOpenShift上の管理用のプロジェクトの選択が可能になります。 入力欄にkube-systemと入力して表示のフィルタリングを行い、プロジェクト欄に表示されたkube-systemを選択します。

フィルタリングを行いkube-systemを選択
フィルタリングを行いkube-systemを選択

画面内に「D」のアイコンがついたcsi-nfs-controllerというオブジェクト(Deployment)と、「DS」のアイコンがついたcsi-nfs-nodeというオブジェクト(DaemonSet)が表示されます。 csi-nfs-controllerがNFS CSIドライバーの管理コンテナ、csi-nfs-nodeが各ノード上で稼働する実行コンテナになります。 csi-nfs-controllerとcsi-nfs-nodeの2つを合わせたものがcsi-driver-nfsという名前のHelmチャートとしてデプロイされています。

csi-nfs-controllerのDeploymentの詳細表示
csi-nfs-controllerのDeploymentの詳細表示

csi-nfs-nodeのDaemonSetの詳細表示
csi-nfs-nodeのDaemonSetの詳細表示

左のメニューからHelmを選択すると、現在選択しているプロジェクトに登録済みのHelmリリース*9の情報が表示されます。

デプロイされたHelmリリースの表示
デプロイされたHelmリリースの表示

「HR」アイコンが付いたHelmリリース名(ここではcsi-driver-nfs)のリンクを選択します。

Helmリリースの詳細
Helmリリースの詳細

リソースタブを選択すると、Helmでインストールされたリソースの名前やタイプが一覧で表示されます。

3-4 ストレージクラスとボリュームスナップショットクラスの作成

インストールしたNFS CSIドライバーを使ってPVの動的プロビジョニングを行うためにストレージクラス(StorageClass)を作成します。 ストレージクラスとはPVの動的プロビジョニングを定義するために使用されます。ユーザーは必要なストレージクラスを指定して永続ボリューム要求(PVC)を作成します。 ストレージクラスの詳細については下記のドキュメントを参照してください。

5.6. デフォルトストレージクラスの管理 | Red Hat Product Documentation

また、合わせてOpenShift Virtualizationで仮想マシンのスナップショットを取得するためのボリュームスナップショットクラス(VolumeSnapshotClass)も作成します。 ボリュームスナップショットについては下記のドキュメントを参照してください。

5.4. CSI ボリュームスナップショット | Red Hat Product Documentation

(1)ストレージクラスの作成

下記のコマンドでストレージクラスのカスタムリソースをstorageclass-nfs.yamlという名前で作成します。

$ cat > storageclass-nfs.yaml <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.1.206
  share: /mnt/nfs
reclaimPolicy: Delete
volumeBindingMode: Immediate
EOF
  • server: 192.168.1.206:NFSサーバーのIPアドレス(またはホスト名)を指定します
  • share: /mnt/nfs:NFSサーバー上でエクスポートしているパス名を指定します
  • reclaimPolicy: Delete:PVを削除した時にNFSサーバー上のデータを保持するかどうかを指定します。DeleteはPVCの削除と同時にPVも削除され、NFSサーバー上の実データも削除されます。
    • PVCの削除後もPVとして残す場合はRetain、PVは削除しつつもNFSサーバー上の実データを残す場合はArchiveを指定します。

下記のコマンドでストレージクラスを作成します。

$ oc create -f storageclass-nfs.yaml

(2)ボリュームスナップショットクラスの作成

下記のコマンドでボリュームスナップショットクラスのカスタムリソースをsnapshotclass-nfs.yamlという名前で作成します。

$ cat > snapshotclass-nfs.yaml <<EOF
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-nfs-snapclass
driver: nfs.csi.k8s.io
deletionPolicy: Delete
EOF

下記のコマンドでボリュームスナップショットクラスを作成します。

$ oc create -f snapshotclass-nfs.yaml

3-5 デフォルトストレージクラスの設定

デフォルトストレージクラスとは2つ以上のストレージクラスが存在する場合に標準的に使用されるストレージクラスのことです。 今回の構成ではストレージクラスは1つしか作成されていませんが、明示的に設定をしておくことで他のストレージクラスよりも優先的に利用するようにしておきます。

デフォルトストレージクラスを設定するには下記のコマンドを実行します。 今回はストレージクラス名:nfs-csiに対して通常のコンテナアプリケーションと仮想マシンの両方にデフォルト設定を行います。

$ oc patch storageclass nfs-csi -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
$ oc patch storageclass nfs-csi -p '{"metadata": {"annotations":{"storageclass.kubevirt.io/is-default-virt-class":"true"}}}'

同様にデフォルトボリュームスナップショットクラスを設定するコマンドを実行します。 ボリュームスナップショットクラス名はcsi-nfs-snapclassを指定します。

$ oc patch volumesnapshotclass csi-nfs-snapclass --type merge -p '{"metadata": {"annotations":{"snapshot.storage.kubernetes.io/is-default-class":"true"}}}'
$ oc patch volumesnapshotclass csi-nfs-snapclass --type merge -p '{"metadata": {"annotations":{"snapshot.storage.kubevirt.io/is-default-virt-class":"true"}}}'

設定が反映されているか下記のコマンドを実行して確認を行います。 oc getコマンドで引数にstorageclassとnfs-csiを指定し、さらに-o yamlオプションを追加して、ストレージクラスの設定内容をYAML形式で出力を行います。

$ oc get storageclass nfs-csi -o yaml

出力結果内のannotations:に先ほど設定した値が反映されているか確認できます。下記の出力結果を展開して内容を確認ください。

出力結果を開く

$ oc get storageclass nfs-csi -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
    storageclass.kubevirt.io/is-default-virt-class: "true"
  creationTimestamp: "2024-07-23T05:35:49Z"
  name: nfs-csi
  resourceVersion: "596631"
  uid: 86a54044-89fa-4f49-8c35-6c5bc6cff696
parameters:
  server: 192.168.1.206
  share: /mnt/nfs
provisioner: nfs.csi.k8s.io
reclaimPolicy: Delete
volumeBindingMode: Immediate

同様にボリュームスナップショットクラスの設定内容を確認します。 oc getコマンドの引数はvolumesnapshotclassをcsi-nfs-snapclass指定します。

$ oc get volumesnapshotclass csi-nfs-snapclass -o yaml

出力結果を開く

$ oc get volumesnapshotclass csi-nfs-snapclass -o yaml
apiVersion: snapshot.storage.k8s.io/v1
deletionPolicy: Delete
driver: nfs.csi.k8s.io
kind: VolumeSnapshotClass
metadata:
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"
    snapshot.storage.kubevirt.io/is-default-virt-class: "true"
  creationTimestamp: "2024-07-23T05:37:13Z"
  generation: 1
  name: csi-nfs-snapclass
  resourceVersion: "597248"
  uid: 774868f9-23f8-46c7-b02e-bf832d6c31ec

3-6 動的プロビジョニングの動作テスト

NFS CSIドライバーの設定ができたので実際にサンプルのDeploymentを実行して動的プロビジョニングの動作テストを行います。

(1)サンプルファイルのダウンロード

NFSプロビジョナーのリポジトリで公開されているサンプルのファイルをダウンロードします。 PVCとPodの実行が定義されたDeploymentのYAMLファイルをダウンロードします。

$ wget https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/deployment.yaml
$ ls -d deployment.yaml
deployment.yaml

(2)PVCとPodの作成

oc createコマンドでYAMLファイルからリソースを作成します。-fオプションで読み込むYAMLファイルを指定します。 最初にテスト実行用のプロジェクトを作成し、そのプロジェクト内にリソースを作成します。

下記のコマンドでプロジェクトを新規作成します。名前はtestとします。

実行コマンド

$ oc new-project test

実行コマンドと結果

$ oc new-project test
Now using project "test" on server "https://api.ocp.home.lab:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.43 -- /agnhost serve-hostname

続けてDeploymentリソースを作成します。

実行コマンド

$ oc create -f deployment.yaml

実行コマンドと結果

$ oc create -f deployment.yaml
persistentvolumeclaim/pvc-deployment-nfs created
deployment.apps/deployment-nfs created

PVCとPodの実行を定義したDeploymentが作成されました。

(3)確認

Webコンソールの管理者向け表示のメニューから「ストレージ>PersistentVolumeClaims」を選択すると、作成されたPVCとバインドされたPVが一覧で表示されます。 「プロジェクト」選択プルダウンからtestを選ぶと、先ほど作成したtestプロジェクト上に作成されたPVCが表示されます。

サンプルのDeploymentから作成されたPVCとPV
サンプルのDeploymentから作成されたPVCとPV

このサンプルでは下記の情報が確認できます。

  • PVC:pvc-deployment-nfs
  • PV:pvc-43c68d68-95f2-4b2a-99cf-4eef0debba39
  • PVの容量:10 GiB
  • 使用したストレージクラス:nfs-csi

踏み台サーバー上のNFSエクスポートディレクトリを確認すると、PV用のディレクトリが自動で作成されていることが分かります。

$ tree /mnt/nfs/
/mnt/nfs/
└── pvc-43c68d68-95f2-4b2a-99cf-4eef0debba39
    └── outfile

1 directory, 1 file

NFSサーバー上に作成されるディレクトリの命名規則はPV名と同じものになります。

Webコンソールの管理者向け表示のメニューから「Workloads>Pods」を選択すると、実行中のPodが一覧で表示されます。 サンプルのPod(deployment-nfs-7579c47f6-r6zbq)のステータスがRunningになっており、このPodが正常に実行していることが分かります。

testプロジェクトで実行中のPod一覧
testプロジェクトで実行中のPod一覧

Pod名のdeployment-nfs-7579c47f6-r6zbqのリンクを選択するとPodの詳細画面が表示されます。 このコンテナがmcr.microsoft.com/oss/nginx/nginxコンテナイメージから作成されていて状態がRunningになっており、コンテナの実行が継続していることが確認できます。 また、このコンテナではPVはnfsという名前でボリュームとして扱われており、コンテナ内の/mnt/nfsディレクトリに読み書き可能でマウントされています。

サンプルPodの詳細情報
サンプルPodの詳細情報

このサンプルではコンテナの実行コマンドとしてホスト名と日時を1秒ごとにループで書き込み続けるようにプログラムされています*10。 そのため、サンプルのPod起動後にNFSサーバー上のディレクトリを確認すると、コンテナの中で作成されたoutfileファイルに継続的にデータが書き込み続けていることが分かります。

$ tail -f /mnt/nfs/pvc-43c68d68-95f2-4b2a-99cf-4eef0debba39/outfile
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:02 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:03 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:04 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:05 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:06 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:07 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:08 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:09 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:10 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:11 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:12 UTC 2024
deployment-nfs-7579c47f6-r6zbq Tue Jul 23 08:04:13 UTC 2024
...

(4)サンプルのPodとPVの削除

作成したサンプルのPodとPVを削除する場合は下記のコマンドを実行します。 oc delete projectコマンドでプロジェクトごとOpenShift上のリソースを削除します。

実行コマンド

$ oc delete project test

実行コマンドと結果

$ oc delete project test
project.project.openshift.io "test" deleted

実行中のコンテナが強制的に停止し、すべてのリソースが削除するまで数十秒程度かかります。

PV削除後のNFSサーバー上の実体ディレクトリは下記のようになります。

$ tree /mnt/nfs/
/mnt/nfs/

0 directories, 0 files

PV削除と同時に実体のディレクトリも削除されます

(5)NFS CSIドライバーの削除

NFS CSIドライバーの削除を行う場合はhelm deleteコマンドを実行します。 helm deleteコマンドに引数としてcsi-driver-nfsとネームスペースにkube-systemを指定して実行します。

$ helm uninstall csi-driver-nfs -n kube-system

4. イメージレジストリの設定

NFS CSIドライバーを設定したことで永続ボリュームを利用できるようになりました。 次は、OpenShiftのインストール後の作業の1つであるイメージレジストリの設定を行います。

この作業はストレージの設定が事前に必要なため、第2回のインストール後の作業では対象外としました

4-1 イメージレジストリーの構成

イメージレジストリはOpenShiftの内部で実行されるコンテナレジストリーです。 イメージレジストリーはパブリックなコンテナレジストリーと同様に、OpenShift上でビルドしたコンテナイメージを登録したり、登録したコンテナイメージからコンテナを実行できます。 また、OpenShiftの外部との接続口(ルート)を作成することでイメージレジストリーを外部へ公開できます。

イメージレジストリーの構成図
イメージレジストリーの構成図

イメージレジストリーの設定方法はOpenShiftのインストールドキュメントに記載があります。下記のドキュメントを参照してください。

第24章 任意のプラットフォームへのインストール | Red Hat Product Documentation

実稼働環境におけるレジストリーの推奨ストレージはオブジェクトストレージです(次点はブロックストレージ)。 本記事で紹介するファイルストレージを使った構成はあくまで検証用途での利用を前提としています。

ファイルストレージは、実稼働ワークロードを使用した OpenShift イメージレジストリークラスターのデプロイメントには推奨されません。

https://docs.redhat.com/ja/documentation/openshift_container_platform/4.16/html/scalability_and_performance/scalability-and-performance-optimization#specific-application-storage-recommendations

4-2 イメージレジストリーの設定

まず、現在のイメージレジストリーの設定を確認します。 下記のコマンドでconfigs.imageregistry.operator.openshift.ioのリソースからstorageとmanagementStateの値を取得します。

実行コマンド

$ oc get configs.imageregistry.operator.openshift.io -o json | jq '.items[] | {storage: .spec.storage, managementState: .spec.managementState}'

実行コマンドと結果

$ oc get configs.imageregistry.operator.openshift.io -o json | jq '.items[] | {storage: .spec.storage, managementState: .spec.managementState}'
{
  "storage": {},
  "managementState": "Removed"
}

storageは空の値、managementStateはRemovedがセットされています。

次に、下記のコマンドを実行してレジストリーPodが存在しないことを確認します。 oc get podコマンドで-lオプションを使用し、ラベルがdocker-registry=defaultであるポッドをフィルタリングします。

実行コマンド

$ oc get pod -n openshift-image-registry -l docker-registry=default

実行コマンドと結果

$ oc get pod -n openshift-image-registry -l docker-registry=default
No resources found in openshift-image-registry namespace.

イメージレジストリーがまだ設定されていないことが確認できました。下記のコマンドを実行し、イメージレジストリーを有効化します。

実行コマンド

$ oc patch config.imageregistry.operator.openshift.io/cluster --type merge -p '{"spec":{"storage":{"pvc":{"claim":""}}}}'
$ oc patch config.imageregistry.operator.openshift.io/cluster --type merge -p '{"spec":{"managementState":"Managed"}}'

実行コマンドと結果

$ oc patch config.imageregistry.operator.openshift.io/cluster --type merge -p '{"spec":{"storage":{"pvc":{"claim":""}}}}'
config.imageregistry.operator.openshift.io/cluster patched
$ oc patch config.imageregistry.operator.openshift.io/cluster --type merge -p '{"spec":{"managementState":"Managed"}}'
config.imageregistry.operator.openshift.io/cluster patched

コマンドの実行後、先ほどの設定を再度確認します。 claimにimage-registry-storageの値が自動で追加されています。

実行コマンド

$ oc get configs.imageregistry.operator.openshift.io -o json | jq '.items[] | {storage: .spec.storage, managementState: .spec.managementState}'

実行コマンドと結果

$ oc get configs.imageregistry.operator.openshift.io -o json | jq '.items[] | {storage: .spec.storage, managementState: .spec.managementState}'
{
  "storage": {
    "managementState": "Managed",
    "pvc": {
      "claim": "image-registry-storage"
    }
  },
  "managementState": "Managed"
}

しばらくしてからレジストリーPodを確認すると、下記のように先ほどは無かったレジストリーPodが起動していることが分かります。

$ oc get pod -n openshift-image-registry -l docker-registry=default
NAME                              READY   STATUS    RESTARTS   AGE
image-registry-5cd6bf8ddf-mgkpm   1/1     Running   0          53s

デフォルトではレジストリーPodのレプリカ数は1で実行されます。 PVのアクセスモードがRWXをサポートしている場合はレジストリーPodのレプリカ数を2にして高可用性構成が可能です。

下記のコマンドを実行して自動で作成されたPVCとPVの設定を確認します。 イメージレジストリーのPVはデフォルトで100GiBの容量を使用します。

$ oc get pvc -n openshift-image-registry
NAME                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
image-registry-storage   Bound    pvc-b3c88568-404c-40c1-a4c8-d252b804a9fc   100Gi      RWX            nfs-csi        <unset>                 2m4s
$ oc get pv -n openshift-image-registry
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                             STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-b3c88568-404c-40c1-a4c8-d252b804a9fc   100Gi      RWX            Delete           Bound    openshift-image-registry/image-registry-storage   nfs-csi        <unset>                          2m11s

4-3 レジストリーの公開

OpenShift上で起動したイメージレジストリーを外部から利用できるように公開設定を行います。 イメージレジストリーの公開方法のドキュメントは下記です。

第5章 レジストリーの公開 | Red Hat Product Documentation

下記のコマンドを実行してイメージレジストリーを公開します。

実行コマンド

$ oc patch configs.imageregistry.operator.openshift.io/cluster --type=merge -p '{"spec":{"defaultRoute":true}}'

実行コマンドと結果

$ oc patch configs.imageregistry.operator.openshift.io/cluster --type=merge -p '{"spec":{"defaultRoute":true}}'
config.imageregistry.operator.openshift.io/cluster patched

イメージレジストリーの公開後、下記のコマンドで接続するためのURL(HOST/PORT)を確認できます。

$ oc get route default-route -n openshift-image-registry
NAME            HOST/PORT                                                  PATH   SERVICES         PORT    TERMINATION   WILDCARD
default-route   default-route-openshift-image-registry.apps.ocp.home.lab          image-registry   <all>   reencrypt     None

4-4 レジストリーの操作

(1)ログイン

外部のコンテナツール(今回はPodmanを使用します)から公開したイメージレジストリーを利用するためにはコンテナレジストリーへのログインが必要です。 下記のコマンドを実行してコンテナレジストリーへのログインを行います。

イメージレジストリーのURLを取得しHOST環境変数へセットします。

$ HOST=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}')
$ echo ${HOST}
default-route-openshift-image-registry.apps.ocp.home.lab

Ingress Operatorの証明書を取得してシステムの信頼された証明書ストアに追加することで、イメージレジストリーへのHTTPSでのアクセスを行えるようにします。 oc get secretで証明書を取得し、踏み台サーバー上の証明書ストアに保存します。

$ oc get secret -n openshift-ingress router-certs-default -o go-template='{{index .data "tls.crt"}}' | base64 -d | sudo tee /etc/pki/ca-trust/source/anchors/${HOST}.crt  > /dev/null

信頼されたルート証明書の一覧を更新します。

$ sudo update-ca-trust enable

podman loginコマンドを実行しイメージレジストリーへログインを行います。 ログインに使用するユーザーはocpadminを使用します。 ocpadminでログインしていない場合は事前にoc loginを実行します(連載第2回を参照)。

$ unset KUBECONFIG
$ oc login -u ocpadmin https://api.ocp.home.lab:6443
$ podman login -u ocpadmin -p $(oc whoami -t) ${HOST}

Login Succeeded!が表示されたらログイン成功です。

(2)イメージのプッシュ

Podmanでサンプルのコンテナイメージを作成し、イメージレジストリーへプッシュします。

下記のコマンドを実行し、ベースイメージにUBI9を使用したContainerfileを作成します。

$ cat > Containerfile <<EOF
FROM ubi9
RUN echo "hello" | tee hello.txt
EOF

podman buildコマンドでコンテナイメージをビルドします。

$ podman build -t ${HOST}/openshift/push-test .

出力結果を開く

$ podman build -t ${HOST}/openshift/push-test .
STEP 1/2: FROM ubi9
Resolved "ubi9" as an alias (/etc/containers/registries.conf.d/001-rhel-shortnames.conf)
Trying to pull registry.access.redhat.com/ubi9:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob f50ab65647ec done   |
Copying config e9beb314b7 done   |
Writing manifest to image destination
Storing signatures
STEP 2/2: RUN echo "hello" | tee hello.txt
hello
COMMIT default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test
--> 4d7c9eb63a75
Successfully tagged default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test:latest
4d7c9eb63a75563e6c4deee82d472ddf5fd0770ca4850b0e2e379ee03fe280fc

podman pushコマンドでコンテナイメージをプッシュします。

$ podman push ${HOST}/openshift/push-test

出力結果を開く

$ podman push ${HOST}/openshift/push-test
Getting image source signatures
Copying blob 09a48287d5f7 done   |
Copying blob f36b8ecab85c done   |
Copying config 4d7c9eb63a done   |
Writing manifest to image destination

(3)プッシュしたイメージの確認

Skopeoを使ってプッシュしたコンテナイメージがイメージレジストリーに登録されているか確認します。

下記のコマンドを実行します。 すでにpodamn loginを実行しているためskopeo loginの実行は不要です(SkopeoはPodmanのログイン情報を利用できます)。

$ skopeo inspect docker://${HOST}/openshift/push-test

下記を展開して出力結果を確認ください。

出力結果を開く

$ skopeo inspect docker://${HOST}/openshift/push-test
{
    "Name": "default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test",
    "Digest": "sha256:4c537beccd99c1853f7e242dcfab054cbaf0bf5535233bcb2ac24335f2f8492f",
    "RepoTags": [
        "latest"
    ],
    "Created": "2024-07-09T21:16:33.272504929Z",
    "DockerVersion": "",
    "Labels": {
        "architecture": "x86_64",
        "build-date": "2024-06-28T07:36:05",
        "com.redhat.component": "ubi9-container",
        "com.redhat.license_terms": "https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI",
        "description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
        "distribution-scope": "public",
        "io.buildah.version": "1.33.7",
        "io.k8s.description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
        "io.k8s.display-name": "Red Hat Universal Base Image 9",
        "io.openshift.expose-services": "",
        "io.openshift.tags": "base rhel9",
        "maintainer": "Red Hat, Inc.",
        "name": "ubi9",
        "release": "1123.1719560047",
        "summary": "Provides the latest release of Red Hat Universal Base Image 9.",
        "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/ubi9/images/9.4-1123.1719560047",
        "vcs-ref": "92a4a475241865d0d11bd861fb2b29fbd9b17df0",
        "vcs-type": "git",
        "vendor": "Red Hat, Inc.",
        "version": "9.4"
    },
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
        "sha256:f50ab65647ec96ba313779f24c41e04bc6fde3e3ee79ee377ea8fd1901b896d5",
        "sha256:68513cceebd5b717b7e307a2c2ff646147e5a84b16657755c25da1be2732bf15"
    ],
    "LayersData": [
        {
            "MIMEType": "application/vnd.oci.image.layer.v1.tar+gzip",
            "Digest": "sha256:f50ab65647ec96ba313779f24c41e04bc6fde3e3ee79ee377ea8fd1901b896d5",
            "Size": 79313334,
            "Annotations": null
        },
        {
            "MIMEType": "application/vnd.oci.image.layer.v1.tar+gzip",
            "Digest": "sha256:68513cceebd5b717b7e307a2c2ff646147e5a84b16657755c25da1be2732bf15",
            "Size": 270,
            "Annotations": null
        }
    ],
    "Env": [
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "container=oci"
    ]
}

Webコンソール上で確認する場合は、メニューの「Builds>ImageStreams」を選択します。 イメージレジストリーに登録されているイメージの一覧が表示されます。 名前で検索できますのでプッシュしたイメージ名:push-testを入力します。

OpenShiftの外部からプッシュしたイメージの表示
OpenShiftの外部からプッシュしたイメージの表示

push-testのリンクを選択するとイメージの詳細画面が表示されます。 パブリックイメージリポジトリーに表示されているURLが、OpenShiftの外部からこのイメージをプルする時のURLになります。

イメージレジストリーの登録したイメージの詳細表示
イメージレジストリーの登録したイメージの詳細表示

(4)OpenShift上のイメージをプルする

OpenShift上のイメージをローカルにプルするテストを行います。

ローカルにあるコンテナイメージを削除します。

$ podman images
$ podman rmi push-test:latest

出力結果を開く

$ podman images
REPOSITORY                                                                    TAG         IMAGE ID      CREATED         SIZE
default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test  latest      4d7c9eb63a75  19 minutes ago  220 MB
registry.access.redhat.com/ubi9                                               latest      e9beb314b7a2  11 days ago     220 MB
$ podman rmi push-test:latest
Untagged: default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test:latest
Deleted: 4d7c9eb63a75563e6c4deee82d472ddf5fd0770ca4850b0e2e379ee03fe280fc

podman pullコマンドでイメージをプルします。

$ podman pull ${HOST}/openshift/push-test
$ podman images

出力結果を開く

$ podman pull ${HOST}/openshift/push-test
Trying to pull default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test:latest...
Getting image source signatures
Copying blob f50ab65647ec skipped: already exists
Copying blob 68513cceebd5 done   |
Copying config 4d7c9eb63a done   |
Writing manifest to image destination
4d7c9eb63a75563e6c4deee82d472ddf5fd0770ca4850b0e2e379ee03fe280fc
$ podman images
REPOSITORY                                                                    TAG         IMAGE ID      CREATED         SIZE
default-route-openshift-image-registry.apps.ocp.home.lab/openshift/push-test  latest      4d7c9eb63a75  21 minutes ago  220 MB
registry.access.redhat.com/ubi9                                               latest      e9beb314b7a2  11 days ago     220 MB

イメージレジストリー上のイメージをプルすることができました。

まとめ

OpenShift Virtualizationで使用するための共有ストレージの作成を行いました。 汎用的なNFSサーバーをバックエンドに使用し、NFS CSIドライバーを使用することでPVの動的なプロビジョニングが行えます。 このストレージはOpenShift Virtualization以外でも使用できるため、イメージレジストリーのイメージ保存用のストレージの設定を行いました。 また、イメージレジストリーを公開設定を行うことにより、OpenShiftの外部からイメージをプッシュ、プルできるようになります。 第1回からここまでの作業でようやくOpenShift Virtualizationのインストールを行う準備が整いました。 次はいよいよOpenShift Virtualizationの設定になります。

(次回予告) 次回はOpenShift Virtualizationのインストールと仮想マシンの実行方法について解説します。

*1:Red Hat OpenShift Kubernetes Engine | Red Hat

*2:Red Hat OpenShift Platform Plus とは?をわかりやすく解説

*3:Red Hat OpenShift Data Foundation | Red Hat

*4:7.1.2.1. 特定アプリケーションのストレージの推奨事項を参照 第7章 スケーラビリティとパフォーマンスの最適化 | Red Hat Product Documentation

*5:container-storage-interface/spec: Container Storage Interface (CSI) Specification.

*6:Helm

*7:Releases · kubernetes-csi/csi-driver-nfs

*8:Releases · kubernetes-csi/csi-driver-nfs

*9:OpenShift上にHelmでデプロイされたアプリケーションやサービスのインスタンスのこと

*10:詳細を知りたい方はdeployment.yamlファイルの中身を参照ください

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。