- Published on
Kubernetes ImagePullBackOff - ECR 인증 만료 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
운영 중인 EKS/Kubernetes에서 갑자기 ImagePullBackOff가 터지면 대부분은 “이미지 태그가 잘못됐나?”부터 의심합니다. 하지만 AWS ECR을 쓰는 환경에서는 ECR 인증 토큰(12시간 유효), 노드/파드의 IAM 경로, 네트워크에서 ECR API/STS로 나가는 경로 중 하나가 깨지면서 동일한 증상으로 나타나는 경우가 많습니다.
이 글은 ImagePullBackOff가 떴을 때 이벤트 로그에서 ECR 인증 만료(Expired/Unauthorized) 계열을 빠르게 판별하고, 단기 처방(Secret 갱신)과 근본 해결(노드 IAM/IRSA/프라이빗 엔드포인트)까지 한 번에 정리합니다.
증상 확인: ImagePullBackOff가 “ECR 인증”인지 먼저 판별
먼저 파드 이벤트를 봅니다.
kubectl describe pod <pod> -n <ns>
다음과 같은 메시지가 보이면 ECR 인증/권한/토큰 문제일 확률이 큽니다.
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:BatchGetImageunauthorized: authentication required
여기서 중요한 포인트는 Kubernetes가 이미지를 pull할 때 사용하는 주체는 기본적으로 “노드(워커) IAM” 이라는 점입니다. (일반적인 구성에서 kubelet/컨테이너 런타임이 노드 자격증명으로 ECR에 접근)
즉, 애플리케이션 파드에 IRSA를 붙였다고 해서 이미지 pull 권한이 자동으로 해결되지는 않습니다.
ECR 인증 토큰의 구조: 왜 “만료”가 자주 보이나?
ECR은 Docker Registry 인증을 위해 GetAuthorizationToken으로 base64 인코딩된 사용자/비밀번호를 발급합니다. 이 토큰은 기본적으로 12시간 만료이며, 다음 두 가지 방식 중 하나로 클러스터에 적용됩니다.
- 노드가 ECR API를 호출해 자동으로 토큰을 받아서 pull (EKS 관리형 노드그룹/권장 구성)
- 사람이
docker login또는imagePullSecret을 만들어서 수동으로 토큰을 주입
2)번은 시간이 지나면 12시간 후 반드시 만료하므로, 운영에서 가장 흔한 장애 패턴이 됩니다.
원인 1) imagePullSecret로 ECR 토큰을 넣어둔 경우(만료)
진단
다음처럼 imagePullSecrets가 붙어 있고, 그 값이 kubernetes.io/dockerconfigjson 타입이면 수동 토큰일 가능성이 큽니다.
kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.imagePullSecrets[*].name}'
kubectl get secret <secret> -n <ns> -o yaml
단기 해결(즉시 복구): Secret 재생성
AWS CLI로 토큰을 다시 받아 Secret을 갱신합니다.
AWS_REGION=ap-northeast-2
ACCOUNT_ID=123456789012
aws ecr get-login-password --region "$AWS_REGION" \
| kubectl create secret docker-registry ecr-pull \
--docker-server="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com" \
--docker-username=AWS \
--docker-password-stdin \
-n <ns> \
--dry-run=client -o yaml \
| kubectl apply -f -
그리고 디플로이먼트를 롤아웃합니다.
kubectl rollout restart deploy/<deploy> -n <ns>
근본 해결: “수동 Secret”을 제거하고 노드 IAM 기반으로 전환
장기 운영에서는 ECR 토큰을 Secret로 넣는 패턴을 피하는 것이 좋습니다.
- EKS/EC2 노드 IAM Role에 ECR Pull 권한 부여
- kubelet/컨테이너 런타임이 자동으로 ECR 인증을 처리하도록 구성
수동 Secret을 꼭 써야 한다면, CronJob으로 주기 갱신(예: 6시간마다)하는 방식이 필요하지만, 운영 복잡도가 커집니다.
원인 2) 노드 IAM Role에 ECR Pull 권한이 없거나 경로가 깨짐
노드가 ECR에서 이미지를 받아오려면 최소 아래 권한이 필요합니다.
ecr:GetAuthorizationTokenecr:BatchGetImageecr:GetDownloadUrlForLayerecr:BatchCheckLayerAvailability
빠른 체크: 노드가 어떤 Role을 쓰는지 확인
노드의 인스턴스 프로파일/Role을 확인합니다.
kubectl get nodes -o wide
# 노드 이름으로 EC2 인스턴스 찾은 뒤 IAM Role 확인
EKS 관리형 노드그룹이면 보통 AmazonEC2ContainerRegistryReadOnly 정책을 노드 Role에 붙이면 해결됩니다.
ECR 권한 정책 예시(최소 권한)
특정 리포지토리만 허용하는 형태로도 줄일 수 있습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
],
"Resource": "arn:aws:ecr:ap-northeast-2:123456789012:repository/my-repo"
}
]
}
원인 3) STS/토큰 계열 오류(ExpiredToken)로 ECR 호출 자체가 실패
ECR 토큰을 받으려면(= GetAuthorizationToken) 결국 AWS API 호출이 필요하고, 이 과정에서 STS 자격증명 만료/시간 오차/IRSA 설정 문제가 있으면 ECR 인증이 “만료처럼” 보이기도 합니다.
특히 파드가 AWS SDK를 쓰는 경우에는 ExpiredToken이 더 노골적으로 드러나지만, 노드 측 자격증명이 꼬여도 유사 현상이 발생할 수 있습니다.
관련해서 STS ExpiredToken을 파드 관점에서 해결하는 체크리스트는 아래 글이 도움이 됩니다.
추가로 IRSA를 이미 썼는데도 AccessDenied가 난다면(= 올바른 Role로 Assume이 안 됨) 이 글의 점검 순서가 유용합니다.
원인 4) 네트워크 문제로 ECR 엔드포인트에 못 붙는 경우(인증 만료처럼 보임)
ImagePullBackOff는 단순 인증 실패뿐 아니라 ECR API/Registry에 대한 네트워크 연결 실패에서도 발생합니다. 프라이빗 서브넷에서 NAT/라우팅/NACL/MTU 이슈로 HTTPS만 막히면, 이벤트에는 종종 i/o timeout, TLS handshake timeout 같은 문구가 섞여 나옵니다.
이때는 “DNS는 되는데 HTTPS만 실패” 패턴을 의심해야 합니다.
ECR에 필요한 대표 엔드포인트
리전마다 다르지만 보통 아래가 필요합니다.
api.ecr.<region>.amazonaws.com(ECR API)<account>.dkr.ecr.<region>.amazonaws.com(Docker Registry)- 그리고 이미지 레이어는 S3 백엔드를 타므로 S3 경로도 영향
프라이빗 환경에서 NAT 없이 운영하려면 VPC Endpoint(Interface/Gateway)를 고려합니다.
- Interface Endpoint:
com.amazonaws.<region>.ecr.api,com.amazonaws.<region>.ecr.dkr,com.amazonaws.<region>.sts - Gateway Endpoint:
com.amazonaws.<region>.s3
실전 트러블슈팅 순서(10분 컷)
운영에서 시간을 아끼려면 아래 순서가 효율적입니다.
이벤트에서 에러 문자열 분류
no basic auth credentials→ Secret/인증 주입 문제not authorized→ IAM 권한 문제i/o timeout,TLS handshake→ 네트워크 문제
imagePullSecret 사용 여부 확인
- 사용 중이면 우선 재생성으로 즉시 복구
노드 IAM Role의 ECR 권한 확인
AmazonEC2ContainerRegistryReadOnly또는 최소 권한 정책 부여
프라이빗 서브넷이면 ECR/ST S/S3 엔드포인트 또는 NAT 경로 확인
클러스터 시간 동기화/STS 만료 이슈 확인
권장 아키텍처: “노드 풀 권한 + 최소 Secret”
정리하면, ECR 이미지 pull은 다음 패턴이 가장 안정적입니다.
- 노드 IAM Role에 ECR Pull 권한 부여(표준)
- 애플리케이션 파드는 IRSA로 AWS 리소스 접근 권한만 부여(분리)
- 프라이빗 클러스터는 ECR API/DKR + STS + S3에 대한 VPC Endpoint 구성
- 수동
imagePullSecret은 가급적 제거(필요 시 자동 갱신 체계 필수)
부록: ECR 로그인 Secret을 CronJob으로 자동 갱신(불가피한 경우)
정책상/구조상 imagePullSecret이 꼭 필요하다면, 만료 전에 주기적으로 갱신하는 CronJob을 둘 수 있습니다. 아래는 개념 예시이며, 실제로는 IRSA를 붙여 ecr:GetAuthorizationToken이 가능해야 합니다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: refresh-ecr-pull-secret
namespace: kube-system
spec:
schedule: "0 */6 * * *" # 6시간마다
jobTemplate:
spec:
template:
spec:
serviceAccountName: ecr-secret-refresher
restartPolicy: OnFailure
containers:
- name: refresh
image: public.ecr.aws/aws-cli/aws-cli:2.15.0
env:
- name: AWS_REGION
value: ap-northeast-2
- name: ACCOUNT_ID
value: "123456789012"
- name: NAMESPACE
value: "default"
command:
- /bin/sh
- -lc
- |
set -e
PASS=$(aws ecr get-login-password --region "$AWS_REGION")
kubectl create secret docker-registry ecr-pull \
--docker-server="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com" \
--docker-username=AWS \
--docker-password="$PASS" \
-n "$NAMESPACE" \
--dry-run=client -o yaml | kubectl apply -f -
이 방식은 “증상 재발 방지”에는 도움이 되지만, 운영 복잡도와 보안 표면적이 커지므로 가능하면 노드 IAM 기반으로 단순화하는 편이 낫습니다.
결론
ImagePullBackOff는 결과(파드가 이미지를 못 받음)만 보여주기 때문에 원인이 다양합니다. ECR을 쓴다면 특히 다음 네 가지를 우선순위로 보면 해결이 빨라집니다.
imagePullSecret기반 ECR 토큰이 12시간 만료됐는가?- 노드 IAM Role에 ECR Pull 권한이 있는가?
- STS/IRSA 경로가 깨져 AWS API 호출이 실패하는가?
- 프라이빗 네트워크에서 ECR/STS/S3로 HTTPS가 막힌 건 아닌가?
이 네 가지를 순서대로 좁혀가면, 대부분의 ImagePullBackOff: ECR 인증 만료 케이스는 짧은 시간 안에 복구하고 재발까지 막을 수 있습니다.