メインコンテンツまでスキップ

S3 Filesを使用した静的プロビジョニング

S3 FilesのストレージクラスとS3 Filesが静的プロビジョニングを使用する理由を理解したので、事前に作成されたS3ファイルシステム用にPersistent Volumeを静的にプロビジョニングし、UIコンポーネントをマウントするように変更しましょう。

S3ファイルシステムは、バージョニングが有効なS3バケットにリンクされた状態でプロビジョニングされています。ファイルシステムには、マウントターゲットと、ポート2049でNFSトラフィックを許可するインバウンドルールを含む必要なセキュリティグループが含まれています。静的PersistentVolumeに必要なIDを取得しましょう:

~$export S3_FILES_ID=$(aws s3files list-file-systems \
--query "fileSystems[?starts_with(bucket, 'arn:aws:s3:::${EKS_CLUSTER_NAME}-s3-files-')].fileSystemId | [0]" \
--output text)
~$echo $S3_FILES_ID
fs-0123456789abcdef0

静的プロビジョニングでは、セットとして機能する3つのオブジェクトを定義します:

  1. パラメータなしのStorageClass(s3-files-sc)で、ボリュームとクレームをバインドするためだけに使用されます
  2. 既存のS3ファイルシステムを直接指し示すPersistentVolume(s3-files-pv)
  3. その特定のPersistentVolumeにバインドするPersistentVolumeClaim(s3-files-claim)

静的プロビジョニングのStorageClassは、PersistentVolumeとPersistentVolumeClaimの間のバインディングラベルとしてのみ機能します。意図的にパラメータがないため、ドライバーがオンデマンドで何かをプロビジョニングしようとすることはありません:

~/environment/eks-workshop/modules/fundamentals/storage/s3-files/pv/s3filesstorageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: s3-files-sc
# No parameters and no dynamic provisioning: this StorageClass exists only to
# bind the statically-provisioned PersistentVolume to its PersistentVolumeClaim.
# The S3 file system is created ahead of time, so the EFS CSI driver does not
# provision anything on demand here.
provisioner: efs.csi.aws.com
A

provisionerパラメータをefs.csi.aws.comに設定します - S3 FilesはEFS CSIドライバーを使用するため、EFSに使用されるものと同じプロビジョナーです

B

parametersがないことに注意してください: 動的プロビジョニングとは異なり、ドライバーにファイルシステムやアクセスポイントの作成を要求していません

では、PersistentVolumeとPersistentVolumeClaimを見てみましょう:

~/environment/eks-workshop/modules/fundamentals/storage/s3-files/pv/s3filespv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: s3-files-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: s3-files-sc
csi:
driver: efs.csi.aws.com
volumeHandle: s3files:${S3_FILES_ID}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: s3-files-claim
namespace: ui
spec:
accessModes:
- ReadWriteMany
storageClassName: s3-files-sc
resources:
requests:
storage: 5Gi
volumeName: s3-files-pv
A

csi.driverはefs.csi.aws.comで、S3 Filesも提供するEFS CSIドライバーです

B

volumeHandleは、ファイルシステムIDの前にs3files:プレフィックスを使用します(例:s3files:fs-0123456789abcdef0)。このプレフィックスは、ドライバーにEFSファイルシステムではなくS3ファイルシステムをマウントするよう指示します。$S3_FILES_ID環境変数がここに注入されます。

C

再利用ポリシーはRetainなので、クレームを削除しても基礎となるS3ファイルシステムやそのデータは削除されません

PersistentVolumeClaimはvolumeName: s3-files-pvを設定しているため、動的プロビジョニングをトリガーするのではなく、定義したボリュームに正確にバインドされます。

kustomizationを適用し、StorageClass、PersistentVolume、PersistentVolumeClaimを作成します:

~$kubectl kustomize ~/environment/eks-workshop/modules/fundamentals/storage/s3-files/pv \
| envsubst | kubectl apply -f-
storageclass.storage.k8s.io/s3-files-sc created
persistentvolume/s3-files-pv created
persistentvolumeclaim/s3-files-claim created

PersistentVolumeは事前に作成されているため、クレームに即座にバインドされます:

~$kubectl get pv
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
s3-files-pv   5Gi        RWX            Retain           Bound    ui/s3-files-claim    s3-files-sc             10s

PersistentVolumeClaim(PVC)の詳細を確認してみましょう:

