Amazon Web Services ブログ
Kubernetes における永続ストレージ
この記事は Persistent storage for Kubernetes (記事公開日: 2022 年 11 月 22 日) を翻訳したものです。
ステートフルアプリケーションを適切に実行するためには、データが永続化され取得できることが必要です。Kubernetes を使用してステートフルアプリケーションを実行する場合、コンテナ、Pod、ノードのクラッシュや終了に関係なく、状態を永続化する必要があります。これには、永続ストレージ、すなわち、コンテナ、Pod、またはノードの生存期間を超えて存続するストレージが必要です。
このブログ記事では、Kubernetes 環境における永続ストレージのコンセプトと、Kubernetes の世界におけるストレージオプションについて取り上げます。そして、ホスト、Pod、コンテナレベルで障害や終了が発生した場合のデータ損失の懸念を軽減する、Kubernetes 上でのステートフルアプリケーションの設計と構築について説明します。
Kubernetes の永続ストレージは、特にストレージの初心者や、Kubernetes を使い始めようとしている人にとっては複雑なトピックです。そのため、私たちはこの 2 つのブログ記事シリーズを作成しました。最初に概念と用語について説明し、次に実用的なユースケースに踏み込みます。
- Part 1 [このブログ記事]: この最初のパートでは、Kubernetes の永続ストレージの概念を説明し、Amazon Elastic Kubernetes Service (Amazon EKS) と永続ストレージとして Amazon Elastic File System (Amazon EFS) を使用して、基本的なワークロードにこれらの概念を適用する方法について解説します。
- Part 2: Amazon EKS 上で Kubeflow を使用して実行される、永続ストレージを必要とする機械学習ワークロードの例を詳しく掘り下げます。
Kubernetes におけるデータの永続化
ステートフルアプリケーションを実行し、永続ストレージがない場合、データは Pod やコンテナのライフサイクルにに関連付けられます。Pod がクラッシュしたり終了したりすると、データは失われます。
このデータ損失を防ぎ、Kubernetes 上でステートフルなアプリケーションを実行するには、以下の 3 つのシンプルな要件に従うストレージが必要です。
- ストレージは、Pod のライフサイクルに依存しないようにする必要があります。
- ストレージは、Kubernetes クラスター内のすべての Pod とノードから利用可能である必要があります。
- ストレージは、クラッシュやアプリケーションの障害に関係なく、高い可用性を持つ必要があります。
Kubernetes ボリューム
Kubernetes にはいくつかの種類のストレージオプションが用意されていますが、そのすべてが永続的であるわけではありません。
エフェメラルストレージ
コンテナは一時的なファイルシステムを使用してファイルの読み取りと書き込みを行うことができます。しかし、このエフェメラルストレージはストレージに必要な 3 つの要件を満たしていません。コンテナがクラッシュした場合、一時的なファイルシステムは失われ、コンテナは再びまっさらな状態から開始されます。また、複数のコンテナで一時的なファイルシステムを共有することはできません。
エフェメラルボリューム
Kubernetes のエフェメラルボリュームは、一時的なファイルシステムが直面する両方の課題を解決します。エフェメラルボリュームの生存期間は Pod に連動します。これにより、コンテナの安全な再起動と、Pod 内のコンテナ間でのデータの共有が可能になります。しかし、Pod が削除されるとすぐにエフェメラルボリュームも削除されるため、依然として 3 つの要件を満たしていません。
一時的なファイルシステムはコンテナのライフサイクルに連動し、エフェメラルボリュームは Pod のライフサイクルに連動します。
ストレージから Pod を切り離す: 永続ボリューム
Kubernetes は永続ボリューム (Persistent Volume) もサポートしています。Persistent Volume を使用すると、アプリケーション、コンテナ、Pod、ノード、あるいはクラスター自体のライフサイクルに関係なく、データが永続化されます。Persistent Volume は、先に説明した 3 つの要件を満たします。
Persistent Volume (PV) オブジェクトは、アプリケーションデータを永続化するために使用されるストレージボリュームを表します。PV には、Kubernetes Pod のライフサイクルとは異なる、独自のライフサイクルがあります。
PV は基本的に 2 つの異なるもので構成されいます。
PersistentVolume
と呼ばれるバックエンドストレージ技術- ボリュームのマウント方法を Kubernetes に指示するアクセスモード
バックエンドストレージ技術
PV は抽象的なコンポーネントであり、実際の物理的なストレージはどこかから提供される必要があります。以下はいくつかの例です。
csi
: Container Storage Interface (CSI) → (例: Amazon EFS、Amazon EBS、 Amazon FSx など)iscsi
: iSCSI (SCSI over IP) ストレージlocal
: ノードにマウントされたローカルストレージデバイスnfs
: Network File System (NFS) ストレージ
Kubernetes は汎用性が高く、さまざまなタイプの PV をサポートしています。Kubernetes は基盤となるストレージの内部構造を気にしません。実際のストレージへのインターフェースとして PV コンポーネントを提供するだけです。
PV には 3 つの大きなメリットがあります。
- PV は Pod のライフサイクルにバインドされていません。PV オブジェクトにアタッチされた Pod を削除しても、PV は存続します。
- Pod がクラッシュした場合にも前述の記述は有効です。すなわち、PV オブジェクトは Pod がクラッシュしても PV は存続し、クラスターから削除されません。
- PV はクラスター全体で利用可能です。クラスター内の任意のノードで動作している任意の Pod にアタッチできます。
さまざまなバックエンドストレージ技術には、それぞれ独自のパフォーマンス特性とトレードオフがあります。このため、本番環境の Kubernetes では、アプリケーションに応じてさまざなタイプの PV が使用されます。
アクセスモード
アクセスモードは PV 作成時に設定され、ボリュームのマウント方法を Kubernetes に伝えます。Persistent Volume は以下の 3 つのアクセスモードをサポートします。
ReadWriteOnce
: ボリュームは、同時に 1 つのノードのみによる読み取り/書き込みが可能です。ReadOnlyMany
: ボリュームは、多くのノードによる読み取り専用モードを同時に許可します。ReadWriteMany
: ボリュームは、同時に複数のノードによる読み取り/書き込みが可能です。
訳注: 他に
ReadWriteOncePod
というアクセスモードがあり、Kubernetes 1.27 からベータ版となっています。
すべての PersistentVolume
タイプがすべてのアクセスモードをサポートしているわけではありません。
永続ボリューム要求 (Persistent Volume Claim)
PersistentVolume
(PV) は、実際のストレージボリュームを表します。Kubernetes には、PV を Pod にアタッチするために必要な追加の抽象化レイヤーである PersistentVolumeClaim
(PVC) があります。
PV は実際のストレージボリュームを表し、PVC は実際のストレージを取得するために Pod が行うストレージのリクエストを表します。
PV と PVC の分離は、Kubernetes 環境においては 2 つの異なる役割が存在するという考えに関連しています。
- Kubernetes 管理者: クラスターの保守および運用し、永続ストレージなどの計算リソースの追加を行います。
- Kubernetes アプリケーション開発者: アプリケーションの開発とデプロイを行います。
簡単に言えば、アプリケーション開発者は管理者が提供する計算リソースを消費します。Kubernetes は、PV オブジェクトはクラスター管理者のスコープに属し、PVC オブジェクトはアプリケーション開発者のスコープに属するという考えに基づいて構築されています。
基本的に、Pod は PV オブジェクトを直接マウントできません。明示的に要求する必要があります。そしてその要求動作は、PVC オブジェクトを作成して Pod にアタッチすることで実現します。これが、この抽象化レイヤーが存在する唯一の理由です。PVC と PV は 1 対 1 の対応関係にあります (PV は 1 つの PVC にしか関連付けることができません) 。
このブログ記事では、Pod に永続ストレージをアタッチするこのプロセスをデモします。しかし、その前に、CSI ドライバーに関する背景を説明する必要があります。
Container Storage Interface (CSI) ドライバー
Container Storage Interface (CSI) は、さまざまなストレージソリューションを Kubernetes において使いやすくするために設計された抽象化です。さまざまなストレージベンダーは、CSI 標準を実装する独自のドライバーを開発し、ストレージソリューションが (基盤となるストレージソリューションの内部構造に関係なく) Kubernetes と連携できるするようにすることができます。AWS には、Amazon EBS、Amazon EFS、Amazon FSx for Lustre などのための CSI プラグインがあります。
静的プロビジョニング
「永続ボリューム要求 (Persistent Volume Claim)」セクションで説明した内容では、まず管理者が 1 つまたは複数の PV を作成し、次にアプリケーション開発者が PVC を作成します。これは静的プロビジョニングと呼ばれます。Kubernetes で PV と PVC を手動で作成する必要があるため、静的です。規模が大きくなると、特に数百の PV や PVC を管理する場合、この方法では管理が困難になる可能性があります。
例えば、Amazon EFS ファイルシステムを作成して PV オブジェクトとしてマウントしたいとします。静的プロビジョニングを使用する場合、次のようにする必要があります。
- Kubernetes 管理者のタスク
- Amazon EFS ファイルシステムを作成します。
- ファイルシステム ID をコピーして PV の YAML 定義ファイルに貼り付けます。
- YAML ファイルを使用して PV を作成します。
- Kubernetes アプリケーション開発者のタスク
- この PV を要求する PVC を作成します。
- Pod の YAML 定義ファイルにの Pod オブジェクトに PVC をマウントします。
この方法は機能しますが、規模が大きくなると時間がかかります。
動的プロビジョニング
動的プロビジョニングでは、PV オブジェクトを作成する必要はありません。その代わり、PVC を作成すると自動的に PV が作成されます。Kubernetes はストレージクラス (Storage Class) と呼ばれる別のオブジェクトを使ってこれを行います。
Storage Class は、コンテナアプリケーションに使用されるバックエンドの永続ストレージ (Amazon EFS ファイルストレージ、Amazon EBS ブロックストレージなど) のクラスを定義する抽象化です。
Storage Class には基本的に次の 2 つのものが含まれます。
- Name: これは、Storage Class オブジェクトを一意に識別する名前です。
- Provisioner: 基盤となるストレージ技術を定義します。例えば、Amazon EFS の場合は
efs.csi.aws.com
、Amazon EBS の場合はebs.csi.aws.com
となります。
Kubernetes が非常に多くの異なるストレージ技術を扱うことができるのは、Storage Class オブジェクトのおかげです。Pod の観点からは、それが EFS ファイルシステムであろうと、EBS ボリュームであろうと、NFS ドライブであろうと、その他何であろうと、Pod には PVC オブジェクトしか見えません。実際のストレージ技術を扱うすべての基盤となるロジックは、Storage Class オブジェクトが使用する Provisioner によって実装されます。
Amazon EKS と Amazon EFS を使用した静的プロビジョニングと動的プロビジョニングのデモ
では、学んだことをすべて実践してみましょう。GitHub のこのページを参考に、このデモセクションのための作業環境をセットアップしてください。
訳注: 日本語翻訳にあたり、EKS 1.28 においてデモが動作することを検証しています。バージョンを適宜置き換えて実施してください。
次のコードスニペットでは、5 つのノードを持つ Amazon EKS クラスターを確認できます。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-12-218.us-west-1.compute.internal Ready <none> 2d3h v1.21.5-eks-9017834
ip-192-168-24-116.us-west-1.compute.internal Ready <none> 2d3h v1.21.5-eks-9017834
ip-192-168-46-22.us-west-1.compute.internal Ready <none> 2d3h v1.21.5-eks-9017834
ip-192-168-63-240.us-west-1.compute.internal Ready <none> 2d3h v1.21.5-eks-9017834
ip-192-168-7-195.us-west-1.compute.internal Ready <none> 2d3h v1.21.5-eks-9017834
Kubernetes クラスターの永続ストレージとして Amazon EFS ファイルシステムを使用します。そのためには、まず Amazon EFS CSI ドライバーをインストールする必要があります。
訳注: こちらのドキュメントに従い、EFS CSI ドライバーをインストールしてください。2023 年 8 月のアップデートで、Amazon EFS CSI ドライバーは EKS アドオンとしてインストールすることができるようになりました。
Amazon EFS を使用した静的プロビジョニング
訳注: マネジメントコンソールから EFS ファイルシステムを作成する場合、事前にセキュリティグループを作成し、EFS ファイルシステムのマウントターゲットと関連付ける必要があります。eksctl によって作成された VPC (
eksctl-efsworkshop-eksctl-cluster/VPC
) に、セキュリティグループを作成し、クラスターセキュリティグループ (eks-cluster-sg-efsworkshop-eksctl-XXXXXXXX
) から TCP 2049 ポートへのアクセスを許可するインバウンドルールを作成してください。
まず AWS マネジメントコンソールで Amazon EFS ファイルシステム (myEFS1
) を作成しましょう。次のステップで PV を作成する際に必要になるので、FileSystemId
を控えておきましょう。
すべてをデフォルトのままにして、ファイルシステムを作成します。
訳注: 「カスタマイズ」を選択し、VPC は
eksctl-efsworkshop-eksctl-cluster/VPC
を選択し、マウントターゲットのセキュリティグループは事前に作成したセキュリティグループを選択してください。
次に、PV のマニフェストファイルを作成し、新しく作成したファイルシステムの FileSystemId
を指定します。
#pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: efs-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: ""
persistentVolumeReclaimPolicy: Retain
csi:
driver: efs.csi.aws.com
volumeHandle: fs-073d77123471b2917
この pv.yaml
ファイルにあるように、spec.capacity.storage
のサイズを 5 ギビバイト (GiB) としました。これは、PV を作成する際に必須であり、Kubernetes を満足させるための単なるプレースホルダー値です。バックエンドには Amazon EFS ファイルシステムを使用しています。これは完全に伸縮性があり、スケーラブルなファイルシステムです。使用量に応じて自動的にスケールアップまたはスケールダウンされるため、容量を心配する必要はありません。
$ kubectl apply -f pv.yaml
persistentvolume/efs-pv created
$ kubectl get pv efs-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
efs-pv 5Gi RWO Retain Available 45s
PV の ステータスは Available
ですが、まだどの Persistent Volume Claim (PVC) ともバインドされていません。次に、PVC を作成します。
#pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: ""
resources:
requests:
storage: 5Gi
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/efs-claim created
では、PV と PVC のステータスを確認してみましょう。
$ kubectl get pv efs-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
efs-pv 5Gi RWO Retain Bound default/efs-claim 15m
$ kubectl get pvc efs-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
efs-claim Bound efs-pv 5Gi RWO 103s
PV のステータスが Available
から Bound
に変わりました。これは、Kubernetes が PVC にマッチするボリュームを見つけることができ、ボリュームがバインドされたことを意味します。
ここで別の PVC を作成しようとすると、PV がもう残っていないため失敗します (PV は 1 つの PVC にのみバインドできます) 。そこで、動的プロビジョニングが役に立ちます。動的プロビジョニングに移る前に、この PVC を使ってサンプルアプリケーション (efs-app
) を作成し、データがどのように永続化されるかを確認しましょう。
#pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: efs-app
spec:
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 2; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
$ kubectl apply -f pod.yaml
pod/efs-app created
kubectl を使用して、データが Amazon EFS ファイルシステムに書き込まれたことが確認できます。
$ kubectl exec -ti efs-app -- tail -f /data/out.txt
Mon Mar 21 23:33:05 UTC 2022
Mon Mar 21 23:33:07 UTC 2022
Mon Mar 21 23:33:09 UTC 2022
以下は静的プロビジョニングのまとめです。
Amazon EFS を使用した動的プロビジョニング
Amazon EFS CSI ドライバーは、動的プロビジョニングと静的プロビジョニングの両方をサポートしています。EFS の場合、動的プロビジョニングでは各 PV のアクセスポイントが作成されます。つまり、手動で Amazon EFS ファイルシステムを作成し、それを Storage Class パラメータの入力として提供する必要があります。
デフォルトでは、動的プロビジョニングによって作成された各アクセスポイントは、EFS 上の異なるディレクトリにファイルを書き込み、各アクセスポイントは異なる POSIX uid/gid を使用して EFS にファイルを書き込みます。これにより、複数のアプリケーションが同じ EFS ファイルシステムを永続ストレージとして使用しながら、アプリケーション間の分離が実現します。
それでは、動的プロビジョニングに使用する新しい Amazon EFS ファイルシステム (myEFS2
) を作成しましょう。
訳注: 静的プロビジョニングで実施した手順と同様に、「カスタマイズ」を選択し、VPC は
eksctl-efsworkshop-eksctl-cluster/VPC
を選択し、マウントターゲットのセキュリティグループは事前に作成したセキュリティグループを選択してください。
次に、Storage Class を作成し、新しく作成されたファイルシステムの FileSystemId
を指定します。
#sc.yaml
----
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-026bb4e33bea77857
directoryPerms: "700"
Storage Class を作成して確認します。
$ kubectl apply -f sc.yaml
storageclass.storage.k8s.io/efs-sc created
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
efs-sc efs.csi.aws.com Delete Immediate false 4s
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 2d5h
「動的プロビジョニング」セクションで述べたように、アプリケーションをデプロイする前に PV を作成する必要はありません。したがって、先に進んで PVC と Pod を作成できます。
#pvc_pod_1.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim-1
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
name: efs-app-1
spec:
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim-1
PVC と Pod をデプロイしましょう。
$ kubectl apply -f pvc_pod_1.yaml
persistentvolumeclaim/efs-claim-1 created
pod/efs-app-1 created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
efs-claim-1 Bound pvc-82da05b5-ff85-40a5-8135-50428480fd22 5Gi RWX efs-sc 89s
$ kubectl get pv | grep efs-sc
pvc-7994fdce-5711-4346-aefb-85e10155ec7c 5Gi RWX Delete
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
efs-app-1 1/1 Running 0 95s
$ kubectl exec -ti efs-app-1 -- tail -f /data/out
Tue Mar 22 00:38:08 UTC 2022
Tue Mar 22 00:38:13 UTC 2022
Tue Mar 22 00:38:18 UTC 2022
efs-app-1
が正常に実行され、EFS CSI ドライバーによって PV が自動的に作成されたことが確認できました。AWS マネジメントコンソールでは、それぞれのアクセスポイントが確認できます。
このプロセスを繰り返して別のアプリケーション (efs-app-2
) を作成すると、自動的に別のアクセスポイントが作成されます。PV について心配する必要はありません。
以下は、動的プロビジョニングのまとめです。
クリーンアップ
すべてのエクササイズが終わったら、すべてのリソースを削除しましょう。
- すべての Kubernetes リソース (PV、PVC、Pod、Storage Class など) を削除します。
$ kubectl delete -f pod.yaml
$ kubectl delete -f pvc.yaml
$ kubectl delete -f pv.yaml
$ kubectl delete -f pvc_pod_1.yaml
$ kubectl delete -f sc.yaml
- AWS マネジメントコンソールまたは AWS CLI で EFS ファイルシステム (
myEFS1
とmyEFS2
) を削除します。 - EKS クラスターを削除 (
eksctl delete cluster efsworkshop-eksctl
) します。
訳注: KMS キー (
efsworkshop
) 、Cloud9 環境 (efsworkshop
) 、IAM ロール (efsworkshop-admin
) も削除してください。
まとめ
このブログ記事では、Kubernetes の永続ストレージの基本原則について説明しました。Persistent Volume を使用することで、Pod のクラッシュや終了に関係なくデータが永続化されるステートフルなアプリケーションを Kubernetes 上に作成できます。Persistent Volume は、静的プロビジョニングまたは動的プロビジョニングのいずれかを使用してプロビジョニングできます。基盤となるストレージとして Amazon EFS を使用して、EKS クラスター上で両方のデモを行いました。
基本的なことはすべて学んだので、次はより実用的なユースケースに移ります。このブログ記事シリーズの Part 2 では、Kubeflow を使って機械学習ワークロードを実行するために永続ストレージを利用します。
このブログ記事をお読みいただきありがとうございます。コンテナでの EFS の使用に関するその他のチュートリアルについては、amazon-efs-developer-zone GitHub リポジトリにアクセスしてください。
翻訳はプロフェッショナルサービスの杉田が担当しました。原文はこちらです。