- Published on
EKS Pod ImagePullBackOff 401 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스가 아닌 이상, EKS에서 ImagePullBackOff는 대부분 노드(kubelet)가 이미지를 못 받아오는 문제입니다. 그중 401 Unauthorized는 특히 “네트워크가 아니라 인증”에 초점이 맞춰져야 합니다. 하지만 EKS에서는 인증 주체가 Pod가 아니라 노드의 kubelet/컨테이너 런타임(containerd) 인 경우가 많아, IRSA를 붙여도 해결되지 않는 일이 흔합니다.
이 글에서는 EKS에서 ImagePullBackOff + 401이 발생할 때 원인을 ECR vs 사설 레지스트리, 노드 IAM vs imagePullSecrets, 토큰 만료/스코프, 프라이빗 엔드포인트/리전 불일치 관점으로 빠르게 쪼개어 해결하는 체크리스트를 정리합니다.
관련해서 노드/클러스터 레벨 장애를 함께 점검해야 한다면 Terraform apply 후 EKS 노드 NotReady - CNI·IRSA·보안그룹 점검도 같이 보면 진단 속도가 빨라집니다.
1) 증상 확정: “정말 401인가?” 이벤트부터 본다
먼저 Pod 이벤트에서 정확히 어떤 레지스트리로, 어떤 이유로 실패했는지 확인합니다.
kubectl describe pod -n <ns> <pod>
아래처럼 이벤트에 401 또는 unauthorized: authentication required가 찍히는지 확인합니다.
Failed to pull image "...": rpc error: code = Unknown desc = ... 401 Unauthorizedpull access denied, repository does not exist or may require authorization
여기서 중요한 포인트:
ErrImagePull→ 단발성 실패ImagePullBackOff→ 재시도 백오프로 굳어진 상태(원인 해결 전까지 계속)
원인 분리를 위해 이미지 URL을 먼저 분류합니다.
- ECR 예:
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:tag - Docker Hub 예:
docker.io/library/nginx:latest - 사설(하버/깃랩) 예:
registry.example.com/team/myapp:tag
2) 핵심 개념: EKS에서 이미지 풀 인증 주체는 보통 “노드”다
많이 헷갈리는 지점이 여기입니다.
- IRSA(ServiceAccount IAM Role): Pod 안에서 AWS API를 호출할 때 사용(예: S3, DynamoDB)
- 이미지 Pull: 기본적으로 노드의 kubelet/containerd가 수행
즉, “Pod에 IRSA를 붙였는데도 ECR Pull이 401”이라면, 대개 IRSA는 상관이 없고 노드 IAM Role 또는 imagePullSecrets 문제일 가능성이 큽니다.
3) 케이스 A: ECR에서 401이 나는 경우(가장 흔함)
3.1 노드 IAM Role에 ECR Pull 권한이 없다
EKS Managed Node Group/자체 노드의 IAM Role에 최소 아래 권한이 필요합니다.
ecr:GetAuthorizationTokenecr:BatchGetImageecr:GetDownloadUrlForLayerecr:BatchCheckLayerAvailability
AWS 관리형 정책으로는 보통 다음을 붙입니다.
AmazonEC2ContainerRegistryReadOnly
확인은 노드가 사용하는 인스턴스 프로파일/역할을 확인해야 합니다.
kubectl get node -o wide
# 노드 이름으로 EC2 인스턴스 식별 후 IAM Role 확인
노드 Role이 잘못 붙었거나(다른 Role), 정책이 누락되면 kubelet이 ECR 토큰을 못 받아 401이 납니다.
3.2 ECR 리전 불일치 또는 레지스트리 도메인 오타
ECR은 리전별 엔드포인트가 다릅니다.
ap-northeast-2에 있는 레포를us-east-1도메인으로 당기면 인증/요청이 꼬입니다.- 계정 ID 오타도 401/404를 유발합니다.
체크:
aws ecr describe-repositories --region ap-northeast-2 --repository-names myapp
이미지 참조가 정확한지(계정/리전/레포명) 다시 확인합니다.
3.3 ECR Private Endpoint/프록시 환경에서 토큰 교환이 실패
프라이빗 서브넷 + VPC 엔드포인트 구성이면 다음이 필요합니다.
com.amazonaws.<region>.ecr.apicom.amazonaws.<region>.ecr.dkr- (이미지 레이어는 S3를 통해 내려받는 경우가 많아)
com.amazonaws.<region>.s3게이트웨이 엔드포인트 또는 NAT 경로
이게 없으면 보통은 타임아웃이 더 흔하지만, 환경에 따라 인증 단계가 실패로 보일 수도 있습니다. 네트워크/DNS 이슈가 의심되면 CoreDNS/노드 DNS도 함께 확인하세요. (DNS 계열 장애는 AWS EKS CoreDNS CrashLoopBackOff와 DNS 타임아웃 해결 같은 케이스로 이어질 수 있습니다.)
4) 케이스 B: 사설 레지스트리에서 401이 나는 경우
사설 레지스트리(하버/깃랩/자체 registry)에서 401은 거의 항상 Kubernetes pull secret 또는 자격증명 스코프 문제입니다.
4.1 imagePullSecrets가 없거나, 네임스페이스가 다르다
imagePullSecrets는 네임스페이스 단위 리소스입니다.
- secret을
default에 만들고 - Pod는
prod네임스페이스에서 실행하면
Pod는 secret을 못 보고 401이 납니다.
확인:
kubectl get secret -n <ns>
kubectl describe pod -n <ns> <pod> | sed -n '/Image Pull Secrets/,$p'
4.2 docker-registry 타입 secret 생성/갱신
가장 안전한 방식은 kubernetes.io/dockerconfigjson 타입으로 만드는 것입니다.
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username='<USER>' \
--docker-password='<PASSWORD_OR_TOKEN>' \
--docker-email='devnull@example.com' \
-n <ns>
그리고 Deployment/Pod spec에 연결합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: prod
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
imagePullSecrets:
- name: regcred
containers:
- name: myapp
image: registry.example.com/team/myapp:1.0.0
이미 존재하는 ServiceAccount에 기본 pull secret을 붙여서, 해당 SA를 쓰는 모든 Pod에 적용할 수도 있습니다.
kubectl patch serviceaccount default -n prod \
-p '{"imagePullSecrets": [{"name": "regcred"}]}'
4.3 토큰/패스워드 만료 또는 2FA로 인한 실패
GitLab/Harbor/Docker Hub는 “비밀번호” 대신 Personal Access Token을 요구하거나, 2FA 활성화 시 비밀번호 로그인이 막힙니다.
증상은 동일하게 401로 떨어지므로, 아래를 확인하세요.
- 토큰 만료 여부
- 토큰 scope:
read_registry(GitLab),pull권한 등 - 레포/프로젝트 접근 권한(비공개 프로젝트)
secret을 갱신했다면 Pod를 재시작해 재풀을 유도합니다.
kubectl rollout restart deploy/myapp -n prod
5) “ECR인데 imagePullSecrets로 해결되던데?”에 대한 설명
ECR도 표준 Docker Registry API를 쓰기 때문에 docker login으로 발급한 토큰을 .dockerconfigjson로 넣어 풀 수는 있습니다. 하지만 ECR 토큰은 기본적으로 12시간 만료라 운영 환경에서 권장되지 않습니다.
대신 권장 패턴은:
- 노드 IAM Role에
AmazonEC2ContainerRegistryReadOnly부여 - (가능하면) Managed Node Group 사용 시 기본 권한 확인
특정 워크로드만 별도 자격증명을 쓰고 싶다면, ECR credential provider(노드 구성) 또는 외부 컨트롤러를 고려해야 하지만, 우선은 “노드 Role로 풀게 한다”가 가장 단순하고 안정적입니다.
6) 컨테이너 런타임 관점: 노드에서 직접 재현해 원인 좁히기
노드에 SSM으로 접속 가능하다면(또는 디버그용 ephemeral container), containerd가 실제로 어떤 에러를 내는지 확인하면 빨라집니다.
6.1 kubelet/containerd 로그 확인
Amazon Linux 2 기반 EKS 노드라면 보통:
sudo journalctl -u kubelet -n 200 --no-pager
sudo journalctl -u containerd -n 200 --no-pager
여기서 401이 반복되면 “Pod spec” 문제가 아니라 “노드 인증/설정” 문제일 확률이 큽니다.
6.2 이미지 풀을 노드에서 직접 시도
환경에 따라 crictl이 있을 수 있습니다.
sudo crictl pull 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:tag
- 여기서도 401이면 노드 IAM/ECR endpoint 문제
- 여기서는 성공하고 Pod만 실패하면 imagePullSecrets/SA/네임스페이스/이미지명 문제 가능성이 큼
7) 자주 하는 실수 Top 7 체크리스트
- IRSA로 이미지 풀을 해결하려고 함 → 대개 노드 IAM 문제
- secret을 다른 네임스페이스에 생성
--docker-server값 불일치 (예:https://registry...붙이거나, 포트 포함 누락)- ECR 리전/계정 ID 오타
- 토큰 만료(GitLab PAT/ECR 12h 토큰)
- 레포 권한 없음(private repo)
- VPC 엔드포인트/S3 경로 누락(프라이빗 클러스터)
8) 문제 해결 후 검증 절차
- 기존 실패 Pod를 지우거나 롤아웃 재시작
kubectl delete pod -n <ns> <pod>
# 또는
kubectl rollout restart deploy/<deploy> -n <ns>
- 새 Pod 이벤트에서
Pulled/Created/Started확인
kubectl get pod -n <ns> -w
kubectl describe pod -n <ns> <new-pod>
- 동일 노드에서만 실패한다면 노드 단위 캐시/설정 문제일 수 있으니, 노드 교체/드레인으로 재현성 확인
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
9) 결론: 401은 “인증 주체”를 먼저 고정하면 빨리 끝난다
EKS에서 ImagePullBackOff (401)는 원인이 다양해 보이지만, 실제로는 아래 두 갈래로 정리됩니다.
- ECR: 노드 IAM Role + ECR 리전/엔드포인트(그리고 S3 경로)
- 사설 레지스트리:
imagePullSecrets(네임스페이스/서버값/토큰 스코프)
진단의 출발점은 항상 kubectl describe pod 이벤트이며, “누가(노드 vs Pod) 인증하는가”를 먼저 확정하면 불필요한 IRSA/권한 삽질을 크게 줄일 수 있습니다.
추가로 EKS에서 노드 레벨 이슈가 동반되는 경우(노드가 꼬여서 kubelet이 이상 동작, NotReady 등)에는 EKS에서 kubelet node not found 오류 해결도 함께 점검하면 전체 장애 범위를 더 빨리 좁힐 수 있습니다.