ImagePullBackOff는 “이미지 문제”가 아니라 네 가지 중 하나다
ImagePullBackOff는 kubelet이 이미지 pull에 실패해 재시도를 미루는 상태다. 원인은 대개 이름/태그 오타 · 인증 · 레이트리밋 · 네트워크 넷 중 하나인데, 이걸 가르는 문장이 describe의 Events에 그대로 찍혀 있다. 로그가 아니라 Events의 정확한 사유 문자열부터 읽는다.
kubectl describe pod <pod> -n <ns> | sed -n '/Events/,$p'
사유 문자열로 원인을 가른다
- manifest unknown / not found / repository does not exist — 이름·태그 오타이거나 존재하지 않는 태그다. 매니페스트에 태그를 안 적어
:latest로 붙었는데 그 태그가 없는 경우가 흔하다. 로컬에서docker pull <image>로 그대로 재현해 본다. - unauthorized / authentication required / pull access denied — 프라이빗 레지스트리 인증 문제. imagePullSecret이 같은 namespace에 있는지, 타입이
kubernetes.io/dockerconfigjson인지, Pod(또는 ServiceAccount)에 연결됐는지를 본다. - toomanyrequests (Docker Hub rate limit) — 익명 pull 한도. 인증을 붙이거나 사내 미러/캐시로 돌린다.
- dial tcp … i/o timeout / no such host — 노드에서 레지스트리로 못 나감. 프록시·방화벽·사설 DNS 문제이지 이미지 문제가 아니다.
인증이 원인일 때 실제로 확인할 것
kubectl get secret <pull-secret> -n <ns> -o jsonpath='{.type}' # dockerconfigjson 이어야
kubectl get sa default -n <ns> -o jsonpath='{.imagePullSecrets}' # SA 연결 여부
가장 자주 밟는 함정은 시크릿을 default namespace에만 만들고 앱은 다른 namespace에 있는 경우다. 시크릿은 namespace를 넘지 못한다. Pod에 직접 imagePullSecrets를 걸었는지, 아니면 SA에 걸었는지도 갈라 본다.
노드 레벨로 좁히기
kubelet이 아니라 노드 컨테이너 런타임에서 재현하면 인증/네트워크를 분리할 수 있다.
crictl pull <image> # 노드에서 직접 — 성공하면 자격증명/네임스페이스 쪽 문제
복구 순서
- 이름·태그를 확정한다(가능하면 태그 대신 digest로 고정해 재현성을 높인다).
- 인증이면 시크릿의 namespace·타입·연결(Pod/SA)을 맞춘다.
- 네트워크면 노드→레지스트리 경로(프록시·방화벽·DNS)를 본다.
이미지 이름이 맞는데도 안 되면 십중팔구 자격증명이 엉뚱한 namespace에 있다는 점만 기억해도 대부분 몇 분 안에 끝난다.
빠른 진단 체크리스트
- 로그가 아니라 describe Events의 사유 문자열부터 읽는다
- manifest unknown·not found면 이미지 이름·태그 오타다
- unauthorized면 imagePullSecret의 namespace·타입·연결을 본다
- 시크릿이 앱과 같은 namespace에 있는지 먼저 확인한다
- toomanyrequests면 레지스트리 레이트리밋, 인증·미러로 푼다
- i/o timeout·no such host면 노드→레지스트리 네트워크·프록시다
crictl pull로 노드 레벨에서 재현해 원인을 분리한다- 재현성을 위해 태그 대신 digest로 고정한다