Published on

K8s PV Pending·Mount 실패 - CSI·IAM·AZ 점검법

Authors

서버리스가 아닌 이상, 쿠버네티스 스토리지 장애는 결국 (1) 컨트롤 플레인에서의 바인딩(PV/PVC), (2) CSI를 통한 프로비저닝, (3) 노드에서의 Attach/Mount 세 단계 중 어디에서 막히는지로 쪼개서 봐야 합니다. PV가 Pending에 머무르거나, Pod 이벤트에 MountVolume.SetUp failed / FailedAttachVolume가 찍히는 경우는 특히 CSI 드라이버 상태, IAM 권한(IRSA 포함), AZ(Topology) 정합성에서 많이 터집니다.

이 글은 EKS + EBS CSI를 주로 예로 들지만, EFS/FSx 등 다른 CSI에도 동일한 진단 프레임을 적용할 수 있습니다.

1) 증상 분류: Pending vs Mount 실패

먼저 “PV Pending”과 “Mount 실패”는 문제 지점이 다릅니다.

PVC/PV가 Pending인 경우

  • PVC가 Pending: 동적 프로비저닝이 안 됐거나, StorageClass/CSI가 응답하지 않거나, topology 제약으로 스케줄이 불가
  • PV는 있는데 PVC가 Pending: selector/용량/접근모드/스토리지클래스 불일치

Pod가 Mount 실패인 경우

  • PVC/PV 바인딩은 끝났지만
  • 노드 단계에서 Attach 실패(EC2 볼륨 연결) 또는 Mount 실패(파일시스템 마운트)로 막힘

아래 명령으로 사건의 축을 빠르게 잡습니다.

# 1) 바인딩 상태
kubectl get pvc -A
kubectl describe pvc -n <ns> <pvc>

# 2) PV/SC 확인
kubectl get pv
kubectl get sc
kubectl describe sc <storageclass>

# 3) Pod 이벤트에서 Attach/Mount 원인 확인
kubectl describe pod -n <ns> <pod>

# 4) 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 ebs-plugin --tail=200
kubectl -n kube-system logs ds/ebs-csi-node -c ebs-plugin --tail=200

2) CSI 드라이버 점검: 설치/버전/리더선출/웹훅

PV Pending의 단골 원인은 “StorageClass는 있는데 프로비저너가 실제로 동작하지 않는” 상태입니다.

(1) StorageClass의 provisioner가 맞는지

EBS CSI라면 보통 ebs.csi.aws.com 입니다.

kubectl get sc -o yaml | sed -n '1,200p'

# 확인 포인트
# provisioner: ebs.csi.aws.com
# volumeBindingMode: WaitForFirstConsumer (권장)

volumeBindingMode: WaitForFirstConsumer가 아니면, PV가 먼저 특정 AZ로 만들어지고 나중에 Pod가 다른 AZ 노드에 스케줄되어 AZ 불일치로 이어질 수 있습니다(뒤에서 자세히).

(2) CSI 컨트롤러/노드 컴포넌트가 모두 Ready인지

  • 컨트롤러(Deployment): 프로비저닝(CreateVolume), Attach/Detach 컨트롤
  • 노드(DaemonSet): 실제 Mount, NodePublish
kubectl -n kube-system get deploy,ds | egrep 'ebs-csi|csi'
kubectl -n kube-system get pods -o wide | egrep 'ebs-csi|csi'

Pod가 CrashLoopBackOff라면 로그에 AccessDenied, UnauthorizedOperation, Throttling, failed to assume role 등이 자주 보입니다. 이 경우 거의 항상 IAM/IRSA 문제로 연결됩니다.

(3) CSI 관련 CRD/CSINode/Node 드라이버 등록

노드가 CSI 드라이버를 인식하지 못하면 Mount 단계에서 꼬입니다.

kubectl get csinodes
kubectl describe csinode <node-name> | sed -n '1,200p'

Drivers: 섹션에 ebs.csi.aws.com가 보이지 않으면 노드 플러그인이 정상 배치되지 않았거나(데몬셋 미스), 노드에 taint/selector로 인해 스케줄되지 않았을 수 있습니다.

3) IAM(특히 IRSA) 점검: CreateVolume/AttachVolume 권한

EKS에서 EBS CSI는 보통 IRSA(ServiceAccount에 IAM Role 부여)로 권한을 획득합니다. 여기서 한 번만 삐끗해도 PV는 Pending이거나, Attach/Mount가 실패합니다.

