S3による永続的オブジェクトストレージ
前のステップでは、イメージオブジェクト用のステージングディレクトリを作成し、画像アセットをダウンロードしてS3バケットにアップロードすることで環境を準備しました。また、Mountpoint for Amazon S3 CSIドライバーをインストールして設定しました。ここでは、Mountpoint for Amazon S3 CSIドライバーが提供するPersistent Volume(PV)を使用するようにPodを接続することで、Amazon S3によって水平スケーリングと永続ストレージを備えた画像ホストアプリケーションを作成するという目標を完成させます。
まず、Persistent Volumeを作成し、デプロイメント内のuiコンテナがこのボリュームをマウントするように変更しましょう。
最初に、s3pvclaim.yamlファイルを調べて、そのパラメータと設定を理解しましょう:
apiVersion: v1
kind: PersistentVolume
metadata:
name: s3-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
mountOptions:
- allow-delete
- allow-other
- uid=1000
- gid=1000
- region=$AWS_REGION
csi:
driver: s3.csi.aws.com
volumeHandle: s3-csi-driver-volume
volumeAttributes:
bucketName: $BUCKET_NAME
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: s3-claim
namespace: ui
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 1Gi
volumeName: s3-pv
ReadWriteMany:同じS3バケットを複数のPodに読み書き用としてマウントすることを許可します
allow-delete:マウントされたバケットからオブジェクトを削除することをユーザーに許可します
allow-other:所有者以外のユーザーがマウントされたバケットにアクセスする ことを許可します
uid=:マウントされたバケット内のファイル/ディレクトリのユーザーID(UID)を設定します
gid=:マウントされたバケット内のファイル/ディレクトリのグループID(GID)を設定します
region= $AWS_REGION:S3バケットのリージョンを設定します
bucketNameはS3バケット名を指定します
- Kustomize Patch
- Deployment/ui
- Diff
apiVersion: apps/v1
kind: Deployment
metadata:
name: ui
spec:
replicas: 2
template:
spec:
containers:
- name: ui
volumeMounts:
- name: mountpoint-s3
mountPath: /mountpoint-s3
env:
- name: RETAIL_UI_PRODUCT_IMAGES_PATH
value: /mountpoint-s3
volumes:
- name: mountpoint-s3
persistentVolumeClaim:
claimName: s3-claim
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/created-by: eks-workshop
app.kubernetes.io/type: app
name: ui
namespace: ui
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/component: service
app.kubernetes.io/instance: ui
app.kubernetes.io/name: ui
template:
metadata:
annotations:
prometheus.io/path: /actuator/prometheus
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
labels:
app.kubernetes.io/component: service
app.kubernetes.io/created-by: eks-workshop
app.kubernetes.io/instance: ui
app.kubernetes.io/name: ui
spec:
containers:
- env:
- name: RETAIL_UI_PRODUCT_IMAGES_PATH
value: /mountpoint-s3
- name: JAVA_OPTS
value: -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom
- name: METADATA_KUBERNETES_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: METADATA_KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: METADATA_KUBERNETES_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
envFrom:
- configMapRef:
name: ui
image: public.ecr.aws/aws-containers/retail-store-sample-ui:1.2.1
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 45
periodSeconds: 20
name: ui
ports:
- containerPort: 8080
name: http
protocol: TCP
resources:
limits:
memory: 1.5Gi
requests:
cpu: 250m
memory: 1.5Gi
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /mountpoint-s3
name: mountpoint-s3
- mountPath: /tmp
name: tmp-volume
securityContext:
fsGroup: 1000
serviceAccountName: ui
volumes:
- name: mountpoint-s3
persistentVolumeClaim:
claimName: s3-claim
- emptyDir:
medium: Memory
name: tmp-volume
app.kubernetes.io/type: app
name: ui
namespace: ui
spec:
- replicas: 1
+ replicas: 2
selector:
matchLabels:
app.kubernetes.io/component: service
app.kubernetes.io/instance: ui
[...]
app.kubernetes.io/name: ui
spec:
containers:
- env:
+ - name: RETAIL_UI_PRODUCT_IMAGES_PATH
+ value: /mountpoint-s3
- name: JAVA_OPTS
value: -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom
- name: METADATA_KUBERNETES_POD_NAME
valueFrom:
[...]
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
+ - mountPath: /mountpoint-s3
+ name: mountpoint-s3
- mountPath: /tmp
name: tmp-volume
securityContext:
fsGroup: 1000
serviceAccountName: ui
volumes:
+ - name: mountpoint-s3
+ persistentVolumeClaim:
+ claimName: s3-claim
- emptyDir:
medium: Memory
name: tmp-volume
それでは、この設定を適用してアプリケーションを再デプロイしましょう:
namespace/ui unchanged
serviceaccount/ui unchanged
configmap/ui unchanged
service/ui unchanged
persistentvolume/s3-pv created
persistentvolumeclaim/s3-claim created
deployment.apps/ui configured
デプロイメントの進行状況を監視しましょう:
deployment "ui" successfully rolled out
ボリュームマウントを確認し、新しい/mountpoint-s3マウントポイントに注目しましょう:
- mountPath: /mountpoint-s3
name: mountpoint-s3
- mountPath: /tmp
name: tmp-volume
次に、新しく作成されたPersistentVolumeを調べましょう:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
s3-pv 1Gi RWX Retain Bound ui/s3-claim <unset> 2m31s
PersistentVolumeClaimの詳細を確認しましょう:
Name: s3-claim
Namespace: ui
StorageClass:
Status: Bound
Volume: s3-pv
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWX
VolumeMode: Filesystem
Used By: ui-9fbbbcd6f-c74vv
ui-9fbbbcd6f-vb9jz
Events: <none>
実行中のPodを確認しましょう:
NAME READY STATUS RESTARTS AGE
ui-9fbbbcd6f-c74vv 1/1 Running 0 2m36s
ui-9fbbbcd6f-vb9jz 1/1 Running 0 2m38s
それでは、Mountpoint for Amazon S3 CSIドライバーを使用した最終的なデプロイメント設定を調べましょう:
Name: ui
Namespace: ui
[...]
Containers:
ui:
Image: public.ecr.aws/aws-containers/retail-store-sample-ui:1.2.1
Port: 8080/TCP
Host Port: 0/TCP
Limits:
memory: 128Mi
Requests:
cpu: 128m
memory: 128Mi
[...]
Mounts:
/mountpoint-s3 from mountpoint-s3 (rw)
/tmp from tmp-volume (rw)
Volumes:
mountpoint-s3:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: s3-claim
ReadOnly: false
tmp-volume:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium: Memory
SizeLimit: <unset>
[...]
ここで、共有ストレージ機能をデモンストレーションしましょう。まず、UIコンポーネントのPodの1つを通じて/mountpoint-s3内の現在のファイルをリストアップします:
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バケットにアップロードした画像と一致していることがわかります。次に、placeholder.jpgという新しい画像を生成し、同じPodを通じてS3バケットに追加しましょう:
ストレージレイヤーの永続性と共有を確認するために、2番目のUIPodを使用して、作成したファイルを確認しましょう:
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 <----------------
最後に、S3バケット内の存在を確認しましょう:
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
2025-07-09 14:43:36 147820 631a3db5-ac07-492c-a994-8cd56923c112.jpg
2025-07-09 14:43:36 100117 79bce3f3-935f-4912-8c62-0d2f3e059405.jpg
2025-07-09 14:43:36 106911 8757729a-c518-4356-8694-9e795a9b3237.jpg
2025-07-09 14:43:36 113010 87e89b11-d319-446d-b9be-50adcca5224a.jpg
2025-07-09 14:43:36 171045 a1258cd2-176c-4507-ade6-746dab5ad625.jpg
2025-07-09 14:43:36 170438 cc789f85-1476-452a-8100-9e74502198e0.jpg
2025-07-09 14:43:36 97592 d27cf49f-b689-4a75-a249-d373e0330bb5.jpg
2025-07-09 14:43:36 169246 d3104128-1d14-4465-99d3-8ab9267c687b.jpg
2025-07-09 14:43:36 151884 d4edfedb-dbe9-4dd9-aae8-009489394955.jpg
2025-07-09 14:43:36 134344 d77f9ae6-e9a8-4a3e-86bd-b72af75cbc49.jpg
2025-07-09 15:10:27 10024 placeholder.jpg <----------------
これで画像がUI経由で利用可能であることを確認できます:
http://k8s-ui-uinlb-647e781087-6717c5049aa96bd9.elb.us-west-2.amazonaws.com/assets/img/products/placeholder.jpg
ブラウザでURLにアクセスしてください:
これで、Mountpoint for Amazon S3をEKSで実行されるワークロードの永続的な共有ストレージとして使用する方法を正常に実証しました。