はじめに
この記事では、AWSのEC2インスタンス上にkubeadmで構築したKubernetesクラスターにEBS CSI Driverをデプロイし、IRSA(IAM Roles for Service Accounts)を使ってEBSボリュームを利用できることを検証します。
注意いただきたいのは、検証環境はEKSではなくて「素のEC2インスタンスに構築したKubernetes」という点です。そのため、EKSだと最初から用意されているコンポーネント(OIDCエンドポイントやAWS Cloud Controller Managerなど)を自分で揃える必要があります。
この取り組みに関しては、以下の記事で書いてきました。ご興味のあるかたは、是非ご一読ください。
- 事前準備:信頼済みTLS証明書を導入する
- 第1回:OIDCメタデータエンドポイントを公開する
- 第2回:IAMロールを作ってServiceAccountからAWSにアクセスする
- 第3回:Amazon EKS Pod Identity Webhookを導入する
- 第4回:AWS Cloud Controller Managerを導入する
環境情報
EC2インスタンス1台を使って、kubeadmでKubernetesクラスターを構築しています。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-0-4 Ready control-plane 17d v1.36.5
このマシンは、Control PlaneとWorkerを兼ねています。
これまでの第1回から第4回までの試みを通して、この1台のEC2インスタンスにさまざまなコンポーネントがデプロイされています。
- cert-manager(事前準備)
- Envoy Gateway(第1回)
- OIDCエンドポイント公開用のリソース(第1回)
- Amazon EKS Pod Identity Webhook(第3回)
- AWS Cloud Controller Manager(第4回)
kubectl コマンドでPodを確認すると、一つのノードに沢山のPodが「すし詰め」になっている状態です。
$ kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase'
NAMESPACE NAME STATUS
cert-manager cert-manager-cainjector-6dccbccc6-vj58x Running
cert-manager cert-manager-df898748d-d4zrx Running
cert-manager cert-manager-startupapicheck-gplgg Succeeded
cert-manager cert-manager-webhook-cd8d5b6bf-xkbqp Running
default apiserver-gateway-zpmsj Running
envoy-gateway-system envoy-gateway-5cfd9b9c6c-njw8t Running
kube-system amazon-eks-pod-identity-webhook-796dbf55c-fd62x Running
kube-system aws-cloud-controller-manager-jnlcz Running
kube-system cilium-envoy-rqljv Running
kube-system cilium-n6zbb Running
kube-system cilium-operator-7757bfd756-b5w2j Running
kube-system coredns-589f44dc88-nx8bg Running
kube-system coredns-589f44dc88-pt299 Running
kube-system etcd-ip-10-0-0-4 Running
kube-system kube-apiserver-ip-10-0-0-4 Running
kube-system kube-controller-manager-ip-10-0-0-4 Running
kube-system kube-proxy-nhhvl Running
kube-system kube-scheduler-ip-10-0-0-4 Running
これまでEC2のインスタンスタイプはt3a.small(2vCPU、2GiB)、ルートボリュームのサイズは10GiBでまかなえてきました。
しかし、さらにEBS CSI Driverをデプロイするとリソースが枯渇して、一部のPodがReadyにならない事態となりました。
そこで、インスタンスタイプは一つ上のt3a.medium(2vCPU、4GiB)にして、ルートボリュームのサイズも15GiBに上げて検証ができる状態にしています。
EBS CSI Driverをデプロイする手順
IAMロールの作成
次の手順でデプロイするEBS CSI Driverには、EBSボリュームの作成や削除などを行うための権限が必要となります。そこで、これを実現するためのIAMロールを作成します。
IAMロールの作成には、以下のAWS公式ドキュメントの「AWS CLI」に書かれた内容を参考にします。
まずは、IAMロールに割り当てる信頼ポリシーのドキュメントを、以下のように作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/[kube-apiserverのホスト名]"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"[kube-apiserverのホスト名]:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa",
"[kube-apiserverのホスト名]:aud": "sts.amazonaws.com"
}
}
}
]
}
[kube-apiserverのホスト名] と書かれた部分は、第1回で設定したOIDCエンドポイントのFQDN名を入力します。自分がGitHubに作成しているk8s-irsa-without-eksだと、terraform/route53/terraform.tfvarsのroute53_a_record_nameと同じ値が入ります。
またsubのキーに書いている値は、「namespace が kube-system の、ebs-csi-controller-sa という ServiceAccount リソース」を表します。次の手順でEBS CSI Driverをデプロイする際に作成するServiceAccountは、これと一致する必要があるので注意してください。
この信頼ポリシーのドキュメントを使って、IAMロールを作成します。今回はEBS_CSI_DriverRoleという名前にします。
aws iam create-role \
--role-name EBS_CSI_DriverRole \
--assume-role-policy-document file://"aws-ebs-csi-driver-trust-policy.json"
作成したIAMロールに、アクセス許可に関するポリシーを付与します。今回はAWSが管理するAmazonEBSCSIDriverPolicyを付与します。
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
--role-name EBS_CSI_DriverRole
EBS CSI Driverのデプロイ
続いて、KubernetesクラスターにEBS CSI Driverをデプロイします。本記事では、以下のHelmfileを使って実現します。
repositories:
- name: aws-ebs-csi-driver
url: https://kubernetes-sigs.github.io/aws-ebs-csi-driver
releases:
- name: aws-ebs-csi-driver
namespace: kube-system
chart: aws-ebs-csi-driver/aws-ebs-csi-driver
version: 2.66.1
values:
- controller:
replicaCount: 1
serviceAccount:
create: true # チャートデプロイ時にServiceAccountを作成する
name: ebs-csi-controller-sa # 信頼ポリシーの sub の記述と同じ名前を指定する
annotations:
# IRSAを使ってIAM Roleを引き受けるための記述
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/EBS_CSI_DriverRole
automountServiceAccountToken: true
以下が、インストールコマンドです。
helmfile apply -f ebs-csi-driver.yaml
次のように、ebs-csi-controller のPodと、各ノード向けの ebs-csi-node のPodがデプロイされるのを確認します。
$ kubectl get deploy,ds -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ebs-csi-controller 1/1 1 1 10s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/ebs-csi-node 1 1 1 1 1 kubernetes.io/os=linux 10s
daemonset.apps/ebs-csi-node-windows 0 0 0 0 0 kubernetes.io/os=windows 10s
$ kubectl get po -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver
NAME READY STATUS RESTARTS AGE
pod/ebs-csi-controller-59d5456f97-m6pmq 5/5 Running 0 21s
pod/ebs-csi-node-thx7j 3/3 Running 0 21s
PVCを使ってEBSボリュームを調達してみる
下準備が出来たので、Kubernetesの各リソースを使ってEBSボリュームが使えるのかを確認します。
Kubernetesに詳しい人にはさほど真新しさがないかと思いますが、以下の手順を実施します。
- (1) EBSボリューム用のStorageClassを作成
- (2) PVC(PersistentVolumeClaim)を作成
- (3) PVCを参照して、EBSボリュームをマウントするPodをデプロイ
(1) EBSボリューム用のStorageClassを作成
以下のマニフェストを用意します。
わかりやすさ重視で、必要最低限の設定にしています。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
上記マニフェストをデプロイし、StorageClassが作成されたのを確認します。
$ kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/ebs-sc created
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
ebs-sc ebs.csi.aws.com Delete WaitForFirstConsumer false 5s
(2) PVC(PersistentVolumeClaim)を作成
以下のマニフェストを用意します。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: ebs-sc
resources:
requests:
storage: 1Gi
上記マニフェストをデプロイし、PVCが作成されたのを確認します。
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/ebs-claim created
$ kubectl get pvc -n default
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
ebs-claim Pending ebs-sc <unset> 10s
(3) PVCを参照して、EBSボリュームをマウントするPodをデプロイ
最後に、EBSボリュームをマウントするPodを、以下のマニフェストで作成します。
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: public.ecr.aws/amazonlinux/amazonlinux
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
以下のコマンドでPodを作成します。
$ kubectl apply -f pod.yaml
pod/app created
EBSボリュームがPodで使えることを確認
Podをデプロイすると、これまでPendingだったPVCにボリュームが割り当てられて、Boundになったことを確認できます。
$ kubectl get pvc -n default
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
ebs-claim Bound pvc-d9e4a0b7-0a1a-4a20-8c0a-e12ffae902b3 1Gi RWO ebs-sc <unset> 5m4s
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-d9e4a0b7-0a1a-4a20-8c0a-e12ffae902b3 1Gi RWO Delete Bound default/ebs-claim ebs-sc <unset> 31s
AWS CLIで、以下のように1GiBのEBSボリュームが作成されたのを確認できます。
$ aws ec2 describe-volumes \
--filters 'Name=tag-key,Values=CSIVolumeName' \
--query "Volumes[].{ID:VolumeId,Size:Size,State:State,Type:VolumeType}" \
--output table
-----------------------------------------------------
| DescribeVolumes |
+------------------------+-------+---------+--------+
| ID | Size | State | Type |
+------------------------+-------+---------+--------+
| vol-0b95c76f3b9f4565e | 1 | in-use | gp3 |
+------------------------+-------+---------+--------+
そして、PodでもEBSボリュームを問題なく使えていることを、以下のように確認できます。
$ kubectl get po -n default app
NAME READY STATUS RESTARTS AGE
app 1/1 Running 0 71s
$ kubectl exec -it -n default pod/app -- df -h
Filesystem Size Used Avail Use% Mounted on
overlay 14G 7.5G 6.1G 56% /
tmpfs 64M 0 64M 0% /dev
/dev/nvme1n1 974M 32K 958M 1% /data # EBSボリュームをマウントできている
/dev/root 14G 7.5G 6.1G 56% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 3.7G 12K 3.7G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.9G 0 1.9G 0% /proc/acpi
tmpfs 1.9G 0 1.9G 0% /proc/scsi
tmpfs 1.9G 0 1.9G 0% /sys/firmware
$ kubectl exec -it -n default pod/app -- tail -n 8 /data/out.txt
Fri Oct 30 01:58:37 UTC 2026
Fri Oct 30 01:58:42 UTC 2026
Fri Oct 30 01:58:47 UTC 2026
Fri Oct 30 01:58:52 UTC 2026
Fri Oct 30 01:58:57 UTC 2026
Fri Oct 30 01:59:02 UTC 2026
Fri Oct 30 01:59:07 UTC 2026
Fri Oct 30 01:59:12 UTC 2026
おわりに
ようやく、非EKSの環境で、IRSAを使って実用的なAWSリソースを使うところまで到達できました。
自前で構築したKubernetes環境で、EKSと同等のことがどこまでできるのか、今後も試していきたいです。