Published on

Kubernetes ImagePullBackOff - ECR 403 해결 가이드

Authors

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 Forbidden
  • pull access denied 혹은 no basic auth credentials
  • failed to fetch oauth token 같은 형태(레지스트리/인증 경로 문제)

여기서 중요한 포인트는 “403이 ECR API에서 난 것인지, Docker Registry 엔드포인트에서 난 것인지”입니다. 메시지에 ecr.amazonaws.com 또는 dkr.ecr.<region>.amazonaws.com가 등장하는지 체크하세요.

2) 가장 흔한 원인: 노드(또는 Pod)가 ECR 권한이 없다

ECR은 크게 두 단계 권한이 필요합니다.

  1. 인증 토큰 획득: ecr:GetAuthorizationToken
  2. 이미지 레이어 조회/다운로드: 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) 실전 해결 순서(가장 빠른 루트)

현장에서 시간을 아끼려면 아래 순서가 효율적입니다.

  1. kubectl describe pod 이벤트에서 에러 문자열 확정(403 + 어떤 호스트인지)
  2. 이미지 주소의 ACCOUNT_ID, REGION, REPO:TAG가 맞는지 확인
  3. 노드 Role에 AmazonEC2ContainerRegistryReadOnly가 붙어 있는지 확인
  4. 크로스 계정이면 레포지토리 정책에서 pull Principal 허용 확인
  5. 프라이빗 네트워크면 NAT 또는 ECR VPC 엔드포인트 확인
  6. imagePullSecret을 쓰는 구조면 토큰 만료/네임스페이스 불일치 점검
  7. 커스텀 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 엔드포인트)를 표준 체크로 포함시키는 것이 좋습니다.