(1) ebs-csi-controller ServiceAccount에 role-arn이 붙었는지

kubectl -n kube-system get sa ebs-csi-controller-sa -o yaml | sed -n '1,120p'

# annotations:
#   eks.amazonaws.com/role-arn: arn:aws:iam::<acct>:role/<role>

(2) 컨트롤러 로그에서 AccessDenied 패턴 찾기

kubectl -n kube-system logs deploy/ebs-csi-controller -c ebs-plugin --tail=300 | egrep -i 'accessdenied|unauthorized|assume|sts|denied'

대표적인 실패 메시지:

  • AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity
  • UnauthorizedOperation: You are not authorized to perform ec2:CreateVolume
  • InvalidIdentityToken(OIDC/신뢰정책 문제)

(3) 최소 권한 정책(예시)

AWS는 EBS CSI용 관리형 정책을 제공합니다. 자체 정책을 쓴다면 최소한 아래 계열이 필요합니다(환경에 따라 추가 필요).

  • ec2:CreateVolume, ec2:DeleteVolume, ec2:DescribeVolumes, ec2:DescribeAvailabilityZones
  • ec2:AttachVolume, ec2:DetachVolume
  • ec2:CreateTags, ec2:DeleteTags
  • (암호화 사용 시) kms:Encrypt/Decrypt/GenerateDataKey

> IRSA/OIDC 신뢰 관계가 꼬여 403/AssumeRole 문제가 나는 패턴은 스토리지뿐 아니라 배포 파이프라인에서도 자주 나옵니다. 원인-진단 흐름은 GitHub Actions OIDC로 AWS 배포 403 해결 가이드와 유사하니, OIDC 신뢰정책/조건 검증 관점에서 같이 보면 해결이 빨라집니다.

(4) KMS 암호화 EBS에서 자주 놓치는 것

StorageClass에 encrypted: "true" 또는 특정 KMS Key를 쓰는 경우:

  • CSI 역할에 KMS 권한이 있어야 하고
  • 해당 KMS Key policy에서 role을 신뢰해야 합니다

KMS 쪽이 막히면 CreateVolume 자체가 실패하여 PVC Pending으로 남는 경우가 많습니다.

4) AZ/Topology 점검: WaitForFirstConsumer, nodeAffinity, 스케줄링

EBS는 단일 AZ 자원입니다. 즉, 볼륨은 특정 AZ에 생성되고, 그 볼륨을 쓰는 Pod는 같은 AZ의 노드에서만 정상 동작합니다.

(1) 가장 흔한 해결: StorageClass에 WaitForFirstConsumer

Pod가 어느 AZ에 스케줄될지 먼저 결정한 다음, 그 AZ에 볼륨을 만들게 해야 합니다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  fsType: ext4

기존 SC가 Immediate이면, PVC 생성 시점에 임의 AZ로 PV가 먼저 생겨버려서 이후 스케줄링이 꼬일 수 있습니다.

(2) 이벤트에서 “zone mismatch”류 확인

kubectl describe pod -n <ns> <pod> | sed -n '/Events:/,$p'

다음과 같은 메시지가 힌트입니다.

  • 0/.. nodes are available: volume node affinity conflict
  • failed to get node AZ / topology 관련 오류

(3) PV의 NodeAffinity(Topology) 확인

kubectl get pv <pv> -o yaml | sed -n '1,220p'

# spec.nodeAffinity.required.nodeSelectorTerms...
# topology.kubernetes.io/zone: ap-northeast-2a

Pod가 스케줄된 노드의 라벨과 비교합니다.

kubectl get node <node> -o jsonpath='{.metadata.labels.topology\.kubernetes\.io/zone}'; echo

(4) 멀티 AZ 노드그룹 + 단일 AZ 스토리지의 운영 팁

  • Stateful workload는 Pod anti-affinity로 AZ 분산을 강제하기보다, 스토리지 특성을 고려해 설계
  • AZ 장애 대응이 필요하면 EBS 단일 볼륨보다 EFS/FSx 같은 멀티 AZ 스토리지를 검토

5) Mount 실패의 다른 원인: 파일시스템/권한/노드 네트워크

PVC/PV가 Bound인데도 Pod가 ContainerCreating에서 멈추면, 노드에서의 Mount 단계 문제일 가능성이 큽니다.

(1) 파일시스템 타입 불일치

