- Published on
EKS Pod가 ContainerCreating에 멈출 때 10분 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스가 아닌 EKS에서 Pod stuck in ContainerCreating는 “이미지 풀” 이전 단계에서 막혔다는 뜻입니다. 즉 컨테이너 프로세스 자체가 뜨기 전, 노드의 kubelet이 네트워크(CNI)·볼륨(CSI)·보안/권한·샌드박스 생성 같은 준비 작업에서 실패하고 있다는 신호입니다. 이 글은 원인을 빠르게 3갈래(CNI/CSI/권한)로 분류하고, kubectl만으로 10분 내에 결론에 가까워지도록 만든 체크리스트입니다.
아래 순서대로만 따라가면, “왜 ContainerCreating이 끝나지 않는지”를 로그 없이도 이벤트 기반으로 대부분 좁힐 수 있습니다.
0) 1분: 증상 확정(정말 ContainerCreating인가?)
먼저 상태와 이벤트를 한 번에 봅니다.
NS=default
POD=my-app-6c7d9c7b7b-abcde
kubectl -n $NS get pod $POD -o wide
kubectl -n $NS describe pod $POD | sed -n '/Events:/,$p'
여기서 핵심은 Events입니다. ContainerCreating은 원인이 아니라 “단계”이므로, 이벤트의 Warning/Failed 메시지가 곧 원인입니다.
이벤트에서 자주 보이는 패턴은 다음 3종입니다.
- CNI 쪽:
FailedCreatePodSandBox,failed to setup network,cni plugin not initialized - CSI 쪽:
FailedMount,MountVolume.SetUp failed,AttachVolume.Attach failed - 권한/토큰 쪽:
AccessDenied,Unauthorized,failed to fetch token,sts:AssumeRoleWithWebIdentity
이제 10분 진단은 (1) 노드 문제인지, (2) CNI인지, (3) CSI인지, (4) 권한인지 순서로 좁히면 됩니다.
1) 2분: 노드가 문제인지 먼저 자르기(스케줄은 됐나?)
Pod가 특정 노드에 스케줄됐는지 확인합니다.
kubectl -n $NS get pod $POD -o jsonpath='{.spec.nodeName}{"\n"}'
kubectl -n $NS get pod $POD -o jsonpath='{.status.conditions[*].type}{"\n"}'
spec.nodeName이 비어 있으면 아직 스케줄 전입니다(이 경우는 ContainerCreating이 아니라 Pending 쪽 이슈일 가능성이 큼).- nodeName이 있고도 계속 ContainerCreating이면, 그 노드의 kubelet/CNI/CSI가 막혔을 확률이 큽니다.
노드 상태도 확인합니다.
NODE=$(kubectl -n $NS get pod $POD -o jsonpath='{.spec.nodeName}')
kubectl get node $NODE
kubectl describe node $NODE | sed -n '/Conditions:/,/Addresses:/p'
Ready=False면 노드 자체 문제(디스크 풀, kubelet, 런타임 등)로 범위를 좁힙니다.Ready=True인데도 ContainerCreating이면, 보통 CNI/CSI/권한 중 하나입니다.
2) 4분: CNI(네트워크) 진단 — “PodSandbox 생성 실패”가 핵심
ContainerCreating에서 가장 흔한 원인은 CNI입니다. 이벤트에 아래가 보이면 거의 확정입니다.
FailedCreatePodSandBoxfailed to setup network for sandboxrpc error: code = Unknown desc = failed to set up sandbox container
2-1) aws-node(DaemonSet) 상태 확인
EKS VPC CNI는 kube-system의 aws-node가 담당합니다.
kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide
- 특정 노드에서만 문제가 나면, 그 노드의
aws-node가CrashLoopBackOff거나NotReady일 수 있습니다.
해당 노드의 aws-node 로그를 봅니다.
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200 --prefix=true
# 특정 노드만 보고 싶다면
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | grep "$NODE"
AWSNODE_POD=$(kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | awk -v n="$NODE" '$7==n{print $1}')
kubectl -n kube-system logs $AWSNODE_POD --tail=200
2-2) “IP 할당 실패”면 IP 고갈/누수 가능성
이벤트에 failed to assign an IP address to container 류가 보이면, 서브넷 IP 고갈 또는 CNI IP 관리 이슈입니다.
EKS에서 실제로 많이 터지는 케이스가 VPC CNI IP 누수로 Pod IP 고갈입니다. 아래 글의 체크리스트를 같이 보면 원인 파악이 빨라집니다.
빠른 확인 포인트:
# 노드에 할당된 Pod IP 여유가 부족하면 신규 Pod 생성이 막힐 수 있습니다.
kubectl -n kube-system exec -it $AWSNODE_POD -- sh -c 'ip -o addr show dev eth0; echo; cat /var/log/aws-routed-eni/ipamd.log 2>/dev/null | tail -n 50'
또한 보안그룹/NACL로 인해 노드가 EC2 메타데이터나 AWS API 호출에 실패해도 CNI가 IP를 못 가져오는 경우가 있습니다(특히 프라이빗 클러스터에서 NAT/엔드포인트 구성 미흡).
2-3) CNI 권한(노드 IAM Role) 문제
CNI는 보통 노드 IAM Role로 ENI/IP를 붙입니다. 이벤트/로그에 AccessDenied가 보이면 노드 Role의 정책을 점검해야 합니다.
ec2:AssignPrivateIpAddressesec2:CreateNetworkInterfaceec2:AttachNetworkInterface
등이 막히면 PodSandbox 단계에서 멈춥니다.
3) 3분: CSI(스토리지) 진단 — “FailedMount/AttachVolume”가 핵심
PVC를 쓰는 Pod가 ContainerCreating에 오래 멈춘다면, CNI보다 CSI 마운트가 더 흔합니다. 이벤트에 아래가 보이면 CSI 축으로 들어갑니다.
FailedMountMountVolume.SetUp failedAttachVolume.Attach failed
3-1) Pod가 PVC를 쓰는지 즉시 확인
kubectl -n $NS get pod $POD -o jsonpath='{.spec.volumes[*].persistentVolumeClaim.claimName}{"\n"}'
PVC가 있다면 상태를 봅니다.
PVC=$(kubectl -n $NS get pod $POD -o jsonpath='{.spec.volumes[?(@.persistentVolumeClaim)].persistentVolumeClaim.claimName}')
kubectl -n $NS get pvc $PVC
kubectl -n $NS describe pvc $PVC | sed -n '/Events:/,$p'
- PVC가
Pending면 StorageClass/Provisioner 문제입니다. - PVC가
Bound인데도 Pod가 멈추면, Attach/Mount 단계에서 막힌 겁니다.
3-2) EBS CSI Driver 상태 확인
EKS에서 EBS를 쓴다면 ebs-csi-controller, ebs-csi-node를 확인합니다.
kubectl -n kube-system get pod | egrep 'ebs-csi|csi'
kubectl -n kube-system logs deploy/ebs-csi-controller --tail=200
대표 원인:
- IRSA 누락/오구성: CSI Controller가 AWS API 호출을 못해서 볼륨 attach/create 실패
- 노드에 필요한 커널 모듈/권한 문제: 노드 플러그인이 mount 실패
- AZ 불일치: PV가 다른 AZ에 있고 스케줄된 노드가 다른 AZ면 attach 불가(특히 StatefulSet 이동 시)
AZ 불일치 빠른 체크:
# 노드 AZ
kubectl get node $NODE -o jsonpath='{.metadata.labels.topology\.kubernetes\.io/zone}{"\n"}'
# PV AZ(라벨/어노테이션은 환경에 따라 다를 수 있어 describe로 확인)
PV=$(kubectl -n $NS get pvc $PVC -o jsonpath='{.spec.volumeName}')
kubectl describe pv $PV | sed -n '/Node Affinity/,$p'
4) 2분: 권한(IRSA/IAM) 진단 — “토큰/AssumeRole” 흔적을 찾기
ContainerCreating은 컨테이너가 뜨기 전 단계라 애플리케이션 로그가 비어 있는 경우가 많습니다. 하지만 CSI/CNI/이미지 풀/외부 시크릿 같은 주변 컴포넌트는 권한 문제가 매우 잦습니다.
특히 다음 단서가 이벤트/컨트롤러 로그에 보이면 IRSA/IAM을 의심하세요.
AccessDeniedNot authorized to perform sts:AssumeRoleWithWebIdentityInvalidIdentityToken
IRSA 점검 기본:
# 서비스어카운트에 role-arn이 붙어 있는지
SA=default
kubectl -n $NS get sa $SA -o yaml | sed -n '/annotations:/,/secrets:/p'
EKS에서 IRSA 이슈를 10분 내로 진단하는 관점은 ExternalSecret 글의 체크리스트와 거의 동일합니다(웹 아이덴티티 토큰, OIDC, role trust policy, 권한 정책).
또한 EKS 업그레이드/테라폼 변경 이후 aws-auth가 꼬여 노드가 정상 Join을 못하거나 노드 권한이 비정상이 되면, 결과적으로 CNI/CSI가 연쇄적으로 실패할 수 있습니다.
5) 실전 “10분 트리아지” 커맨드 묶음
아래 스니펫은 현장에서 그대로 복붙하기 좋게 만든 최소 세트입니다.
NS=default
POD=my-app-6c7d9c7b7b-abcde
echo "== Pod status =="
kubectl -n $NS get pod $POD -o wide
echo "\n== Pod events =="
kubectl -n $NS describe pod $POD | sed -n '/Events:/,$p'
NODE=$(kubectl -n $NS get pod $POD -o jsonpath='{.spec.nodeName}')
echo "\n== Node =="
echo "NODE=$NODE"
kubectl get node $NODE
echo "\n== Node conditions =="
kubectl describe node $NODE | sed -n '/Conditions:/,/Addresses:/p'
echo "\n== CNI (aws-node) =="
kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | grep "$NODE" || true
AWSNODE_POD=$(kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | awk -v n="$NODE" '$7==n{print $1; exit}')
if [ -n "$AWSNODE_POD" ]; then
kubectl -n kube-system logs $AWSNODE_POD --tail=120
fi
echo "\n== PVC check =="
kubectl -n $NS get pod $POD -o jsonpath='{.spec.volumes[*].persistentVolumeClaim.claimName}{"\n"}'
이 출력만으로도 보통 다음처럼 결론이 납니다.
- 이벤트가
FailedCreatePodSandBox중심 + aws-node 로그 에러 → CNI - 이벤트가
FailedMount/AttachVolume중심 + ebs-csi 로그 에러 → CSI - 로그/이벤트에
AccessDenied/AssumeRoleWithWebIdentity→ 권한(IRSA/IAM 또는 노드 Role)
6) 자주 나오는 원인-처방 매핑(요약)
6-1) CNI: IP 부족/고갈
- 징후:
failed to assign an IP address/ 노드별로 특정 시점부터 신규 Pod가 안 뜸 - 처방: 서브넷 여유 IP 확보, CNI 설정(WARM_IP_TARGET 등) 재검토, IP 누수 여부 확인
6-2) CNI: aws-node CrashLoop
- 징후: 해당 노드의
aws-node가 반복 재시작 - 처방: 최근 CNI 버전업/커스텀 설정 확인, 노드 커널/iptables 충돌 여부, 노드 재부팅으로 임시 회복 후 근본 원인 추적
6-3) CSI: EBS attach 실패(AZ/권한)
- 징후:
AttachVolume.Attach failed/UnauthorizedOperation - 처방: PV NodeAffinity(AZ)와 노드 AZ 일치, EBS CSI IRSA 정책 및 trust policy 점검
6-4) 권한: IRSA 트러스트/웹아이덴티티 토큰
- 징후:
AssumeRoleWithWebIdentity실패 - 처방: OIDC provider 연결, ServiceAccount annotation, IAM role trust policy의
sub/aud조건 확인
7) 마무리: “ContainerCreating”은 이벤트로 끝낸다
ContainerCreating에서 멈춘 Pod를 애플리케이션 관점에서 파고들면 시간이 낭비됩니다. 이 구간은 대부분 kubelet이 샌드박스(네트워크)와 볼륨(스토리지)을 준비하는 단계이므로, kubectl describe pod 이벤트를 기준으로 CNI/CSI/권한 세 갈래로 나누면 10분 내에 원인에 근접할 수 있습니다.
추가로, 네트워크 쪽이 의심되면 VPC CNI IP 고갈/누수 글을, 권한 쪽이 의심되면 IRSA 10분 진단 글을 함께 보며 체크리스트를 병행하면 재발 방지까지 훨씬 빨라집니다.