ツリー内ストレージ クラスから Azure Kubernetes Service (AKS) 上の CSI ドライバーに移行する
Container Storage Interface (CSI) ドライバーの実装は、バージョン 1.21 以降の Azure Kubernetes Service (AKS) で導入されました。 CSI が標準として採用されて使用されることにより、ツリー内永続ボリューム (PV) を使っている既存のステートフル ワークロードを、CSI ドライバーを使うように移行またはアップグレードする必要があります。
この記事では、このプロセスを可能な限り簡単にし、データが失われないようにするための、さまざまな移行オプションを提供します。 これらのオプションには、ツリー内から、Azure ディスクと Azure Files の CSI ドライバーへのスムーズな移行に役立つスクリプトが含まれます。
開始する前に
- Azure CLI バージョン 2.37.0 以降。
az --version
を実行してバージョンを見つけ、az upgrade
を実行してバージョンをアップグレードします。 インストールまたはアップグレードする必要がある場合は、Azure CLI のインストールに関するページを参照してください。 - Kubectl とクラスターの管理者は、PVC または PV、ボリューム スナップショット、またはボリューム スナップショットのコンテンツに対する作成、取得、一覧表示、削除のアクセス権を持ちます。 Microsoft Entra RBAC が有効なクラスターの場合、あなたは Azure Kubernetes Service RBAC クラスター管理者ロールのメンバーです。
ディスク ボリュームを移行する
注意
ラベル failure-domain.beta.kubernetes.io/zone
と failure-domain.beta.kubernetes.io/region
は AKS 1.24 で非推奨となり、1.28 で削除されました。 既存の永続ボリュームで、これら 2 つのラベルに一致する nodeAffinity が引き続き使用されている場合は、新しい永続ボリューム設定で topology.kubernetes.io/zone
と topology.kubernetes.io/region
ラベルに変更する必要があります。
ツリー内から CSI への移行は、2 つの移行オプションを使ってサポートされます。
- 静的ボリュームを作成する
- 動的ボリュームを作成する
静的ボリュームを作成する
このオプションを使う場合は、後で作成する新しい PVC に claimRef
を静的に割り当てることによって PV を作成し、PersistentVolumeClaim に対して volumeName
を指定します。
このアプローチには次のような利点があります。
- これは簡単であり、自動化できます。
- ツリー内ストレージ クラスを使って元の構成をクリーンアップする必要はありません。
- Kubernetes PV/PVC の論理的な削除のみを実行し、実際の物理データは削除しないので、低リスクです。
- ディスクやスナップショットなどの追加の Azure オブジェクトを作成する必要がないため、追加コストは発生しません。
評価すべき重要な考慮事項を次に示します。
- 元の動的スタイルのボリュームから静的ボリュームに移行するには、すべてのオプションで PV オブジェクトを手動で構築および管理する必要があります。
- 新しい PVC オブジェクトへの参照を使って新しいアプリケーションを再デプロイするときに、アプリケーションでダウンタイムが発生する可能性があります。
移行
次のコマンドを実行して、既存の PV の
ReclaimPolicy
を Delete から Retain に更新します。kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
pvName を、選んだ PersistentVolume の名前に置き換えます。 または、複数の PV の reclaimPolicy を更新する場合は、patchReclaimPVs.sh という名前のファイルを作成して、次のコードをコピーします。
#!/bin/bash # Patch the Persistent Volume in case ReclaimPolicy is Delete NAMESPACE=$1 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIMPOLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" echo "Reclaim Policy for Persistent Volume $PV is $RECLAIMPOLICY" if [[ $RECLAIMPOLICY == "Delete" ]]; then echo "Updating ReclaimPolicy for $pv to Retain" kubectl patch pv $PV -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' fi fi done
namespace
パラメーターでクラスターの名前空間./PatchReclaimPolicy.sh <namespace>
を指定して、スクリプトを実行します。次のコマンドを実行し、名前空間内のすべての PVC の一覧を、creationTimestamp で並べ替えて取得します。
--namespace
引数で実際のクラスターの名前空間を指定して、名前空間を設定します。kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage
このステップは、多数の PV を移行する必要があり、一度にいくつか移行したい場合に役立ちます。 このコマンドを実行すると、特定の期間内に作成された PVC を特定できます。 CreatePV.sh スクリプトを実行するとき、パラメーターのうちの 2 つは開始時刻と終了時刻であり、その期間内の PVC だけを移行できます。
CreatePV.sh という名前のファイルを作成して、次のコードをコピーします。 スクリプトでは次を実行します。
- ストレージ クラス
storageClassName
の名前空間内のすべての PersistentVolumes に対し、existing-pv-csi
という名前で新しい PersistentVolume を作成します。 - 新しい PVC 名を
existing-pvc-csi
として構成します。 - 指定した PV 名で新しい PVC を作成します。
#!/bin/bash #kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage # TimeFormat 2022-04-20T13:19:56Z NAMESPACE=$1 FILENAME=$(date +%Y%m%d%H%M)-$NAMESPACE EXISTING_STORAGE_CLASS=$2 STORAGE_CLASS_NEW=$3 STARTTIMESTAMP=$4 ENDTIMESTAMP=$5 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PVC_CREATION_TIME=$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.metadata.creationTimestamp}') if [[ $PVC_CREATION_TIME >= $STARTTIMESTAMP ]]; then if [[ $ENDTIMESTAMP > $PVC_CREATION_TIME ]]; then PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" STORAGECLASS="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.storageClassName}')" echo $PVC RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" if [[ $RECLAIM_POLICY == "Retain" ]]; then if [[ $STORAGECLASS == $EXISTING_STORAGE_CLASS ]]; then STORAGE_SIZE="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.capacity.storage}')" SKU_NAME="$(kubectl get storageClass $STORAGE_CLASS_NEW -o jsonpath='{.parameters.skuname}')" DISK_URI="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.azureDisk.diskURI}')" PERSISTENT_VOLUME_RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" cat >$PVC-csi.yaml <<EOF apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: disk.csi.azure.com name: $PV-csi spec: accessModes: - ReadWriteOnce capacity: storage: $STORAGE_SIZE claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: $PVC-csi namespace: $NAMESPACE csi: driver: disk.csi.azure.com volumeAttributes: csi.storage.k8s.io/pv/name: $PV-csi csi.storage.k8s.io/pvc/name: $PVC-csi csi.storage.k8s.io/pvc/namespace: $NAMESPACE requestedsizegib: "$STORAGE_SIZE" skuname: $SKU_NAME volumeHandle: $DISK_URI persistentVolumeReclaimPolicy: $PERSISTENT_VOLUME_RECLAIM_POLICY storageClassName: $STORAGE_CLASS_NEW --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: $PVC-csi namespace: $NAMESPACE spec: accessModes: - ReadWriteOnce storageClassName: $STORAGE_CLASS_NEW resources: requests: storage: $STORAGE_SIZE volumeName: $PV-csi EOF kubectl apply -f $PVC-csi.yaml LINE="PVC:$PVC,PV:$PV,StorageClassTarget:$STORAGE_CLASS_NEW" printf '%s\n' "$LINE" >>$FILENAME fi fi fi fi fi done
- ストレージ クラス
名前空間内のすべての PersistentVolumes に対して新しい PersistentVolume を作成するには、次のパラメーターを指定してスクリプト CreatePV.sh を実行します。
namespace
- クラスターの名前空間sourceStorageClass
- ツリー内ストレージ ドライバー ベースの StorageClasstargetCSIStorageClass
- CSI ストレージ ドライバー ベースの StorageClass。プロビジョナーが disk.csi.azure.com または file.csi.azure.com に設定されている既定のストレージ クラスのいずれかを使用できます。 または、それら 2 つのプロビジョナーのいずれかに設定するのであれば、カスタム ストレージ クラスを作成できます。startTimeStamp
- PVC の作成時刻前の開始時間を yyyy-mm-ddthh:mm:ssz の形式で提供しますendTimeStamp
- 終了日時を yyyy-mm-ddthh:mm:ssz の形式で指定します。
./CreatePV.sh <namespace> <sourceIntreeStorageClass> <targetCSIStorageClass> <startTimestamp> <endTimestamp>
新しい PVC を使うようにアプリケーションを更新します。
動的ボリュームを作成する
このオプションを使うと、永続ボリューム要求から永続ボリュームを動的に作成できます。
このアプローチには次のような利点があります。
スナップショットで他のコピーを保持しながらすべての新しいオブジェクトが作成されるため、リスクが低くなります。
PV を個別に構築し、PVC マニフェストにボリューム名を追加する必要はありません。
評価すべき重要な考慮事項を次に示します。
このアプローチは低リスクですが、複数のオブジェクトを作成するのでストレージ コストが増えます。
新しいボリュームを作成している間は、アプリケーションを使用できません。
削除手順は慎重に実行する必要があります。 移行が完了し、アプリケーションが正常に検証されるまで、リソース グループに一時的なリソース ロックを適用できます。
スナップショットから新しいディスクが作成されたら、データの検証と確認を行います。
移行
先に進む前に、次のことを確認します。
データがディスクに書き込まれる前にメモリに書き込まれる特定のワークロードでは、アプリケーションを停止し、メモリ内のデータをディスクにフラッシュできるようにする必要があります。
次の YAML の例で示すように、
VolumeSnapshot
クラスが存在する必要があります。apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass metadata: name: custom-disk-snapshot-sc driver: disk.csi.azure.com deletionPolicy: Delete parameters: incremental: "false"
次のコマンドを実行し、指定した名前空間内のすべての PVC の一覧を、creationTimestamp で並べ替えて取得します。
--namespace
引数で実際のクラスターの名前空間を指定して、名前空間を設定します。kubectl get pvc --namespace <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage
このステップは、多数の PV を移行する必要があり、一度にいくつか移行したい場合に役立ちます。 このコマンドを実行すると、特定の期間内に作成された PVC を特定できます。 MigrateCSI.sh スクリプトを実行するとき、パラメーターのうちの 2 つは開始時刻と終了時刻であり、その期間内の PVC だけを移行できます。
MigrateToCSI.sh という名前のファイルを作成して、次のコードをコピーします。 スクリプトでは次を実行します。
- Azure CLI を使ってディスクの完全なスナップショットを作成します
VolumesnapshotContent
を作成しますVolumeSnapshot
を作成しますVolumeSnapshot
から新しい PVC を作成します<namespace>-timestamp
というファイル名で新しいファイルを作成します。このファイルには、クリーンアップする必要があるすべての古いリソースの一覧が格納されます。
#!/bin/bash #kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage # TimeFormat 2022-04-20T13:19:56Z NAMESPACE=$1 FILENAME=$NAMESPACE-$(date +%Y%m%d%H%M) EXISTING_STORAGE_CLASS=$2 STORAGE_CLASS_NEW=$3 VOLUME_STORAGE_CLASS=$4 START_TIME_STAMP=$5 END_TIME_STAMP=$6 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PVC_CREATION_TIME=$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.metadata.creationTimestamp}') if [[ $PVC_CREATION_TIME > $START_TIME_STAMP ]]; then if [[ $END_TIME_STAMP > $PVC_CREATION_TIME ]]; then PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" STORAGE_CLASS="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.storageClassName}')" echo $PVC RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" if [[ $STORAGE_CLASS == $EXISTING_STORAGE_CLASS ]]; then STORAGE_SIZE="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.capacity.storage}')" SKU_NAME="$(kubectl get storageClass $STORAGE_CLASS_NEW -o jsonpath='{.parameters.skuname}')" DISK_URI="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.azureDisk.diskURI}')" TARGET_RESOURCE_GROUP="$(cut -d'/' -f5 <<<"$DISK_URI")" echo $DISK_URI SUBSCRIPTION_ID="$(echo $DISK_URI | grep -o 'subscriptions/[^/]*' | sed 's#subscriptions/##g')" echo $TARGET_RESOURCE_GROUP PERSISTENT_VOLUME_RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" az snapshot create --resource-group $TARGET_RESOURCE_GROUP --name $PVC-$FILENAME --source "$DISK_URI" --subscription ${SUBSCRIPTION_ID} SNAPSHOT_PATH=$(az snapshot list --resource-group $TARGET_RESOURCE_GROUP --query "[?name == '$PVC-$FILENAME'].id | [0]" --subscription ${SUBSCRIPTION_ID}) SNAPSHOT_HANDLE=$(echo "$SNAPSHOT_PATH" | tr -d '"') echo $SNAPSHOT_HANDLE sleep 10 # Create Restore File cat <<EOF >$PVC-csi.yml apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotContent metadata: name: $PVC-$FILENAME spec: deletionPolicy: 'Delete' driver: 'disk.csi.azure.com' volumeSnapshotClassName: $VOLUME_STORAGE_CLASS source: snapshotHandle: $SNAPSHOT_HANDLE volumeSnapshotRef: apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot name: $PVC-$FILENAME namespace: $1 --- apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot metadata: name: $PVC-$FILENAME namespace: $1 spec: volumeSnapshotClassName: $VOLUME_STORAGE_CLASS source: volumeSnapshotContentName: $PVC-$FILENAME --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: csi-$PVC namespace: $1 spec: accessModes: - ReadWriteOnce storageClassName: $STORAGE_CLASS_NEW resources: requests: storage: $STORAGE_SIZE dataSource: name: $PVC-$FILENAME kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io EOF kubectl create -f $PVC-csi.yml LINE="OLDPVC:$PVC,OLDPV:$PV,VolumeSnapshotContent:volumeSnapshotContent-$FILENAME,VolumeSnapshot:volumesnapshot$FILENAME,OLDdisk:$DISK_URI" printf '%s\n' "$LINE" >>$FILENAME fi fi fi fi done
ディスク ボリュームを移行するには、次のパラメーターを指定してスクリプト MigrateToCSI.sh を実行します。
namespace
- クラスターの名前空間sourceStorageClass
- ツリー内ストレージ ドライバー ベースの StorageClasstargetCSIStorageClass
- CSI ストレージ ドライバー ベースの StorageClassvolumeSnapshotClass
- ボリューム スナップショット クラスの名前。 たとえば、「custom-disk-snapshot-sc
」のように入力します。startTimeStamp
- 開始日時を yyyy-mm-ddthh:mm:ssz の形式で指定します。endTimeStamp
- 終了日時を yyyy-mm-ddthh:mm:ssz の形式で指定します。
./MigrateToCSI.sh <namespace> <sourceStorageClass> <TargetCSIstorageClass> <VolumeSnapshotClass> <startTimestamp> <endTimestamp>
新しい PVC を使うようにアプリケーションを更新します。
ツリー内 PVC/PV、VolumeSnapshot、VolumeSnapshotContent などの古いリソースを手動で削除します。 そうしないと、ツリー内 PVC/PC オブジェクトとスナップショット オブジェクトを維持するコストが余分に発生します。
ファイル共有ボリュームを移行する
ツリー内から CSI への移行は、静的ボリュームの作成によってサポートされます。
- ツリー内ストレージ クラスを使って元の構成をクリーンアップする必要はありません。
- Kubernetes PV/PVC の論理的な削除のみを実行し、実際の物理データは削除しないので、低リスクです。
- ファイル共有などの追加の Azure オブジェクトを作成する必要がないため、追加コストは発生しません。
移行
次のコマンドを実行して、既存の PV の
ReclaimPolicy
を Delete から Retain に更新します。kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
pvName を、選んだ PersistentVolume の名前に置き換えます。 または、複数の PV の reclaimPolicy を更新する場合は、patchReclaimPVs.sh という名前のファイルを作成して、次のコードをコピーします。
#!/bin/bash # Patch the Persistent Volume in case ReclaimPolicy is Delete namespace=$1 i=1 for pvc in $(kubectl get pvc -n $namespace | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else pv="$(kubectl get pvc $pvc -n $namespace -o jsonpath='{.spec.volumeName}')" reclaimPolicy="$(kubectl get pv $pv -n $namespace -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" echo "Reclaim Policy for Persistent Volume $pv is $reclaimPolicy" if [[ $reclaimPolicy == "Delete" ]]; then echo "Updating ReclaimPolicy for $pv to Retain" kubectl patch pv $pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' fi fi done
namespace
パラメーターでクラスターの名前空間./PatchReclaimPolicy.sh <namespace>
を指定して、スクリプトを実行します。プロビジョナーが
file.csi.azure.com
に設定された新しいストレージ クラスを作成するか、CSI ファイル プロビジョナーを使用する既定の StorageClasses のいずれかを使用できます。次のコマンドを実行して、既存の PersistentVolumes から
secretName
とshareName
を取得します。kubectl describe pv pvName
新しい StorageClass と、ツリー内 PV の
shareName
とsecretName
を使って、新しい PV を作成します。 azurefile-mount-pv.yaml という名前のファイルを作成して、次のコードをコピーします。csi
の下にあるresourceGroup
、volumeHandle
、shareName
を更新します。 マウント オプションでは、fileMode と dirMode の既定値は 0777 です。fileMode
とdirMode
の既定値は 0777 です。apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: file.csi.azure.com name: azurefile spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-csi csi: driver: file.csi.azure.com readOnly: false volumeHandle: unique-volumeid # make sure volumeid is unique for every identical share in the cluster volumeAttributes: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, only set this when storage account is not in the same resource group as the cluster nodes shareName: aksshare nodeStageSecretRef: name: azure-secret namespace: default mountOptions: - dir_mode=0777 - file_mode=0777 - uid=0 - gid=0 - mfsymlinks - cache=strict - nosharesock - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
次のコードを使って、PersistentVolume を使用する PersistentVolumeClaim で azurefile-mount-pvc.yaml という名前のファイルを作成します。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile spec: accessModes: - ReadWriteMany storageClassName: azurefile-csi volumeName: azurefile resources: requests: storage: 5Gi
kubectl
コマンドを使って PersistentVolume を作成します。kubectl apply -f azurefile-mount-pv.yaml
kubectl
コマンドを使って PersistentVolumeClaim を作成します。kubectl apply -f azurefile-mount-pvc.yaml
次のコマンドを実行し、PersistentVolumeClaim が作成されて PersistentVolume にバインドされていることを確認します。
kubectl get pvc azurefile
出力結果は、以下のようになります。
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE azurefile Bound azurefile 5Gi RWX azurefile 5s
PersistentVolumeClaim を参照してポッドを更新するようにコンテナーの仕様を更新します。 たとえば、次のコードをコピーし、azure-files-pod.yaml という名前のファイルを作成します。
... volumes: - name: azure persistentVolumeClaim: claimName: azurefile
ポッドの仕様をそのままで更新することはできません。 次の
kubectl
コマンドを使ってポッドを削除してから作り直します。kubectl delete pod mypod
kubectl apply -f azure-files-pod.yaml
次のステップ
- ストレージのベスト プラクティスの詳細については、「Azure Kubernetes Service のストレージとバックアップに関するベスト プラクティス」を参照してください。
- 新しく移行された CSI ドライバー ベースの PV を、AKS の Azure Backup を使用してバックアップすることで保護します。
Azure Kubernetes Service