Published on

EKS ImagePullBackOff·ErrImagePull 해결 9가지

Authors

서버리스가 아닌 이상, 컨테이너 배포 장애의 상당수는 결국 이미지 Pull 단계에서 터집니다. EKS에서 ImagePullBackOff 또는 ErrImagePull이 뜨면 애플리케이션 로그조차 남지 않는 경우가 많아, 원인 분류와 재현 가능한 진단 루틴이 중요합니다.

이 글은 EKS에서 자주 만나는 Pull 실패를 9가지로 나누고, 각 케이스별로 “어디를 봐야 하는지”와 “어떻게 고치는지”를 실전 커맨드 중심으로 정리합니다.

먼저: 증상과 원인 단서 빠르게 모으기

가장 먼저 해야 할 일은 이벤트에서 “정확한 에러 문자열”을 확보하는 것입니다. ImagePullBackOff는 결과 상태이고, 실제 원인은 이벤트의 Failed 메시지에 들어 있습니다.

# 1) 파드 상태와 이미지 확인
kubectl get pod -n <namespace> <pod-name> -o wide

# 2) 이벤트에서 실제 에러 메시지 확인
kubectl describe pod -n <namespace> <pod-name>

# 3) 특정 컨테이너만 골라 보기
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.status.containerStatuses[*].state.waiting.message}'

자주 보이는 메시지 패턴은 다음과 같습니다.

  • no basic auth credentials
  • pull access denied
  • manifest unknown
  • x509: certificate signed by unknown authority
  • i/o timeout, TLS handshake timeout, connection reset by peer
  • toomanyrequests: You have reached your pull rate limit

이제부터는 원인별로 쪼개어 해결합니다.

1) 이미지 이름·태그 오타, 혹은 존재하지 않는 매니페스트

가장 흔하지만 가장 먼저 배제해야 하는 케이스입니다. 태그 오타, 잘못된 리포지토리 경로, 혹은 멀티 아키텍처 매니페스트가 없는 이미지에서 자주 발생합니다.

진단

# 디플로이먼트에 실제로 어떤 이미지가 박혀 있는지 확인
kubectl get deploy -n <namespace> <deploy-name> -o jsonpath='{.spec.template.spec.containers[*].image}'

# ECR이라면 리포지토리/태그 존재 여부 확인
aws ecr describe-images \
  --repository-name <repo> \
  --image-ids imageTag=<tag>

해결

  • 이미지 경로를 정확히 맞추고, 가능하면 latest 대신 고정 태그 또는 다이제스트를 사용합니다.
  • 운영에서는 다음처럼 다이제스트 핀을 추천합니다.
# deployment.yaml 일부
containers:
  - name: app
    image: <account-id>.dkr.ecr.<region>.amazonaws.com/<repo>@sha256:<digest>

2) ECR 인증 실패: IRSA 미구성·노드 IAM 권한 부족

EKS에서 ECR을 쓰는데 no basic auth credentials 또는 pull access denied가 나오면, 대개 “노드가 ECR을 읽을 권한이 없거나” “프라이빗 레지스트리 인증이 파드에 주입되지 않은” 상태입니다.

ECR Pull은 기본적으로 노드 IAM Role 권한이 중요합니다. Fargate나 특수 구성에서는 IRSA로도 접근하지만, 일반적인 EC2 노드 그룹은 노드 Role에 AmazonEC2ContainerRegistryReadOnly가 있어야 합니다.

진단

# 노드가 어떤 IAM Role을 쓰는지 확인(노드 이름은 get pod -o wide로 확인)
kubectl get node <node-name> -o jsonpath='{.spec.providerID}'

# 노드 그룹 Role 확인
aws eks describe-nodegroup \
  --cluster-name <cluster> \
  --nodegroup-name <nodegroup> \
  --query 'nodegroup.nodeRole'

해결

  • 노드 Role에 다음 권한을 부여합니다.
    • ecr:GetAuthorizationToken
    • ecr:BatchGetImage
    • ecr:GetDownloadUrlForLayer
    • ecr:BatchCheckLayerAvailability
  • 가장 빠른 방법은 AWS 관리 정책 AmazonEC2ContainerRegistryReadOnly를 붙이는 것입니다.

참고로, ACR 인증 이슈를 다룬 글이지만 “인증 실패 이벤트를 어떻게 읽고, 어떤 자격 증명이 필요한지” 흐름은 유사합니다.

3) imagePullSecrets 누락 또는 잘못된 Secret 타입

