- Published on
EKS Pod ImagePullBackOff - ECR 권한·IRSA 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스든 노드 그룹이든, EKS에서 ImagePullBackOff는 결국 “컨테이너 이미지를 가져오지 못했다”는 뜻입니다. 하지만 원인은 생각보다 다양합니다. 이미지 태그 오타 같은 단순 실수부터, ECR 권한 누락, IRSA 오해(특히 serviceAccount에 IAM Role을 붙였는데도 풀 실패), 크로스 어카운트 ECR 정책 미설정, 프라이빗 서브넷에서 ECR 엔드포인트 미구성까지 한 번에 얽힙니다.
이 글은 특히 ECR 권한·IRSA 관점에서 ImagePullBackOff를 재현 가능한 체크리스트로 좁혀가는 방법을 다룹니다. IRSA 자체가 안 먹는 경우는 별도 글인 EKS에서 IRSA가 안 먹을 때 STS AccessDenied 해결도 함께 보면 원인 분리가 빨라집니다.
1) 먼저 이벤트에서 “진짜 에러 메시지”를 확보
ImagePullBackOff는 상태값일 뿐이고, 핵심 단서는 Events에 있습니다.
kubectl -n your-ns describe pod your-pod
아래 중 어떤 문구가 보이는지로 분기합니다.
Failed to pull image ...: rpc error: code = Unknown desc = Error response from daemon: pull access deniedno basic auth credentialsdenied: User ... is not authorized to perform: ecr:BatchGetImagei/o timeout또는dial tcp ...:443: i/o timeout
여기서 권한 문제인지 네트워크 문제인지 먼저 갈라야 합니다.
AccessDenied,not authorized,no basic auth credentials는 IAM/ECR/인증 축timeout,no route to host는 VPC 라우팅/NAT/엔드포인트 축
2) 많이 하는 오해: “IRSA를 붙였는데 왜 이미지 Pull이 안 되지?”
중요한 사실부터 정리하면,
- 일반적으로 이미지 Pull은 노드의 IAM(EC2 노드 역할) 권한으로 수행됩니다.
- IRSA는 Pod 내부 AWS SDK 호출 권한(예: S3, DynamoDB)을 주기 위한 메커니즘입니다.
- EKS 기본 동작에서 kubelet/컨테이너 런타임이 이미지를 당겨오는 과정은 Pod의 IRSA 역할을 자동으로 쓰지 않습니다.
즉, serviceAccount에 Role을 붙여도 ImagePullBackOff가 해결되지 않는 경우가 많습니다. 해결책은 대개 노드 IAM Role에 ECR Pull 권한 추가 또는 imagePullSecrets 사용, 혹은 (특수 구성) kubelet credential provider 설정 쪽입니다.
3) 노드 IAM Role에 ECR Pull 권한이 있는지 확인
3-1) 노드가 어떤 IAM Role을 쓰는지 확인
Managed Node Group이면 노드 인스턴스 프로파일(Role)이 있습니다.
kubectl -n kube-system get configmap aws-auth -o yaml
또는 EC2 콘솔에서 노드 인스턴스의 IAM Role을 확인합니다.
3-2) 최소 필요 권한(같은 계정 ECR 기준)
노드 Role에 아래 권한이 필요합니다.
ecr:GetAuthorizationToken(리소스는 보통*)ecr:BatchCheckLayerAvailabilityecr:GetDownloadUrlForLayerecr:BatchGetImage
예시 정책:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "arn:aws:ecr:ap-northeast-2:123456789012:repository/your-repo"
}
]
}
리포지토리를 여러 개 쓰면 Resource를 넓히거나 여러 ARN을 나열합니다.
3-3) AmazonEC2ContainerRegistryReadOnly만으로 충분한가?
대부분 충분합니다. 하지만 아래 케이스에서는 추가 조치가 필요할 수 있습니다.
- 크로스 어카운트 ECR: 노드 Role 권한뿐 아니라 ECR 리포지토리 정책도 필요
- ECR KMS 암호화 사용: KMS 키 정책/권한 추가 필요
- VPC 엔드포인트/프라이빗 DNS 구성 문제: 권한이 있어도 네트워크에서 막힘
4) 크로스 어카운트 ECR이면 “리포지토리 정책”이 핵심
EKS 클러스터(노드 Role)가 있는 계정 A에서, ECR이 계정 B에 있다면 계정 B의 ECR 리포지토리에 리포지토리 정책으로 Pull을 허용해야 합니다.
예시(계정 A의 노드 Role ARN을 허용):
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPullFromAccountA",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/your-eks-node-role"
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
]
}
]
}
여기에 더해 노드 Role 쪽에도 해당 리포지토리 ARN에 대한 ECR 권한이 있어야 합니다. 한쪽만 설정하면 AccessDenied가 계속 납니다.
5) 프라이빗 서브넷에서 i/o timeout이면 ECR 엔드포인트를 점검
권한이 완벽해도, 노드가 인터넷/ECR API에 도달 못하면 Pull이 실패합니다.
5-1) NAT 게이트웨이 없이 프라이빗 서브넷만 쓴다면
다음 중 하나가 필요합니다.
- NAT 게이트웨이로
0.0.0.0/0egress - 또는 VPC Interface Endpoint 구성
ECR은 보통 아래 엔드포인트가 필요합니다.
com.amazonaws.region.ecr.apicom.amazonaws.region.ecr.dkr- 그리고 레이어 다운로드를 위해
com.amazonaws.region.s3(Gateway endpoint)도 사실상 필요해지는 경우가 많습니다(환경에 따라 다름).
보안 그룹/라우트 테이블/프라이빗 DNS 옵션까지 함께 확인해야 합니다.
6) no basic auth credentials가 뜨는 대표 케이스
이 메시지는 “ECR 로그인 토큰을 못 얻었거나, 토큰을 컨테이너 런타임이 못 썼다” 쪽에서 자주 나옵니다.
체크 포인트:
- 노드 Role에
ecr:GetAuthorizationToken이 있는가 - 노드에
aws ecr get-login-password를 수행할 수 있는 네트워크가 있는가 - (자체 구축 AMI/런타임) ECR credential helper/credential provider 구성이 망가졌는가
EKS 표준 AMI를 쓰는 Managed Node Group이면 대개 마지막 항목은 가능성이 낮고, 권한/네트워크가 원인인 경우가 많습니다.
7) IRSA는 어디에 쓰나: “Pod 내부에서 ECR을 직접 호출”하는 경우
기본 이미지 Pull에는 IRSA가 개입하지 않지만, 아래 시나리오에서는 IRSA가 관련될 수 있습니다.
- InitContainer나 앱 컨테이너가 실행 중에 ECR API를 호출
aws ecr batch-get-image같은 동작을 Pod 내부에서 수행
이때는 serviceAccount에 붙인 Role의 신뢰 정책(trust policy)과 OIDC 설정이 정확해야 합니다. IRSA 자체 디버깅은 EKS에서 IRSA가 안 먹을 때 STS AccessDenied 해결에 더 자세히 정리되어 있습니다.
IRSA Role의 신뢰 정책 예시(핵심은 sub 조건):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E:sub": "system:serviceaccount:your-ns:your-sa"
}
}
}
]
}
8) 빠른 재현/검증용: 노드에서 직접 ECR Pull 테스트
운영 환경에서 노드에 직접 접속하기 어렵다면, 최소한 “노드 IAM Role로 ECR 토큰 발급이 되는지”를 확인하는 방식으로 원인을 좁힐 수 있습니다.
예: SSM으로 노드에 들어가서(가능한 경우) 아래를 수행합니다.
aws sts get-caller-identity
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com
docker pull 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/your-repo:your-tag
여기서도 실패하면 Pod 이전에 노드 레벨 문제(권한/네트워크)입니다.
9) imagePullSecrets가 필요한 경우(외부 레지스트리 또는 고정 자격증명)
ECR이 아닌 외부 프라이빗 레지스트리거나, 조직 정책상 특정 자격증명을 강제한다면 imagePullSecrets를 사용합니다.
kubectl -n your-ns create secret docker-registry regcred \
--docker-server=123456789012.dkr.ecr.ap-northeast-2.amazonaws.com \
--docker-username=AWS \
--docker-password="$(aws ecr get-login-password --region ap-northeast-2)"
그리고 Pod/ServiceAccount에 연결:
apiVersion: v1
kind: ServiceAccount
metadata:
name: your-sa
namespace: your-ns
imagePullSecrets:
- name: regcred
다만 ECR 토큰은 만료가 있으므로, 이 방식은 운영 자동화(주기적 갱신) 없이는 관리 부담이 큽니다. 가능하면 노드 IAM Role 기반을 우선으로 두는 편이 안정적입니다.
10) 결론: 원인 분리 체크리스트
마지막으로, 현장에서 가장 빨리 먹히는 순서로 정리합니다.
kubectl describe pod이벤트에서 에러 문구를 확보(권한 vs 네트워크)- 이미지 URI/태그/리전이 정확한지 확인
- 노드 IAM Role에 ECR Pull 권한이 있는지 확인(
ecr:GetAuthorizationToken포함) - 크로스 어카운트면 ECR 리포지토리 정책까지 함께 확인
- 프라이빗 서브넷이면 NAT 또는 ECR/S3 VPC 엔드포인트 구성 확인
- IRSA는 “기본 이미지 Pull 해결책”이 아니라는 점을 전제로, Pod 내부 AWS 호출 권한에만 적용
비슷한 결로, 인증/권한이 맞는데도 403이 나는 케이스는 OIDC 연동에서 자주 터집니다. CI/CD에서 AWS 권한이 403으로 막힐 때는 GitHub Actions OIDC로 AWS 배포 403 해결 가이드도 함께 참고하면 권한 모델을 정리하는 데 도움이 됩니다.