~$kubectl describe pvc s3-files-claim -n ui
Name:          s3-files-claim
Namespace:     ui
StorageClass:  s3-files-sc
Status:        Bound
Volume:        s3-files-pv
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      5Gi
Access Modes:  RWX
VolumeMode:    Filesystem
Used By:       <none>
Events:        <none>

クレームが静的に定義したs3-files-pvボリュームにBoundされていることに注目してください - 外部プロビジョナーが何かを作成する必要はありませんでした。

次に、S3 Files PVCを参照するようにUIコンポーネントを更新します:

~/environment/eks-workshop/modules/fundamentals/storage/s3-files/deployment/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ui
spec:
replicas: 2
template:
spec:
initContainers:
- name: fix-permissions
image: public.ecr.aws/docker/library/busybox:1.37
command: ["sh", "-c", "chown 1000:1000 /s3files"]
volumeMounts:
- name: s3filesvolume
mountPath: /s3files
securityContext:
runAsUser: 0
containers:
- name: ui
volumeMounts:
- name: s3filesvolume
mountPath: /s3files
env:
- name: RETAIL_UI_PRODUCT_IMAGES_PATH
value: /s3files
volumes:
- name: s3filesvolume
persistentVolumeClaim:
claimName: s3-files-claim

このパッチは、小さなfix-permissions initコンテナも追加します。uiコンテナは非rootユーザー1000として実行されますが、S3 Filesボリュームはrootが所有した状態でマウントされます。S3 FilesはNFSベースのファイルシステムを使用するため、PodのfsGroup設定は、Amazon EBSのようなブロックストレージの場合のようにマウントされたボリュームの所有権を自動的に変更しません。initコンテナはrootとして実行され、uiコンテナが起動する前にマウントをユーザー1000にchownするため、アプリケーションが/s3filesに書き込めるようになります。これがないと、このラボの後半での書き込みのような操作が権限エラーで失敗します。

次のコマンドでこれらの変更を適用します:

~$kubectl apply -k ~/environment/eks-workshop/modules/fundamentals/storage/s3-files/deployment
namespace/ui unchanged
serviceaccount/ui unchanged
configmap/ui unchanged
service/ui unchanged
deployment.apps/ui configured
~$kubectl rollout status --timeout=130s deployment/ui -n ui

デプロイメントのvolumeMountsを確認してみましょう。s3filesvolumeという名前の新しいボリュームが/s3filesにマウントされていることに注目してください:

~$kubectl get deployment -n ui \
-o yaml | yq '.items[].spec.template.spec.containers[].volumeMounts'
- mountPath: /s3files
  name: s3filesvolume
- mountPath: /tmp
  name: tmp-volume

この時点で、S3ファイルシステムは正常にマウントされていますが、現在は空です:

~$POD_1=$(kubectl -n ui get pods -l app.kubernetes.io/instance=ui -o jsonpath='{.items[0].metadata.name}')
~$kubectl exec --stdin $POD_1 -n ui -- bash -c 'ls /s3files/'

Kubernetes Jobを使用して、S3 Filesボリュームに画像を取り込みましょう:

~$export PVC_NAME="s3-files-claim"
~$cat ~/environment/eks-workshop/modules/fundamentals/storage/populate-images-job.yaml | envsubst | kubectl apply -f -
~$kubectl wait --for=condition=complete -n ui \
job/populate-images --timeout=300s

次に、UIコンポーネントのPodの1つを通じて/s3files内の現在のファイルをリストすることで、共有ストレージ機能を実証しましょう:

~$POD_1=$(kubectl -n ui get pods -l app.kubernetes.io/instance=ui -o jsonpath='{.items[0].metadata.name}')
~$kubectl exec --stdin $POD_1 -n ui -- bash -c 'ls /s3files/'
1ca35e86-4b4c-4124-b6b5-076ba4134d0d.jpg
4f18544b-70a5-4352-8e19-0d070f46745d.jpg
631a3db5-ac07-492c-a994-8cd56923c112.jpg
79bce3f3-935f-4912-8c62-0d2f3e059405.jpg
8757729a-c518-4356-8694-9e795a9b3237.jpg
87e89b11-d319-446d-b9be-50adcca5224a.jpg
a1258cd2-176c-4507-ade6-746dab5ad625.jpg
cc789f85-1476-452a-8100-9e74502198e0.jpg
d27cf49f-b689-4a75-a249-d373e0330bb5.jpg
d3104128-1d14-4465-99d3-8ab9267c687b.jpg
d4edfedb-dbe9-4dd9-aae8-009489394955.jpg
d77f9ae6-e9a8-4a3e-86bd-b72af75cbc49.jpg

