- Published on
EKS ImagePullBackOff - IRSA·ECR 인증 8단계
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
EKS에서 파드가 ImagePullBackOff 또는 ErrImagePull로 멈추면 대부분은 ECR 인증(권한/토큰) 또는 네트워크(프라이빗 서브넷, VPC 엔드포인트, DNS) 문제로 귀결됩니다. 특히 IRSA(IAM Roles for Service Accounts)를 쓰는 환경에서는 “파드의 서비스어카운트 권한으로 ECR에서 이미지를 당겨올 수 있다”라고 오해하기 쉬운데, 기본적으로 이미지 풀은 노드(또는 런타임)가 수행합니다. 다만 Fargate, 특정 CNI/런타임 구성, 프라이빗 레지스트리 인증을 위한 웹훅/사이드카 패턴 등에서는 서비스어카운트/토큰 설정이 얽히며 진단 난도가 올라갑니다.
아래 8단계는 “지금 당장 원인을 좁히고 재현 가능한 증거를 남기는” 순서로 구성했습니다. 단계별로 확인한 결과를 기록해두면, 같은 유형의 장애가 재발했을 때 평균 복구 시간을 크게 줄일 수 있습니다.
관련 진단 글로, 파드가 뜬 뒤 반복 재시작에 빠지는 케이스는 EKS CrashLoopBackOff? OOMKilled 진단 7단계도 함께 참고하면 좋습니다.
1단계: 이벤트에서 “정확한 실패 문장” 확보
가장 먼저 해야 할 일은 추측이 아니라 이벤트 원문을 확보하는 것입니다. ImagePullBackOff는 결과 상태일 뿐이고, 원인은 이벤트에 남습니다.
kubectl -n <namespace> describe pod <pod-name>
kubectl -n <namespace> get events --sort-by=.lastTimestamp | tail -n 50
자주 보는 패턴은 다음과 같습니다.
no basic auth credentials: 레지스트리 인증 정보(도커 로그인/풀 시크릿) 부재pull access denied/denied: User ... is not authorized: IAM/ECR 권한 부족manifest unknown/not found: 이미지 태그/리포지토리 경로 오타i/o timeout/TLS handshake timeout: 네트워크/라우팅/DNS/NAT/ECR 엔드포인트x509: certificate signed by unknown authority: 프록시/사내 CA/미러 레지스트리 인증서 문제
여기서 오타/태그 문제와 권한/네트워크 문제가 1차로 갈립니다.
2단계: 이미지 경로·태그·리전부터 정합성 확인
ECR은 리전 종속입니다. ap-northeast-2에 푸시했는데 us-east-1 엔드포인트로 풀을 시도하면 인증이 맞아도 실패합니다.
- 이미지 URI 형식:
ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/REPO:TAG TAG존재 여부REPO이름(슬래시 포함 여부) 정확성
# 로컬/CI에서 태그 존재 확인
aws ecr describe-images \
--region ap-northeast-2 \
--repository-name <repo> \
--image-ids imageTag=<tag>
만약 이벤트가 manifest unknown이면 이 단계에서 끝나는 경우가 많습니다.
3단계: 노드 IAM Role에 ECR Pull 권한이 있는지 확인
EKS에서 일반 노드 그룹(EC2) 이 이미지를 풀 때 주체는 대개 노드의 IAM Role입니다. 따라서 IRSA를 잘 구성했더라도 노드 Role에 ECR Pull 권한이 없으면 그대로 실패합니다.
노드 인스턴스 프로파일(Role)을 확인한 뒤, 아래 권한이 포함되어 있는지 봅니다.
ecr:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:GetDownloadUrlForLayerecr:BatchGetImage
가장 빠른 방법은 AWS 관리형 정책 AmazonEC2ContainerRegistryReadOnly가 붙어 있는지 확인하는 것입니다.
# 노드가 속한 노드그룹 확인
aws eks list-nodegroups --cluster-name <cluster>
# 노드그룹 상세에서 nodeRole 확인
aws eks describe-nodegroup --cluster-name <cluster> --nodegroup-name <ng>
권한이 없다면 노드 Role에 정책을 부여하고, 문제 파드를 재스케줄링해 재시도합니다.
4단계: IRSA를 쓰는 경우, “이 워크로드가 정말 IRSA가 필요한가” 분리
여기서 중요한 포인트는 다음입니다.
- 이미지 풀 자체는 보통 IRSA와 무관합니다(EC2 노드 기준).
- 하지만 조직에 따라 다음이 섞여 장애가 복잡해집니다.
- 프라이빗 레지스트리 인증을 위해
imagePullSecrets를 IRSA 기반으로 동적으로 주입하는 컨트롤러 사용 - Fargate 프로파일(노드가 없고, 실행 주체가 다름)
- ECR 외 레지스트리(Artifactory, Harbor 등)와 혼용
- 프라이빗 레지스트리 인증을 위해
따라서 먼저 “이 파드가 ECR만 풀면 되는가, 아니면 별도 인증 체인이 있는가”를 분리하세요.
kubectl -n <namespace> get pod <pod> -o jsonpath='{.spec.imagePullSecrets}'
kubectl -n <namespace> get pod <pod> -o jsonpath='{.spec.serviceAccountName}'
imagePullSecrets가 비어 있고 ECR인데도 no basic auth credentials가 뜬다면, 노드 IAM 권한/네트워크 쪽으로 다시 회귀하는 게 맞습니다.
5단계: 서비스어카운트 어노테이션과 OIDC 신뢰 정책 점검(IRSA 핵심)
IRSA를 실제로 사용하는 워크로드(예: 앱이 S3/STS 호출)라면, 여기서 틀어질 확률이 큽니다. 특히 OIDC Provider, Trust Policy의 sub 조건이 조금만 어긋나도 STS AssumeRoleWithWebIdentity가 실패합니다.
서비스어카운트에 Role ARN이 올바르게 붙었는지 확인합니다.
kubectl -n <namespace> get sa <sa-name> -o yaml
다음 형태가 있어야 합니다.
eks.amazonaws.com/role-arn: arn:aws:iam::...:role/...
그리고 IAM Role의 신뢰 정책에서 sub가 정확히 일치해야 합니다. 예시는 아래와 같습니다(각 값은 환경에 맞게).
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:sub": "system:serviceaccount:<namespace>:<sa-name>",
"oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:aud": "sts.amazonaws.com"
}
}
}
]
}
여기서도 MDX 렌더링을 고려해 <...> 같은 표기를 본문에서 그대로 쓰지 말고, 위처럼 코드 블록 안에서만 사용하세요.
6단계: 파드 내부에서 WebIdentity 토큰 마운트 여부 확인
IRSA가 동작하려면 파드에 토큰 파일이 마운트되고, 환경변수로 Role ARN과 토큰 경로가 주입됩니다.
kubectl -n <namespace> exec -it <pod> -- sh -lc 'env | egrep "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE"'
kubectl -n <namespace> exec -it <pod> -- sh -lc 'ls -l $AWS_WEB_IDENTITY_TOKEN_FILE && head -c 20 $AWS_WEB_IDENTITY_TOKEN_FILE; echo'
- 값이 비어 있다면: 서비스어카운트 지정 누락,
automountServiceAccountToken비활성화, 혹은 Admission/Webhook 정책 영향 가능 - 토큰 파일이 없거나 권한이 이상하면: PSA/OPA/Gatekeeper 정책, 커스텀 SA 설정을 의심
이 단계는 “IRSA 자체가 살아있는지”를 빠르게 확인하는 데 유용합니다.
7단계: ECR 네트워크 경로 점검(NAT vs VPC Endpoint)
프라이빗 서브넷의 노드가 ECR에 접근하려면 보통 둘 중 하나가 필요합니다.
- NAT Gateway를 통한 아웃바운드 인터넷
- VPC Interface Endpoint(ECR API, ECR DKR) + Gateway Endpoint(S3)
ECR은 이미지 레이어를 S3에서 내려받는 흐름이 포함되므로, VPC 엔드포인트를 쓴다면 S3까지 같이 봐야 합니다.
체크 포인트:
- 노드 서브넷 라우팅 테이블에
0.0.0.0/0가 NAT로 가는지 - 보안그룹/NACL이 443 아웃바운드 허용인지
- VPC 엔드포인트를 쓴다면 Private DNS 활성화 여부
노드에서 직접 DNS/HTTPS 확인을 해보면 단서가 빨리 나옵니다. 디버그 파드를 띄워 점검하세요.
kubectl -n <namespace> run net-debug \
--image=public.ecr.aws/docker/library/alpine:3.19 \
--restart=Never -it --rm -- sh
# 파드 안에서
apk add --no-cache curl bind-tools
nslookup <account>.dkr.ecr.<region>.amazonaws.com
curl -I https://api.ecr.<region>.amazonaws.com
i/o timeout이나 DNS 실패가 나오면 IAM이 아니라 네트워크 문제일 가능성이 큽니다.
8단계: 컨테이너 런타임/노드 캐시/레지스트리 제한(최종 수습 단계)
위 1~7단계를 통과했는데도 간헐적으로만 실패한다면, 마지막으로 런타임과 노드 상태를 봅니다.
- 노드 디스크 압박(
DiskPressure)로 이미지 풀 실패 - containerd가 꼬여서 인증 토큰 캐시 문제
- ECR API rate limit/네트워크 순간 장애
확인 명령:
kubectl get node
kubectl describe node <node>
# 노드로 접속 가능하다면(SSM/SSH)
sudo journalctl -u containerd --since "30 min ago" | tail -n 200
sudo df -h
sudo df -i
디스크는 남았는데 inode가 고갈된 케이스도 실제로 자주 나옵니다. 이 경우는 용량 남는데 No space left? inode 고갈 해결법을 같이 보면 원인 파악이 빨라집니다.
재발 방지 체크리스트(운영 기준)
운영 환경에서는 “한 번 해결”보다 “다음에 안 터지게”가 더 중요합니다. 아래를 권장합니다.
- 노드 Role에
AmazonEC2ContainerRegistryReadOnly포함 여부를 IaC로 강제(Terraform/CloudFormation) - 프라이빗 서브넷이면 NAT 또는 VPC 엔드포인트(ECR API/ECR DKR/S3) 표준화
kubectl get events기반의 알람 룰 구축(예:ImagePullBackOff가 5분 이상 지속)- IRSA는 워크로드별로 최소 권한 정책 + Trust Policy
sub를 템플릿화 - 디버그용 네트워크 점검 파드(알파인/넷슈트)를 표준 런북에 포함
부록: 최소 ECR Pull 정책 예시
노드 Role 또는 (특수한 구성에서) 풀 주체에 붙일 수 있는 최소 권한 예시입니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "arn:aws:ecr:<region>:<account-id>:repository/<repo>"
}
]
}
위 정책에서 Resource는 리포지토리 단위로 최소화하고, 멀티 리포지토리면 ARN을 추가하세요.
정리
ImagePullBackOff는 증상은 단순하지만 원인 축이 명확합니다.
- 이벤트 원문으로 방향을 잡고(1단계)
- 이미지/리전 오타를 제거한 뒤(2단계)
- 노드 IAM과 IRSA를 분리해서 보고(3~6단계)
- 마지막으로 네트워크와 런타임 상태를 확인하면(7~8단계)
대부분의 케이스는 30분 안에 원인을 특정할 수 있습니다. 특히 “IRSA를 붙였는데 왜 ECR 풀을 못 하지?” 같은 혼란은 3단계에서 노드 Role을 확인하는 것만으로 빠르게 정리되는 경우가 많습니다.