- Published on
Kubernetes PVC Pending - EBS CSI 동적 프로비저닝 실패 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS에서 StatefulSet이나 Deployment에 PVC를 붙였는데 PVC Pending이 계속 유지되면, 대부분 “동적 프로비저닝(External Provisioner)이 EBS 볼륨을 만들지 못하는 상황”입니다. 겉으로는 단순히 스토리지가 안 붙는 문제처럼 보이지만, 실제 원인은 IAM/IRSA 권한, StorageClass 파라미터, AZ 토폴로지, 서브넷/태그, CSI 컨트롤러 상태, 심지어는 잘못된 volumeBindingMode까지 다양합니다.
이 글에서는 EBS CSI Driver 기반 동적 프로비저닝이 실패해 PVC가 Pending으로 멈출 때를 전제로, “어디부터 확인해야 가장 빨리 원인을 좁힐 수 있는지”를 체크리스트 형태로 정리하고, 자주 나오는 에러 메시지별 처방을 함께 제공합니다.
PVC Pending의 구조: 어디에서 멈추는가
PVC가 Pending일 때는 보통 아래 중 하나입니다.
- PVC 이벤트에 Provisioning 시도가 아예 없음: StorageClass/프로비저너가 맞지 않거나 CSI 컨트롤러가 동작하지 않음
- Provisioning 시도는 있는데 EBS 생성이 실패: IAM/IRSA, API 제한, KMS, 태그/서브넷, 토폴로지 문제
- EBS는 생성됐는데 바인딩/스케줄링이 막힘:
WaitForFirstConsumer+ Pod 스케줄 불가, AZ 불일치
따라서 진단은 “PVC 이벤트 → CSI 컨트롤러 로그 → StorageClass/토폴로지 → IAM/IRSA” 순서로 내려가면 빠릅니다.
1) 가장 먼저: PVC/StorageClass 이벤트로 단서 확보
PVC 상태/이벤트 확인
kubectl get pvc -A
kubectl describe pvc -n <ns> <pvc-name>
Events 섹션에서 자주 보이는 패턴:
waiting for a volume to be created, either by external provisioner "ebs.csi.aws.com" ...failed to provision volume with StorageClass ...no persistent volumes available for this claim and no storage class is set
StorageClass가 실제로 무엇인지 확인
kubectl get storageclass
kubectl describe storageclass <sc-name>
EBS CSI 동적 프로비저닝이라면 대개 다음 중 하나여야 합니다.
provisioner: ebs.csi.aws.comvolumeBindingMode: WaitForFirstConsumer(권장)
만약 kubernetes.io/aws-ebs(in-tree)로 되어 있거나, 클러스터가 CSI로 전환된 상태에서 in-tree를 사용하면 예상치 못한 Pending이 나올 수 있습니다.
2) EBS CSI 컨트롤러가 정말 동작 중인가
EKS Add-on 또는 Helm으로 설치된 EBS CSI Driver는 보통 kube-system 네임스페이스에 있습니다.
kubectl -n kube-system get pods -l app.kubernetes.io/name=aws-ebs-csi-driver
kubectl -n kube-system get deploy ebs-csi-controller
kubectl -n kube-system get ds ebs-csi-node
정상이라면:
ebs-csi-controllerDeployment의 Pod가 Runningebs-csi-nodeDaemonSet이 노드 수만큼 Running
컨트롤러 로그를 보면 실패 원인이 직관적으로 드러나는 경우가 많습니다.
kubectl -n kube-system logs deploy/ebs-csi-controller -c ebs-plugin --tail=200
kubectl -n kube-system logs deploy/ebs-csi-controller -c csi-provisioner --tail=200
특히 csi-provisioner 컨테이너 로그에 “왜 PVC를 프로비저닝 못했는지”가 찍히는 경우가 많습니다.
3) 가장 흔한 원인 1: IRSA/IAM 권한 문제
EBS CSI Driver는 EC2 API를 호출해 EBS를 생성/태그/연결합니다. 권한이 없으면 PVC는 Pending으로 남고 이벤트에는 AccessDenied류가 뜹니다.
증상 예시
UnauthorizedOperationAccessDenied: Not authorized to perform: ec2:CreateVolumests:AssumeRoleWithWebIdentity AccessDenied
이 경우는 대개 아래 둘 중 하나입니다.
- IRSA가 제대로 연결되지 않음 (ServiceAccount에 role-arn 주석 누락/오타)
- IAM Policy가 부족 (CreateVolume, CreateTags, AttachVolume 등)
IRSA 자체가 꼬였을 때의 점검 흐름은 아래 글이 그대로 도움이 됩니다.
ServiceAccount에 role-arn이 붙어있는지 확인
kubectl -n kube-system get sa ebs-csi-controller-sa -o yaml | sed -n '1,120p'
대개 아래가 있어야 합니다.
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/<EbsCsiRole>
(참고) EBS CSI Driver에 필요한 대표 권한
AWS 관리형 정책 AmazonEBSCSIDriverPolicy를 쓰는 것이 가장 안전합니다. 커스텀 정책을 쓴다면 최소한 아래 액션들이 필요합니다.
ec2:CreateVolume,ec2:DeleteVolume,ec2:DescribeVolumesec2:CreateTags,ec2:DeleteTagsec2:AttachVolume,ec2:DetachVolumeec2:DescribeAvailabilityZones,ec2:DescribeInstances,ec2:DescribeTags
또한 KMS 암호화를 쓰면 kms:Encrypt/Decrypt/GenerateDataKey 등이 추가로 필요합니다(뒤에서 다룹니다).
4) 가장 흔한 원인 2: StorageClass 파라미터/모드 실수
volumeBindingMode가 핵심인 이유
EBS는 AZ에 종속됩니다. volumeBindingMode: Immediate이면 PVC 생성 시점에 AZ를 결정해야 하는데, 아직 Pod가 어느 노드(AZ)에 스케줄될지 모르므로 잘못된 AZ로 볼륨이 생성되거나(혹은 토폴로지 제약으로 생성 자체가 실패) 이후 스케줄링이 막힐 수 있습니다.
EKS에서는 보통 아래처럼 WaitForFirstConsumer를 권장합니다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
type: gp3
fsType: ext4
allowVolumeExpansion 누락
PVC 확장이 필요한 워크로드라면 allowVolumeExpansion: true가 없으면 확장이 실패합니다(이건 Pending과 직접 관련은 적지만 운영에서 자주 같이 터집니다).
fsType/파일시스템 이슈
대부분 ext4가 무난합니다. 특정 이미지/노드 설정에 따라 xfs를 쓸 때 마운트 실패가 날 수 있는데, 이 경우는 PVC Pending이 아니라 Pod 이벤트에서 MountVolume 실패로 나타나는 편입니다.
5) 가장 흔한 원인 3: AZ/토폴로지 불일치(특히 멀티 AZ)
증상
- PVC는 Pending 또는 Bound가 늦게 됨
- Pod는
Pending이며 스케줄러 이벤트에 볼륨 토폴로지 관련 메시지가 보임
Pod 이벤트 확인:
kubectl describe pod -n <ns> <pod>
WaitForFirstConsumer를 쓰는 경우, PVC는 “Pod가 스케줄될 노드의 AZ”가 정해진 뒤에야 볼륨이 만들어집니다. 따라서 Pod가 스케줄 불가능하면 PVC도 계속 Pending이 됩니다.
이때는 스토리지 문제가 아니라 노드 리소스/노드셀렉터/테인트/서브넷 IP 부족 같은 스케줄링 문제일 수 있습니다. Pod 자체가 Pending인 경우는 아래 글의 “IP 부족으로 Pending” 케이스도 함께 점검해볼 만합니다.
nodeSelector/affinity로 특정 AZ를 강제했는지 확인
예를 들어 Pod가 topology.kubernetes.io/zone=ap-northeast-2a만 허용하도록 묶여 있는데, 해당 AZ에 노드가 없거나 스케줄 불가하면 PVC는 끝까지 Pending입니다.
6) 서브넷/태그/EC2 환경 이슈: EBS 생성 자체가 실패하는 케이스
EBS CSI는 EBS를 만들 때 AZ를 기준으로 생성합니다. 다음과 같은 환경 이슈가 있으면 CreateVolume이 실패합니다.
- 해당 AZ에서 EBS 쿼터/용량 제한
- 계정/리전에 대한 API 제한(Throttling)
- KMS 키 권한 문제(암호화 볼륨)
CSI 로그에서 Throttling/Limit 확인
kubectl -n kube-system logs deploy/ebs-csi-controller -c ebs-plugin --tail=300 | rg -i "thrott|limit|quota|kms|accessdenied|unauthorized"
Throttling이면 재시도/지수백오프가 걸리며 PVC가 오래 Pending으로 보일 수 있습니다. 이 경우 CloudTrail/CloudWatch에서 EC2 API 호출 실패를 같이 확인하는 게 빠릅니다.
7) KMS 암호화 사용 시: 키 정책과 IAM을 같이 봐야 함
StorageClass에서 암호화를 켜는 형태는 보통 아래 중 하나입니다.
- 기본 EBS 암호화가 계정에 켜져 있음
- StorageClass에
encrypted: "true"와kmsKeyId를 지정
예시:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-kms
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
kmsKeyId: arn:aws:kms:ap-northeast-2:<account-id>:key/<key-id>
이때는 EBS CSI가 Assume한 Role에 KMS 권한이 있어야 하고, 동시에 KMS Key policy가 그 Role 사용을 허용해야 합니다. 한쪽만 맞아도 실패합니다.
로그에는 보통 kms:GenerateDataKey 또는 AccessDeniedException 형태가 남습니다.
8) 실전 트러블슈팅 플로우(10분 컷)
아래 순서대로 보면, 대부분 10~15분 안에 원인을 특정할 수 있습니다.
1) PVC 이벤트 확인
kubectl describe pvc -n <ns> <pvc>
- 프로비저너가
ebs.csi.aws.com인지 - 에러가 AccessDenied인지, topology인지, StorageClass 미지정인지
2) StorageClass 확인
kubectl get sc <sc> -o yaml
provisioner: ebs.csi.aws.comvolumeBindingMode: WaitForFirstConsumer권장
3) CSI 컨트롤러 상태/로그
kubectl -n kube-system get pods -l app.kubernetes.io/name=aws-ebs-csi-driver
kubectl -n kube-system logs deploy/ebs-csi-controller -c csi-provisioner --tail=200
kubectl -n kube-system logs deploy/ebs-csi-controller -c ebs-plugin --tail=200
4) IRSA/IAM
kubectl -n kube-system get sa ebs-csi-controller-sa -o yaml | rg -n "role-arn|eks.amazonaws.com"
- role-arn 존재 여부
- STS AccessDenied가 있으면 IRSA부터 바로 수정
5) Pod 스케줄링 상태(WaitForFirstConsumer일 때 필수)
kubectl get pod -n <ns>
kubectl describe pod -n <ns> <pod>
- Pod가 Pending이면, 스케줄러 이벤트를 먼저 해결(리소스 부족, taint, nodeSelector, IP 부족 등)
9) 재현 가능한 최소 예제: PVC Pending 상황 만들고 고치기
(1) 잘못된 StorageClass(Immediate)로 멀티 AZ에서 꼬이는 예
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-immediate
provisioner: ebs.csi.aws.com
volumeBindingMode: Immediate
parameters:
type: gp3
이 상태에서 특정 AZ로만 스케줄되는 워크로드가 있거나 노드 풀이 제한적이면, 볼륨이 다른 AZ에 만들어져 Pod 스케줄이 막힐 수 있습니다.
(2) 수정: WaitForFirstConsumer로 변경
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
type: gp3
fsType: ext4
(3) PVC/Pod 예시
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: gp3-wffc
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
적용 후 확인:
kubectl apply -f ./pvc-pod.yaml
kubectl get pvc app-data -w
kubectl get pv -w
10) 운영 팁: “PVC Pending”을 장애로 키우지 않기
- 클러스터 표준 StorageClass를 하나 정해두기:
gp3 + WaitForFirstConsumer + allowVolumeExpansion조합을 기본으로 - EBS CSI Driver는 EKS Add-on으로 관리: 버전 호환성과 롤백이 쉬움
- IRSA 변경은 GitOps로 추적: ServiceAccount annotation이 수동 변경되면 재발이 잦음
- 스케줄링 문제와 분리해서 보기: Pod Pending이면 PVC도 같이 Pending처럼 보일 수 있음
리소스/스케일링 문제로 Pod가 Pending인 경우에는 HPA/metrics 쪽도 함께 점검해야 하는데, metrics-server가 0값을 내는 케이스는 스케줄링/오토스케일 판단을 왜곡해 장애를 키울 수 있습니다.
결론
PVC Pending은 “스토리지 생성이 안 됨” 하나로 보이지만, 실제로는 CSI 컨트롤러 동작 여부 → StorageClass 설정 → IRSA/IAM 권한 → 토폴로지(AZ)와 Pod 스케줄링이 맞물린 결과입니다.
가장 효율적인 접근은 kubectl describe pvc 이벤트에서 시작해, ebs-csi-controller의 csi-provisioner/ebs-plugin 로그로 실패 원인을 확정하고, 그 다음에 StorageClass(WaitForFirstConsumer)와 IRSA/IAM, KMS/토폴로지를 순서대로 교정하는 것입니다.
이 흐름대로만 점검하면, “계속 Pending이라 막막한 상태”에서 벗어나 원인을 빠르게 좁히고 재발 방지까지 연결할 수 있습니다.