S3 FilesはファイルシステムとS3バケットの間でデータを自動的に同期するため、基礎となるS3バケットにも画像が存在することを確認できます:

~$export S3_FILES_BUCKET_NAME=$(aws s3api list-buckets \
--query "Buckets[?starts_with(Name, '${EKS_CLUSTER_NAME}-s3-files-')].Name | [0]" --output text)
~$aws s3 ls $S3_FILES_BUCKET_NAME
                           PRE /
2025-07-09 14:43:36     102950 1ca35e86-4b4c-4124-b6b5-076ba4134d0d.jpg
2025-07-09 14:43:36     118546 4f18544b-70a5-4352-8e19-0d070f46745d.jpg
[...]

PRE /エントリは、S3 Filesがバケット内のファイルシステムのルートディレクトリを表現するために使用するゼロバイトのオブジェクトです - fix-permissions initコンテナがマウントルートに所有権を設定するときに作成されます。これは、S3 FilesがPOSIXファイルシステムをS3オブジェクトにマッピングする方法の通常の一部であり、エラーではなく、画像ファイルには影響しません。すべての画像ファイルは、バケットのトップレベルにそれと一緒に表示されます。

共有ストレージ機能をさらに実証するために、placeholder.jpgという新しい画像を作成し、最初のPodを通じてS3 Filesボリュームに追加しましょう:

~$POD_1=$(kubectl -n ui get pods -l app.kubernetes.io/instance=ui -o jsonpath='{.items[0].metadata.name}')
~$kubectl exec --stdin $POD_1 -n ui -- bash -c 'curl -sS -o /s3files/placeholder.jpg https://placehold.co/600x400/jpg?text=EKS+Workshop\\nPlaceholder'

次に、2番目のUI Podがこの新しく作成されたファイルにアクセスできることを確認し、S3 Filesストレージの共有性を実証します:

~$POD_2=$(kubectl -n ui get pods -l app.kubernetes.io/instance=ui -o jsonpath='{.items[1].metadata.name}')
~$kubectl exec --stdin $POD_2 -n ui -- bash -c 'ls /s3files/'
1ca35e86-4b4c-4124-b6b5-076ba4134d0d.jpg
4f18544b-70a5-4352-8e19-0d070f46745d.jpg
631a3db5-ac07-492c-a994-8cd56923c112.jpg
79bce3f3-935f-4912-8c62-0d2f3e059405.jpg
8757729a-c518-4356-8694-9e795a9b3237.jpg
87e89b11-d319-446d-b9be-50adcca5224a.jpg
a1258cd2-176c-4507-ade6-746dab5ad625.jpg
cc789f85-1476-452a-8100-9e74502198e0.jpg
d27cf49f-b689-4a75-a249-d373e0330bb5.jpg
d3104128-1d14-4465-99d3-8ab9267c687b.jpg
d4edfedb-dbe9-4dd9-aae8-009489394955.jpg
d77f9ae6-e9a8-4a3e-86bd-b72af75cbc49.jpg
placeholder.jpg      <----------------

ご覧のとおり、最初のPodを通じてファイルを作成したにもかかわらず、両方のPodが同じ共有S3ファイルシステムにアクセスしているため、2番目のPodもすぐにアクセスできます。

S3 Filesは、この新しいファイルも基礎となるS3バケットに自動的に同期し、S3 APIを通じてもアクセスできるようにします。

最後に、UIサービスを通じて画像にアクセスできることを確認しましょう:

~$LB_HOSTNAME=$(kubectl -n ui get service ui-nlb -o jsonpath='{.status.loadBalancer.ingress[*].hostname}{"\n"}')
~$echo "http://$LB_HOSTNAME/assets/img/products/placeholder.jpg"
http://k8s-ui-uinlb-647e781087-6717c5049aa96bd9.elb.us-west-2.amazonaws.com/assets/img/products/placeholder.jpg

ブラウザでURLにアクセスしてください:

http://k8s-ui-uinlb-647e781087-6717c5049aa96b...

Amazon S3 Filesが、Amazon EKS上で実行されているワークロードに永続的な共有ストレージを提供する方法を実証しました。このソリューションは、ファイルシステムのシンプルさとパフォーマンスとAmazon S3のスケーラビリティと耐久性を組み合わせることで、複数のPodが同じストレージボリュームに同時に読み書きできるようにし、データをS3バケットと同期させたままにします。