0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EBS CSI DriverのPV/PVCライフサイクルをログ追跡で完全理解する

0
Last updated at Posted at 2026-04-10

はじめに

EKSでステートフルなワークロードを動かすとき、EBSボリュームの管理は避けて通れません。しかし、kubectl apply でPVCを作ってからPodにマウントされるまでの間に、Kubernetes内部では複数のコンポーネントが連携して動いています。

この記事では、EBS CSI Driverを使ったPV/PVCのライフサイクル(プロビジョニング → アタッチ → マウント → デタッチ → 削除)を、実際のログを追跡しながら一つずつ確認していきます。

ebs-csi-lifecycle-overview.png

この記事で学べること

  • EBS CSI Driverの内部アーキテクチャ(Controller / Node の役割分担)
  • PVC作成からEBSボリュームが作られるまでの具体的なフロー
  • WaitForFirstConsumer によるトポロジー対応プロビジョニングの仕組み
  • AZ制約(InvalidVolume.ZoneMismatch)が発生する条件と回避策
  • 旧in-tree kubernetes.io/aws-ebs との違い

想定読者

  • EKSでステートフルワークロードを運用している方
  • CSI Driverの内部動作を理解したい方
  • EBSボリュームのトラブルシューティングに役立てたい方

背景・課題

Kubernetesのストレージは、もともとコア本体に組み込まれた「in-tree」プラグインで提供されていました。EBSの場合は kubernetes.io/aws-ebs がそれにあたります。

しかし、in-treeプラグインはKubernetes本体のリリースサイクルに縛られるため、ストレージベンダーが独立して機能追加やバグ修正を行えないという課題がありました。この問題を解決するために策定されたのが Container Storage Interface(CSI) です。

EKSでは Kubernetes 1.25 以降、CSI Migration が有効化されており、既存の kubernetes.io/aws-ebs を使ったStorageClassも内部的にはCSI Driverにルーティングされます。新規構築では ebs.csi.aws.com を明示的に使うことが推奨されています。

参考: Kubernetes 1.25: Kubernetes In-Tree to CSI Volume Migration Status Update

検証環境

項目 内容
EKS バージョン 1.31
EBS CSI Driver v1.57.1(EKSアドオン)
ノード t3.small × 2(ap-northeast-1a, ap-northeast-1c)
リージョン ap-northeast-1
IAM認証 IRSA(IAM Roles for Service Accounts)

検証内容

1. EBS CSI Driverのアーキテクチャ確認

EBS CSI Driverは、大きく2つのコンポーネントで構成されています。

ebs-csi-lifecycle-overview.png1.png

Controller(Deployment: 2レプリカ)

ボリュームの作成・削除・アタッチ・デタッチを担当します。以下のサイドカーコンテナで構成されています。

$ kubectl get pods -n kube-system -l app=ebs-csi-controller \
    -o jsonpath='{.items[0].spec.containers[*].name}'
ebs-plugin csi-provisioner csi-attacher csi-snapshotter csi-resizer liveness-probe
コンテナ 役割
ebs-plugin メインのCSIドライバー。AWS APIを呼び出してEBSを操作
csi-provisioner PVCの作成を検知し、ebs-pluginにCreateVolumeを要求
csi-attacher VolumeAttachmentを検知し、ebs-pluginにAttach/Detachを要求
csi-resizer PVCの容量変更を検知し、ebs-pluginにExpandVolumeを要求
csi-snapshotter VolumeSnapshotを検知し、スナップショット操作を要求
liveness-probe ヘルスチェック

Node(DaemonSet: 各ノードに1つ)

ボリュームのフォーマット・マウントを担当します。

$ kubectl get pods -n kube-system -l app=ebs-csi-node \
    -o jsonpath='{.items[0].spec.containers[*].name}'
ebs-plugin node-driver-registrar liveness-probe
コンテナ 役割
ebs-plugin ノード上でのマウント・アンマウント操作を実行
node-driver-registrar kubeletにCSIドライバーを登録
liveness-probe ヘルスチェック

2. StorageClassの比較(in-tree vs CSI)

まず、デフォルトで存在するin-treeのStorageClassを確認します。

$ kubectl get storageclass
NAME          PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2           kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  15m

CSI Driver用のStorageClassを作成します。

# storageclass-gp3.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-gp3-csi
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
$ kubectl get storageclass
NAME          PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
ebs-gp3-csi   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   6s
gp2           kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  15m

主な違いは以下の通りです。

項目 in-tree (gp2) CSI (ebs-gp3-csi)
provisioner kubernetes.io/aws-ebs ebs.csi.aws.com
ボリュームタイプ gp2 gp3
ボリューム拡張 不可 可能
スナップショット 非対応 対応

3. プロビジョニングフローのログ追跡

PVCを作成し、Podをデプロイして、EBSボリュームがプロビジョニングされるまでの流れをログで追跡します。

# pvc-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-test-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs-gp3-csi
  resources:
    requests:
      storage: 10Gi

PVCを作成した時点では、WaitForFirstConsumer の設定により、ボリュームはまだ作成されません。Podを作成して初めてプロビジョニングが開始されます。