StorageClass fsType과 실제 포맷이 다르거나, 기존 볼륨을 재사용하면서 파일시스템이 꼬인 경우:

  • wrong fs type, bad option, bad superblock 같은 메시지

노드 플러그인 로그에서 확인합니다.

kubectl -n kube-system logs ds/ebs-csi-node -c ebs-plugin --tail=300 | egrep -i 'superblock|fs type|mount'

(2) 노드에 필요한 유틸리티 누락(커스텀 AMI)

ext4/xfs 도구, nfs-utils(EFS) 등이 없으면 마운트가 실패합니다. EKS 최적화 AMI면 대체로 안전하지만, 하드닝된 커스텀 AMI는 주의가 필요합니다.

(3) 보안그룹/네트워크(특히 EFS)

EBS는 EC2 attach 기반이라 SG 이슈는 덜하지만, EFS는 NFS(2049) 통신이 필요해 SG/NACL에서 막히면 Mount가 실패합니다.

(4) kubelet/노드 상태 이상

노드가 NotReady/디스크 압박 등으로 kubelet이 비정상일 때도 Mount가 연쇄 실패합니다. 노드 레벨의 증상이 같이 보인다면 EKS에서 kubelet node not found 오류 해결처럼 kubelet/노드 등록 상태를 함께 점검하는 편이 빠릅니다.

6) 실전 체크리스트(10분 컷)

아래 순서대로 보면 대부분의 케이스가 빠르게 좁혀집니다.

  1. PVC 이벤트

    kubectl describe pvc -n <ns> <pvc>
    
    • waiting for a volume to be created → CSI/권한/SC
  2. StorageClass

    kubectl describe sc <sc>
    
    • provisioner, volumeBindingMode, parameters 확인
  3. CSI Pod 상태/로그

    kubectl -n kube-system get pods | egrep 'csi|ebs'
    kubectl -n kube-system logs deploy/ebs-csi-controller -c ebs-plugin --tail=200
    
    • AccessDenied면 IAM/IRSA로 직행
  4. AZ/Topology 정합성

    kubectl get pv <pv> -o jsonpath='{.spec.nodeAffinity}' | jq
    kubectl get node <node> -o jsonpath='{.metadata.labels.topology\.kubernetes\.io/zone}'; echo
    
  5. Pod 이벤트에서 Attach vs Mount 구분

    kubectl describe pod -n <ns> <pod> | sed -n '/Events:/,$p'
    

7) 재발 방지: 권장 구성 패턴

(1) StorageClass 표준화

  • 기본 SC를 WaitForFirstConsumer로 통일
  • gp3 표준 파라미터(throughput/iops)는 워크로드별로 분리

(2) IRSA를 “명시적으로” 검증하는 습관

  • ServiceAccount annotation, IAM role trust policy(OIDC sub/aud), 정책 범위를 코드로 관리
  • 클러스터 업그레이드/애드온 교체 시 CSI SA 이름이 바뀌는지 확인

(3) 장애 관측 포인트 추가

  • ebs-csi-controller/ebs-csi-node 로그를 중앙 수집
  • PVC Pending 시간, Attach/Mount 실패 이벤트를 알람화

> 운영 중에는 스토리지 장애가 단독으로 오기보다 다른 장애(예: 노드 네트워크, kubelet, API 오류)와 함께 나타나기도 합니다. kubectl 로그/exec 자체가 안 되는 상황이라면 EKS에서 kubectl exec·logs가 안 될 때 진단법처럼 관측 경로부터 복구해야 스토리지 원인 분석도 진행됩니다.

8) 마무리

K8s에서 PV Pending과 Mount 실패는 “스토리지 문제”로 뭉뚱그리기 쉽지만, 실제로는 CSI(컴포넌트/프로비저너) → IAM(IRSA/KMS) → AZ/Topology(바인딩 모드/스케줄링) 3축으로 나눠보면 대부분 빠르게 해결됩니다.

  • Pending이면: StorageClass/CSI 컨트롤러/IAM을 먼저
  • Bound인데 멈추면: Attach/Mount 이벤트 + 노드 플러그인 로그 + AZ 정합성을 먼저

위 체크리스트대로 확인해도 원인이 애매하다면, kubectl describe pvc/pod의 Events 전체와 ebs-csi-controller 로그의 에러 구간을 함께 확보하면 다음 단계(재현/원인 특정/권한 최소화)로 훨씬 수월하게 넘어갈 수 있습니다.