ECR이 아닌 외부 프라이빗 레지스트리(예: Harbor, GitHub Container Registry, Docker Hub private)를 쓰면 imagePullSecrets가 필요합니다.

진단

kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.imagePullSecrets}'

kubectl get secret -n <namespace> <secret-name> -o yaml

Secret 타입이 kubernetes.io/dockerconfigjson인지 확인합니다.

해결

kubectl create secret docker-registry regcred \
  -n <namespace> \
  --docker-server=<registry-host> \
  --docker-username=<username> \
  --docker-password=<password> \
  --docker-email=<email>

그리고 워크로드에 연결합니다.

spec:
  template:
    spec:
      imagePullSecrets:
        - name: regcred

4) ECR 리포지토리 정책(Resource Policy) 또는 KMS 암호화 이슈

조직 계정/크로스 계정에서 ECR을 공유하거나, 리포지토리 정책으로 접근을 제한한 경우 “권한이 있는데도 Pull이 안 되는” 상황이 생깁니다.

또한 ECR이 KMS로 암호화되어 있고, 키 정책이 노드 Role을 허용하지 않으면 이미지 레이어 다운로드에서 실패할 수 있습니다.

진단

aws ecr get-repository-policy --repository-name <repo>

# KMS 키를 쓴다면 키 정책/그랜트 확인
aws ecr describe-repositories --repository-names <repo> \
  --query 'repositories[0].encryptionConfiguration'

해결

  • ECR 리포지토리 정책에 노드 Role 또는 해당 계정을 명시적으로 허용합니다.
  • KMS 키 정책에 kms:Decrypt가 가능하도록 주체를 추가합니다.

5) 네트워크: 프라이빗 서브넷에서 ECR 엔드포인트 미구성

프라이빗 서브넷에서 NAT 없이 운영하거나, egress가 제한된 환경에서는 ECR로 나가는 경로가 없어 Pull이 실패합니다. 이때 이벤트에 i/o timeout, TLS handshake timeout 같은 메시지가 자주 보입니다.

ECR Pull은 보통 다음이 필요합니다.

  • ecr.api (인증 토큰/메타)
  • ecr.dkr (이미지 레지스트리)
  • s3 (레이어가 S3를 통해 내려오는 경로)
  • DNS 해석 경로(코어DNS 및 VPC DNS)

진단

노드에서 외부로 나갈 수 있는지 확인합니다. 가장 간단한 방법은 임시 디버그 파드를 띄우는 것입니다.

kubectl run netdebug -n <namespace> --rm -it \
  --image=public.ecr.aws/docker/library/busybox:1.36 \
  --restart=Never -- sh

# 파드 내부에서 DNS/HTTPS 확인
nslookup <account-id>.dkr.ecr.<region>.amazonaws.com
wget -S -O - https://<account-id>.dkr.ecr.<region>.amazonaws.com 2>&1 | head

해결

  • NAT Gateway를 붙이거나
  • VPC Interface Endpoint를 구성합니다.
    • com.amazonaws.<region>.ecr.api
    • com.amazonaws.<region>.ecr.dkr
    • com.amazonaws.<region>.sts (환경에 따라 필요)
  • Gateway Endpoint로 com.amazonaws.<region>.s3를 추가합니다.

6) 보안그룹·네트워크 ACL·프록시로 인한 TLS 차단

보안 장비, 프록시, 혹은 과도한 아웃바운드 제한이 있으면 레지스트리 접속은 되지만 TLS 단계에서 끊기기도 합니다.

진단

  • 이벤트에 x509 또는 remote error: tls 류 메시지 확인
  • 노드의 보안그룹 egress가 443을 허용하는지 확인
aws ec2 describe-security-groups --group-ids <sg-id>
aws ec2 describe-network-acls --network-acl-ids <nacl-id>

해결

  • 노드 egress에서 목적지 443 허용
  • 프록시를 쓴다면 containerd가 프록시 환경변수를 제대로 받는지 점검
  • 사설 CA를 쓰는 프록시라면, 노드/런타임에 CA를 신뢰하도록 배포(조직 표준에 맞게)

7) Docker Hub 레이트 리밋 또는 외부 레지스트리 쿼터

EKS 노드가 스케일 아웃될 때 Docker Hub의 익명 Pull 제한에 걸리면 toomanyrequests로 실패합니다. 이 경우 ImagePullBackOff가 파도처럼 번집니다.

진단

kubectl describe pod 이벤트에서 toomanyrequests 문자열을 찾습니다.

해결

  • 베이스 이미지를 public.ecr.aws 미러로 전환
  • Docker Hub 유료 계정으로 인증을 붙여 imagePullSecrets 구성
  • 이미지 프록시/캐시 레지스트리(예: Harbor proxy cache) 도입