# pod-test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: ebs-test-pod
spec:
  containers:
  - name: app
    image: amazonlinux:2
    command: ["sleep", "3600"]
    volumeMounts:
    - mountPath: /data
      name: ebs-volume
  volumes:
  - name: ebs-volume
    persistentVolumeClaim:
      claimName: ebs-test-pvc

csi-provisioner のログ:

Podが作成されると、csi-provisionerがPVCのプロビジョニングを開始します。

I0406 08:37:16  Event occurred  object="default/ebs-test-pvc"
  type="Normal" reason="Provisioning"
  message="External provisioner is provisioning volume for claim \"default/ebs-test-pvc\""

I0406 08:37:18  successfully created PV pvc-xxxxxxxx-... for PVC ebs-test-pvc
  and csi volume name vol-xxxxxxxxxxxxxxxxx

I0406 08:37:18  Event occurred  type="Normal" reason="ProvisioningSucceeded"
  message="Successfully provisioned volume pvc-xxxxxxxx-..."

ebs-plugin(Controller)のログ:

ebs-pluginがAWS APIを呼び出してEBSボリュームを作成し、続けてノードへのアタッチを実行します。

I0406 08:37:19  ControllerPublishVolume: attaching
  volumeID="vol-xxxxxxxxxxxxxxxxx" nodeID="i-xxxxxxxxxxxxxxxxx"

I0406 08:37:21  ControllerPublishVolume: attached
  volumeID="vol-xxxxxxxxxxxxxxxxx" nodeID="i-xxxxxxxxxxxxxxxxx" devicePath="/dev/xvdaa"

実行結果:

PVが作成され、EBSボリュームとの対応が確認できます。

$ kubectl describe pv pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Name:              pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
StorageClass:      ebs-gp3-csi
Status:            Bound
Claim:             default/ebs-test-pvc
Capacity:          10Gi
Access Modes:      RWO
VolumeMode:        Filesystem
Source:
    Type:       CSI
    Driver:     ebs.csi.aws.com
    VolumeHandle:  vol-xxxxxxxxxxxxxxxxx
Node Affinity:
  Required Terms:
    Term 0:  topology.kubernetes.io/zone in [ap-northeast-1c]

AWS側でもgp3タイプ・10GiBのボリュームが作成されていることを確認できます。

$ aws ec2 describe-volumes --volume-ids vol-xxxxxxxxxxxxxxxxx \
    --query 'Volumes[0].{VolumeId:VolumeId,Size:Size,VolumeType:VolumeType,State:State,AvailabilityZone:AvailabilityZone}'
{
    "VolumeId": "vol-xxxxxxxxxxxxxxxxx",
    "Size": 10,
    "VolumeType": "gp3",
    "State": "in-use",
    "AvailabilityZone": "ap-northeast-1c"
}

AWSコンソールのブロックデバイス画面でも、ルートボリューム(80GB)に加えて、CSI Driverが作成した10GBのボリュームが /dev/xvdaa としてアタッチされていることが確認できます。

1.png

4. Attach/Mountプロセスの詳細

ボリュームがプロビジョニングされた後、ノードへのアタッチとPodへのマウントが行われます。

csi-attacher のログ:

I0406 08:37:19  Attaching  VolumeAttachment="csi-xxxxxxxx..."
I0406 08:37:22  Attached   VolumeAttachment="csi-xxxxxxxx..."

ebs-plugin(Node)のログ:

Node側のebs-pluginが、ブロックデバイスのフォーマットとマウントを実行します。

I0406 08:37:22  Disk "/dev/nvme1n1" appears to be unformatted,
  attempting to format as type: "ext4" with options: [-F -m0 /dev/nvme1n1]

I0406 08:37:22  Disk successfully formatted (mkfs): ext4
  - /dev/nvme1n1 /var/lib/kubelet/plugins/kubernetes.io/csi/...

I0406 08:37:22  Volume needs resizing  source="/dev/nvme1n1"
I0406 08:37:22  Device /dev/nvme1n1 resized successfully

VolumeAttachment リソース:

KubernetesのVolumeAttachmentリソースで、ボリュームとノードの紐付けが管理されています。

$ kubectl describe volumeattachment csi-xxxxxxxx...
Spec:
  Attacher:   ebs.csi.aws.com
  Node Name:  ip-192-168-xx-xxx.ap-northeast-1.compute.internal
  Source:
    Persistent Volume Name:  pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Status:
  Attached:  true
  Attachment Metadata:
    Device Path:  /dev/xvdaa

EC2側でもアタッチ状態を確認できます。

$ aws ec2 describe-volumes --volume-ids vol-xxxxxxxxxxxxxxxxx \
    --query 'Volumes[0].Attachments[0].{InstanceId:InstanceId,Device:Device,State:State}'
{
    "InstanceId": "i-xxxxxxxxxxxxxxxxx",
    "Device": "/dev/xvdaa",
    "State": "attached"
}

5. Pod再スケジュール時のDetach/Re-attachとAZ制約

