- Published on
K8s ImagePullBackOff 401 해결 - ECR·Secret
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
운영 중인 Kubernetes에서 Pod가 뜨지 않고 ImagePullBackOff가 반복되며 이벤트에 401 Unauthorized가 찍히는 경우는 대부분 레지스트리 인증 정보가 없거나, 만료됐거나, 잘못 연결된 상황입니다. 특히 AWS ECR은 인증 토큰이 12시간 만료되므로, 한 번은 되다가도 어느 순간부터 갑자기 401이 발생하기 쉽습니다.
이 글은 ECR을 기준으로, 401이 나는 지점을 이벤트 확인 → Secret 확인 → ServiceAccount 연결 → 노드/IRSA 권한 → 네트워크/엔드포인트 순서로 좁혀가며 해결하는 실전 체크리스트를 제공합니다.
참고로 클러스터 전반의 API 호출 지연이나 타임아웃이 동반된다면 네트워크/컨트롤플레인 이슈일 수 있으니, 원인 분리용으로 Kubernetes apiserver i/o timeout 원인과 해결도 함께 확인해두면 좋습니다.
증상 빠르게 확정하기: 이벤트에서 401을 먼저 본다
먼저 Pod 이벤트를 보면 원인이 거의 드러납니다.
kubectl -n <namespace> describe pod <pod-name>
아래 같은 메시지라면 인증 문제로 확정입니다.
Failed to pull image ...: failed to authorize: rpc error ... 401 Unauthorizedpull access deniedno basic auth credentials
여기서 중요한 포인트는 ImagePullBackOff는 결과(재시도 백오프)이고, 진짜 원인은 이벤트에 있다는 점입니다.
401 유형 분류: Secret 없음 vs 만료 vs 권한 부족
401은 크게 3가지로 나뉩니다.
- Secret이 아예 없음 / imagePullSecrets 미지정
- 이벤트에
no basic auth credentials가 자주 보입니다.
- 이벤트에
- ECR 토큰 만료(12시간)
- 한동안 잘 되다가 특정 시점 이후 동일 노드/네임스페이스에서 동시다발로 터집니다.
- IAM 권한 부족 또는 잘못된 계정/리전 레지스트리
- 토큰은 만들었는데
ecr:BatchGetImage등이 막혀서 실패하거나, 다른 계정 ECR을 바라보는 경우.
- 토큰은 만들었는데
이제부터는 가장 흔한 1, 2를 빠르게 제거하고, 3을 확인하는 흐름으로 진행합니다.
1) 이미지 레퍼런스부터 점검: 리전·계정·레포 이름
의외로 가장 흔한 실수는 이미지 주소 오타입니다.
예시(정상):
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:1.2.3
체크리스트:
- 계정 ID가 맞는가
- 리전이 클러스터/노드가 접근 가능한 리전인가
- 레포지토리 이름이 실제 존재하는가
- 태그가 존재하는가(또는
latest에 의존하고 있지 않은가)
이미지 존재 여부는 로컬/CI에서 다음으로 확인할 수 있습니다.
aws ecr describe-images \
--region ap-northeast-2 \
--repository-name my-app \
--query 'imageDetails[0].imageTags'
2) 가장 단순한 해결: docker-registry Secret 직접 생성
Kubernetes는 레지스트리 인증을 kubernetes.io/dockerconfigjson 타입 Secret으로 받습니다. ECR은 aws ecr get-login-password로 비밀번호를 받아 docker login 형태로 주입합니다.
아래는 네임스페이스에 Secret을 만드는 대표 패턴입니다.
NAMESPACE=default
REGION=ap-northeast-2
ACCOUNT_ID=123456789012
SECRET_NAME=ecr-pull
kubectl -n $NAMESPACE create secret docker-registry $SECRET_NAME \
--docker-server=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com \
--docker-username=AWS \
--docker-password="$(aws ecr get-login-password --region $REGION)"
이제 Deployment(또는 Pod)에 imagePullSecrets를 붙입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
template:
spec:
imagePullSecrets:
- name: ecr-pull
containers:
- name: app
image: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:1.2.3
적용 후 재배포:
kubectl -n default rollout restart deploy/my-app
kubectl -n default get pods -w
Secret이 제대로 들어갔는지 확인
kubectl -n default get secret ecr-pull -o yaml
type이 kubernetes.io/dockerconfigjson인지 확인합니다. 값 자체는 base64라 읽기 어렵지만, 타입과 존재 여부만으로도 1차 진단이 됩니다.
3) 자주 터지는 함정: Secret은 만들었는데 Pod가 못 씀
Secret을 만들었더라도 다음 케이스에서 401이 지속됩니다.
- Secret을
default네임스페이스에 만들고, Pod는prod네임스페이스에 떠 있음 - Deployment에
imagePullSecrets를 안 붙였거나 오타 - ServiceAccount에 붙이려다 누락
네임스페이스를 먼저 확인하세요.
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.serviceAccountName}'
ServiceAccount에 imagePullSecrets를 기본으로 붙이기
매번 Deployment에 넣기 싫다면 ServiceAccount에 기본으로 연결할 수 있습니다.
kubectl -n default patch serviceaccount default \
-p '{"imagePullSecrets": [{"name": "ecr-pull"}]}'
이 방식은 같은 네임스페이스의 많은 워크로드에 일괄 적용할 때 유용합니다.
4) ECR 토큰 만료 문제를 근본적으로 해결하기
앞의 방식은 “당장” 해결은 되지만, ECR 토큰이 12시간 만료라서 언젠가 다시 401이 납니다. 운영 환경에서는 토큰을 자동 갱신하는 구조가 필요합니다.
대표적인 선택지는 2가지입니다.
선택지 A: ECR Credential Helper 또는 노드 IAM으로 끌어오기(환경 의존)
런타임이 Docker이고 노드에 ECR credential helper 설정이 되어 있으면 자동으로 갱신되기도 합니다. 다만 EKS의 기본 런타임(containerd)과 설정 상태에 따라 다르고, 클러스터/노드 AMI 구성에 의존합니다.
운영에서 예측 가능성을 높이려면 다음 선택지 B가 더 흔히 사용됩니다.
선택지 B: ecr-credential-provider 또는 External Secret로 갱신 자동화
- EKS에서는 버전/구성에 따라 kubelet의 ECR credential provider를 사용하는 패턴이 있습니다.
- 또는 CronJob으로 주기적으로 Secret을 갱신하는 방식도 많이 씁니다.
CronJob으로 갱신하는 가장 단순한 형태(개념 예시):
apiVersion: batch/v1
kind: CronJob
metadata:
name: refresh-ecr-secret
namespace: default
spec:
schedule: "0 */6 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
serviceAccountName: refresh-ecr-secret-sa
containers:
- name: refresh
image: public.ecr.aws/aws-cli/aws-cli:2
command:
- /bin/sh
- -c
- |
set -e
REGION=ap-northeast-2
ACCOUNT_ID=123456789012
SECRET_NAME=ecr-pull
PASSWORD=$(aws ecr get-login-password --region $REGION)
kubectl -n default delete secret $SECRET_NAME --ignore-not-found
kubectl -n default create secret docker-registry $SECRET_NAME \
--docker-server=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com \
--docker-username=AWS \
--docker-password="$PASSWORD"
주의점:
- 위 예시는 동작 원리를 보여주는 용도이며, 실제로는
kubectl을 Job 컨테이너에서 쓰기 위한 RBAC(Secret delete/create 권한)와 클러스터 접근 구성이 필요합니다. - Secret을 삭제 후 생성하는 방식은 짧은 순간 레이스가 생길 수 있어, 가능하면
apply또는 서버사이드 패치로 갱신하는 방식이 더 안전합니다.
5) IAM 권한 점검: 노드 역할 또는 IRSA
ECR에서 이미지를 받으려면 최소한 다음 권한이 필요합니다.
ecr:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:GetDownloadUrlForLayerecr:BatchGetImage
어디에 권한이 있어야 하냐는 클러스터 구성에 따라 달라집니다.
- 일반적으로는 노드 인스턴스 프로파일(IAM role) 이 ECR Pull 권한을 가집니다.
- 일부 구성에서는 IRSA를 통해 Pod 단위로 권한을 주려는 시도를 하는데, 이미지 Pull은 kubelet 레벨에서 일어나므로 “Pod IAM만 줬는데 Pull이 안 됨” 같은 혼동이 생깁니다.
노드 역할을 확인(예: EKS 워커 노드):
aws ec2 describe-instances \
--instance-ids <instance-id> \
--query 'Reservations[0].Instances[0].IamInstanceProfile.Arn'
노드 역할에 AmazonEC2ContainerRegistryReadOnly 같은 정책이 붙어있는지 확인합니다.
6) 같은 401처럼 보이지만 다른 문제: 프록시·VPC 엔드포인트·DNS
가끔 이벤트에 401이 찍혀도 실제로는 네트워크 경로가 꼬여 인증 토큰 교환이 정상적으로 되지 않는 경우가 있습니다. 특히 다음 환경에서 자주 발생합니다.
- 사내 프록시를 거치는 노드
- Private subnet에서 NAT 없이 ECR에 접근
- ECR API/DP에 VPC 엔드포인트를 붙였는데 라우팅/DNS가 어긋남
이 경우는 401 외에도 i/o timeout, TLS handshake timeout 같은 로그가 섞일 수 있습니다. 클러스터 전반 증상이라면 Kubernetes apiserver i/o timeout 원인과 해결의 네트워크/타임아웃 점검 방식이 원인 분리에 도움이 됩니다.
7) 재발 방지 체크리스트
운영에서 ImagePullBackOff 401을 “한 번 해결하고 끝”내려면 아래를 표준화하는 게 좋습니다.
- ECR 토큰 만료를 고려해 Secret 자동 갱신 또는 kubelet credential provider 사용
- 네임스페이스별로
imagePullSecrets를 ServiceAccount에 기본 주입 - 이미지 주소(계정/리전/레포/태그) 검증을 CI에서 선제 체크
- 노드 IAM role에 ECR pull 권한이 일관되게 부여되었는지 IaC로 관리
- 장애 시
describe pod이벤트를 수집하는 런북/알람(예:Failed to pull image카운트)
마무리: 가장 빠른 해결 순서 요약
kubectl describe pod에서 401 유형 확인- 이미지 주소(계정/리전/레포/태그) 오타 제거
docker-registrySecret 생성 후imagePullSecrets연결- 토큰 만료 재발 방지(자동 갱신/credential provider)
- 노드 IAM 권한(ECR read) 확인
- Private 네트워크라면 NAT/VPC 엔드포인트/DNS 점검
이 순서대로 보면 대부분의 K8s ImagePullBackOff 401은 30분 안에 원인까지 좁혀지고, 재발 방지까지 설계할 수 있습니다.