“레이트리밋에 걸렸을 때 재시도 설계” 관점은 API 호출이지만 운영 감각은 동일합니다.

8) 아키텍처 불일치: ARM 노드에 AMD64 이미지를 배포

Graviton(ARM64) 노드 그룹에 AMD64 전용 이미지를 배포하면 Pull 자체가 실패하거나(매니페스트 미존재), Pull은 되는데 실행 단계에서 exec format error로 죽기도 합니다. 팀에 ARM 노드가 섞이기 시작하면 빈도가 급격히 올라갑니다.

진단

# 노드 아키텍처 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.architecture}{"\n"}{end}'

# 파드가 어느 노드로 스케줄됐는지 확인
kubectl get pod -n <namespace> <pod-name> -o wide

해결

  • 멀티 아키텍처 이미지를 빌드/푸시합니다.
# buildx 예시
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t <repo>:<tag> \
  --push .
  • 또는 노드 셀렉터로 아키텍처를 고정합니다.
spec:
  template:
    spec:
      nodeSelector:
        kubernetes.io/arch: amd64

9) 노드 런타임(containerd)·디스크 압박으로 Pull 실패

이미지가 너무 크거나, 노드의 ephemeral 스토리지가 부족하면 레이어를 풀다가 실패합니다. 이때 이벤트에 no space left on device 또는 failed to extract layer가 등장합니다.

또한 containerd 상태가 꼬이거나, 이미지 GC가 제때 돌지 않아 디스크가 지속적으로 압박받는 경우도 있습니다.

진단

# 노드 디스크 압박 조건 확인
kubectl describe node <node-name> | sed -n '1,200p'

# 파드 이벤트에서 용량 관련 메시지 확인
kubectl describe pod -n <namespace> <pod-name>

해결

  • 이미지 사이즈를 줄입니다(멀티 스테이지 빌드, 불필요 패키지 제거).
  • 노드 볼륨(EBS) 용량 확장 또는 더 큰 인스턴스/런타임 디스크 구성
  • 임시로는 노드 교체(드레인 후 새 노드로 롤링)도 빠른 복구책입니다.

이미지 빌드/캐시가 꼬여 결과물이 비대해지는 케이스도 많습니다. 빌드 파이프라인을 함께 점검하면 Pull 장애 빈도를 줄일 수 있습니다.

운영자가 쓰는 “원인 분류” 체크리스트

아래 순서대로 보면 대부분 5분 안에 방향이 잡힙니다.

  1. kubectl describe pod 이벤트에서 에러 문자열 확보
  2. manifest unknown이면 이미지 경로/태그부터
  3. no basic auth credentials이면 인증(노드 Role, IRSA, imagePullSecrets)
  4. i/o timeout이면 네트워크(NAT, VPC Endpoint, SG egress)
  5. x509이면 TLS 가로채기/사설 CA/프록시
  6. toomanyrequests이면 레지스트리 레이트리밋
  7. 노드가 ARM인지 확인(혼합 노드 그룹이면 특히)
  8. no space left on device면 노드 디스크/이미지 슬림화

재발 방지: 배포 파이프라인에 넣을 가드레일

장애를 “한 번 고치고 끝”내지 않으려면 CI와 클러스터 설정에 가드레일을 넣는 게 효과적입니다.

  • 이미지 태그 정책: latest 금지, 다이제스트 핀 권장
  • 멀티 아키텍처 빌드 표준화: linux/amd64linux/arm64 동시 푸시
  • 프라이빗 서브넷 표준: NAT 또는 ECR/S3 VPC Endpoint를 IaC로 강제
  • 레지스트리 인증 표준: 외부 레지스트리 사용 시 imagePullSecrets 템플릿화
  • 노드 스토리지 모니터링: 디스크 압박 알림과 노드 교체 자동화

마무리

EKS의 ImagePullBackOff·ErrImagePull은 겉으로는 비슷하지만, 이벤트 메시지 한 줄이 네트워크 문제인지 인증 문제인지, 혹은 이미지 아키텍처 문제인지 빠르게 갈라줍니다. 위 9가지를 “메시지 패턴 기준”으로 매핑해두면, 장애 대응 속도와 재발 방지 품질이 확실히 올라갑니다.

원하시면 현재 이벤트 메시지( kubectl describe pod 중 Events 섹션 )를 붙여주시면, 어떤 9가지 중 어디에 해당하는지와 최소 변경으로 복구하는 순서를 구체적으로 짚어드릴게요.