Podを削除して別ノードに再スケジュールした際の挙動を確認します。ここが最も重要なポイントです。

Detachの確認:

Podを削除すると、csi-attacherがDetachを実行します。

I0406 09:06:26  Detaching  VolumeAttachment="csi-xxxxxxxx..."
I0406 09:06:32  Detached   VolumeAttachment="csi-xxxxxxxx..."

AZ制約エラーの再現:

EBSボリュームは作成されたAZから移動できません。nodeName で別AZのノードを直接指定すると、スケジューラのAZ考慮がバイパスされ、以下のエラーが発生します。

Error processing VolumeAttachment err="failed to attach: rpc error:
  code = Internal desc = Could not attach volume \"vol-xxxxxxxxxxxxxxxxx\"
  to node \"i-yyyyyyyyyyyyyyyyy\":
  operation error EC2: AttachVolume, api error InvalidVolume.ZoneMismatch:
  The volume 'vol-xxxxxxxxxxxxxxxxx' is not in the same availability zone
  as instance 'i-yyyyyyyyyyyyyyyyy'"

実際にノードのAZを確認すると、ボリューム(ap-northeast-1c)と指定先ノード(ap-northeast-1a)が異なっていました。

$ kubectl get nodes -L topology.kubernetes.io/zone
NAME                                                STATUS   ROLES    AGE   VERSION                ZONE
ip-192-168-xx-xxx.ap-northeast-1.compute.internal   Ready    <none>   51m   v1.31.14-eks-f69f56f   ap-northeast-1a
ip-192-168-yy-yyy.ap-northeast-1.compute.internal   Ready    <none>   51m   v1.31.14-eks-f69f56f   ap-northeast-1c

正しい再スケジュール:

nodeName を指定せずにPodを再作成すると、スケジューラがPVのNode Affinityを考慮して同一AZのノードに配置し、正常にアタッチされます。

I0406 09:12:27  Detached   VolumeAttachment="csi-xxxxxxxx..."
I0406 09:12:30  Attaching  VolumeAttachment="csi-xxxxxxxx..."
I0406 09:12:31  Attached   VolumeAttachment="csi-xxxxxxxx..."

6. PVC削除時のボリューム削除フロー

最後に、PodとPVCを削除した際のクリーンアップフローを確認します。

ebs-plugin(Controller)のログ:

I0406 09:15:17  ControllerUnpublishVolume: detaching
  volumeID="vol-xxxxxxxxxxxxxxxxx" nodeID="i-xxxxxxxxxxxxxxxxx"

I0406 09:15:18  Waiting for volume state
  volumeID="vol-xxxxxxxxxxxxxxxxx" actual="detaching" desired="detached"

I0406 09:15:22  ControllerUnpublishVolume: detached
  volumeID="vol-xxxxxxxxxxxxxxxxx" nodeID="i-xxxxxxxxxxxxxxxxx"

reclaimPolicy: Delete が設定されているため、PVC削除後にPVとEBSボリュームが自動的に削除されます。

検証結果のまとめ

検証項目 結果 備考
CSI Driverのデプロイ構成 Controller(Deployment) + Node(DaemonSet) Controller 2レプリカ、Node 各ノード1つ
プロビジョニング WaitForFirstConsumerでPod作成後に実行 PVC作成だけではボリューム未作成
Attach/Mount Controller→Attach、Node→Format+Mount 初回はext4フォーマットが実行される
AZ制約 別AZノードへのアタッチは ZoneMismatch エラー nodeNameの直接指定で再現
再スケジュール スケジューラに任せれば同一AZに配置 PVのNode Affinityが考慮される
削除フロー Detach → PV削除 → EBS削除 reclaimPolicy: Deleteで自動クリーンアップ

わかったこと・学び

  • WaitForFirstConsumerは必須: PVC作成時点ではボリュームが作られず、Podのスケジュール先AZにボリュームが作成されます。これにより初回のAZミスマッチを防げます
  • サイドカーパターンの理解が重要: csi-provisioner、csi-attacher、ebs-pluginがそれぞれ異なる責務を持ち、Kubernetes APIのリソース(PVC、VolumeAttachment)をウォッチして連携しています
  • Node Affinityが再スケジュールの鍵: PVに topology.kubernetes.io/zone のNode Affinityが設定されるため、スケジューラに任せれば同一AZのノードに自動配置されます

まとめ

EBS CSI Driverのボリュームライフサイクルを、実際のログを追跡しながら確認しました。

  1. プロビジョニング: csi-provisioner → ebs-plugin → AWS CreateVolume API
  2. アタッチ: csi-attacher → ebs-plugin → AWS AttachVolume API
  3. マウント: kubelet → ebs-plugin(Node) → mkfs + mount
  4. デタッチ: csi-attacher → ebs-plugin → AWS DetachVolume API
  5. 削除: ebs-plugin → AWS DeleteVolume API

各フェーズで異なるコンポーネントが責務を分担しており、ログを見ることでどこで問題が起きているかを特定できます。EBSボリュームのトラブルシューティング時には、まず csi-provisionercsi-attacherebs-plugin の順にログを確認するのがおすすめです。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?