- Published on
Kubernetes ImagePullBackOff - ECR 403 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Kubernetes에서 배포가 잘 되다가 갑자기 ImagePullBackOff가 뜨고, 이벤트를 보면 ECR 쪽에서 403이 보이는 경우가 있습니다. 겉으로는 단순히 “이미지 못 가져옴”이지만, 실제 원인은 크게 세 갈래로 나뉩니다.
- 인증 토큰을 못 받았거나(또는 만료/잘못된 레지스트리)
- 토큰은 받았는데 레포지토리 권한이 없거나
- ECR에 붙는 네트워크 경로가 꼬여서 인증/요청이 정상 완료되지 않은 경우
이 글은 kubectl describe pod 이벤트를 출발점으로, EKS를 포함한 Kubernetes 환경에서 ECR 403을 재현 가능한 체크리스트로 해결하는 방법을 정리합니다.
1) 증상 확인: ImagePullBackOff와 이벤트에서 단서 찾기
먼저 실제로 403이 어디서 발생했는지 확인해야 합니다. ImagePullBackOff는 결과일 뿐이고, 원인은 이벤트 메시지에 있습니다.
kubectl get pod -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
Events 섹션에서 흔히 보이는 패턴은 다음과 같습니다.
Failed to pull image ...뒤에403 Forbiddenpull access denied혹은no basic auth credentialsfailed to fetch oauth token같은 형태(레지스트리/인증 경로 문제)
여기서 중요한 포인트는 “403이 ECR API에서 난 것인지, Docker Registry 엔드포인트에서 난 것인지”입니다. 메시지에 ecr.amazonaws.com 또는 dkr.ecr.<region>.amazonaws.com가 등장하는지 체크하세요.
2) 가장 흔한 원인: 노드(또는 Pod)가 ECR 권한이 없다
ECR은 크게 두 단계 권한이 필요합니다.
- 인증 토큰 획득:
ecr:GetAuthorizationToken - 이미지 레이어 조회/다운로드:
ecr:BatchGetImage,ecr:GetDownloadUrlForLayer,ecr:BatchCheckLayerAvailability
노드 IAM Role(EC2 인스턴스 프로파일) 또는 Pod IAM Role(IRSA)을 쓰는지에 따라 권한을 부여할 위치가 달라집니다.
2-1) EKS에서 노드 Role에 ECR 권한이 있는지 확인
노드 그룹을 쓴다면 보통 노드 Role에 AmazonEC2ContainerRegistryReadOnly를 붙입니다.
aws eks describe-nodegroup \
--cluster-name <cluster> \
--nodegroup-name <nodegroup> \
--query 'nodegroup.nodeRole'
출력된 Role에 정책이 붙어 있는지 확인합니다.
aws iam list-attached-role-policies --role-name <role-name>
권장 빠른 조치(읽기 전용):
aws iam attach-role-policy \
--role-name <role-name> \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
2-2) IRSA를 쓰는 경우: Pod가 노드 권한을 “대체”하지 않는지 확인
일부 구성에서는 이미지 풀을 Pod의 IRSA로 해결하려고 시도하지만, 기본적으로 이미지 풀은 kubelet(노드)에서 수행됩니다. 즉, IRSA를 잘 붙였더라도 노드 Role에 ECR 권한이 없으면 여전히 실패할 수 있습니다.
다만 환경에 따라(특정 런타임/설정) 동작이 달라 혼동이 잦습니다. IRSA/STS 권한 문제로 꼬이는 케이스는 아래 글도 함께 참고하면 좋습니다.
3) 403인데 no basic auth credentials가 같이 보이면: imagePullSecret 또는 토큰 생성이 실패
ECR은 도커 로그인 토큰을 주기적으로 갱신해야 합니다(기본 12시간). Kubernetes에서는 보통 두 방식 중 하나를 씁니다.
- 노드 IAM 권한으로 kubelet이 자동으로 ECR에서 pull
imagePullSecret에 도커 레지스트리 인증을 넣어 Pod에 제공
온프레미스 K8s, 또는 EKS지만 노드 권한을 최소화하려는 환경에서 imagePullSecret을 쓰는 경우가 많습니다.
3-1) 올바른 레지스트리 주소인지 확인
ECR은 레지스트리 주소가 정확해야 합니다.
- 올바른 형태:
ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/REPO:TAG
리전이 다르거나, 계정이 다른데 같은 레포지토리 이름을 쓰면 403 또는 인증 실패가 섞여 보일 수 있습니다.
3-2) imagePullSecret 생성/갱신
아래는 ECR 토큰으로 docker-registry 타입 시크릿을 만드는 예시입니다.
AWS_REGION=ap-northeast-2
ACCOUNT_ID=123456789012
NAMESPACE=default
SECRET_NAME=ecr-pull
PASSWORD=$(aws ecr get-login-password --region $AWS_REGION)
kubectl create secret docker-registry $SECRET_NAME \
--docker-server=${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com \
--docker-username=AWS \
--docker-password="$PASSWORD" \
-n $NAMESPACE
Deployment에 적용:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
imagePullSecrets:
- name: ecr-pull
containers:
- name: app
image: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myrepo:1.0.0
이미 시크릿이 있는데도 실패한다면 “토큰 만료” 또는 “시크릿이 다른 네임스페이스에 있음”이 흔한 원인입니다. 시크릿은 네임스페이스 스코프라서, Pod가 있는 네임스페이스에 존재해야 합니다.
kubectl get secret -n <namespace>
kubectl describe secret ecr-pull -n <namespace>
4) 레포지토리 정책(Repo policy) 또는 KMS 암호화 때문에 403이 나는 경우
권한을 Role에 줬는데도 403이면, 다음을 의심합니다.
- ECR 레포지토리 정책에서 해당 Principal을 거부
- 크로스 계정 ECR인데 레포지토리 정책 미설정
- ECR이 KMS로 암호화되어 있고, KMS 키 사용 권한이 없음
4-1) 크로스 계정 ECR pull 체크
이미지가 A 계정의 ECR에 있고, 클러스터는 B 계정에서 돌면 B 계정의 노드 Role에 ECR 권한만 줘서는 부족할 수 있습니다. 레포지토리 정책에서 B 계정 Principal을 허용해야 합니다.
레포지토리 정책 확인:
aws ecr get-repository-policy \
--repository-name <repo> \
--region <region>
정책에 ecr:BatchGetImage 등 pull 액션이 허용되어 있는지 확인하세요.
4-2) KMS 키 권한
ECR 레포지토리가 KMS CMK로 암호화되어 있다면, 노드 Role(또는 pull에 쓰이는 Principal)이 KMS 키에 대해 kms:Decrypt 권한이 필요할 수 있습니다. 이 경우 이벤트에는 ECR 403으로 뭉뚱그려 보이기도 합니다.
5) 네트워크가 원인인데 403으로 보이는 함정
대부분 네트워크 문제는 timeout이나 i/o timeout으로 보이지만, 프록시/방화벽/미들박스가 개입하면 403으로 변형되는 경우가 있습니다.
체크 포인트:
- 노드가 퍼블릭 인터넷으로 ECR에 나갈 수 있는가
- 프라이빗 서브넷이면 NAT 게이트웨이 또는 ECR VPC 엔드포인트가 있는가
- 보안 그룹/NACL에서
443이 막히지 않았는가 - DNS는 되는데 TLS만 실패하는 특이 케이스는 없는가
EKS에서 “DNS는 되는데 HTTPS만 실패” 같은 네트워크 계층 문제는 원인이 이미지 풀 실패로도 이어집니다.
간단한 네트워크 확인은 노드 또는 디버그 Pod에서 레지스트리 엔드포인트로 443 연결이 되는지 보는 것입니다.
kubectl run net-debug \
--image=public.ecr.aws/docker/library/busybox:1.36 \
--restart=Never -it --rm \
-- sh
# 컨테이너 안에서
wget -S -O - https://123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/v2/ 2>&1 | head
응답이 401이면 “레지스트리 도달은 됨(인증 필요)”로 해석할 수 있어 네트워크는 대체로 정상입니다. 반면 여기서도 403이면 프록시 차단이나 정책 기반 차단을 의심해야 합니다.
6) 런타임/노드 설정 이슈: credential provider 또는 kubelet 설정
EKS AMI는 보통 ECR credential provider 구성이 되어 있어 노드 Role로 자동 pull이 됩니다. 하지만 다음 상황에서는 깨질 수 있습니다.
- 커스텀 AMI에서 credential provider 바이너리/설정 누락
- containerd 설정 변경으로 credential helper 연동 깨짐
- kubelet 플래그/설정이 꼬여 레지스트리 인증 흐름이 비정상
이 경우 노드에서 직접 journalctl로 kubelet 로그를 확인하거나, 노드에 접속해 containerd 로그를 확인해야 합니다.
# 노드에서
sudo journalctl -u kubelet -n 200 --no-pager
로그에 ecr-credential-provider 관련 에러가 보이면 AMI/부트스트랩 스크립트 변경 이력을 먼저 되짚는 게 빠릅니다.
7) 실전 해결 순서(가장 빠른 루트)
현장에서 시간을 아끼려면 아래 순서가 효율적입니다.
kubectl describe pod이벤트에서 에러 문자열 확정(403+ 어떤 호스트인지)- 이미지 주소의
ACCOUNT_ID,REGION,REPO:TAG가 맞는지 확인 - 노드 Role에
AmazonEC2ContainerRegistryReadOnly가 붙어 있는지 확인 - 크로스 계정이면 레포지토리 정책에서 pull Principal 허용 확인
- 프라이빗 네트워크면 NAT 또는 ECR VPC 엔드포인트 확인
imagePullSecret을 쓰는 구조면 토큰 만료/네임스페이스 불일치 점검- 커스텀 AMI/런타임 변경이 있었다면 credential provider 로그 확인
권한 문제를 다루다 보면 다른 401/403 유형의 인증 오류 디버깅 방법론이 그대로 재사용됩니다. 인증/인가를 “어떤 주체가 어떤 리소스에 어떤 액션을 하려다 막혔는지”로 분해하는 습관이 중요합니다.
8) 체크리스트: ECR pull에 필요한 최소 IAM 액션
직접 정책을 만들고 싶다면 아래 액션을 포함하세요.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
}
]
}
운영 환경에서는 Resource를 특정 레포지토리 ARN으로 제한하고, 크로스 계정이면 레포지토리 정책과 함께 최소 권한으로 조정하는 것을 권장합니다.
마무리
ImagePullBackOff와 ECR 403은 “인증 토큰 단계”, “레포지토리 권한 단계”, “네트워크/런타임 단계” 중 어디에서 막혔는지를 먼저 분리하면 해결이 급격히 빨라집니다. 특히 EKS에서는 노드 Role 권한만으로 해결되는 경우가 가장 많고, 그 다음이 크로스 계정 레포지토리 정책, 마지막이 프라이빗 네트워크/NAT/VPC 엔드포인트 이슈입니다.
다음에 같은 문제가 재발하지 않게 하려면, 배포 파이프라인에서 이미지 URL의 계정/리전 검증, 노드 Role 정책 검증, 그리고 프라이빗 서브넷의 ECR 접근 경로(NAT 또는 VPC 엔드포인트)를 표준 체크로 포함시키는 것이 좋습니다.