ImagePullBackOff의 근본 원인은 “한 번은 됐는데 계속 실패”에서 갈린다
사유 문자열로 1차 분기를 했다면(manifest unknown / unauthorized / timeout), 그다음은 “왜 이게 지금 발생했나”를 본다. 특히 전엔 되던 pull이 어느 순간부터 실패한다면 원인이 좁혀진다.
1) 레지스트리 토큰 만료 (클라우드 레지스트리의 단골)
ECR·GCR·ACR은 pull 토큰의 수명이 짧다. 예컨대 ECR 토큰은 12시간이라, 정적 dockerconfigjson 시크릿에 토큰을 박아 두면 반나절 뒤 unauthorized로 바뀐다. 해법은 시크릿을 갱신하는 게 아니라 IRSA/워크로드 아이덴티티나 크리덴셜 헬퍼로 pull 때마다 토큰을 자동 발급하는 것이다.
2) dockerconfigjson 형식·URL 불일치
인증 실패의 절반은 시크릿 형식이다 — base64 이중 인코딩, auth 필드 누락, 그리고 레지스트리 host 표기 불일치(포트 포함/미포함, index.docker.io vs docker.io). 시크릿의 host가 이미지의 host와 글자까지 같아야 매칭된다.
3) 아키텍처 불일치 — “no matching manifest”
arm64 노드(예: Graviton, Apple 실리콘 빌드)에 amd64 전용 이미지를 배치하면 매니페스트에 해당 플랫폼이 없어 pull이 실패한다. 멀티아치(docker buildx --platform)로 밀거나 노드 아키텍처를 맞춘다.
4) 태그 재사용 + 캐시
가변 태그(:latest, :prod)를 재사용하면 노드가 옛 이미지를 캐시해 새 내용이 반영되지 않거나, 레지스트리에서 지워진 태그를 참조해 실패한다. 태그는 커밋 SHA나 digest로 고정한다.
확인·복구
kubectl describe pod <pod> | grep -A3 Events # 사유가 언제부터 바뀌었나
crictl pull <image> # 노드에서 직접 재현(인증/아키텍처 분리)
“전엔 됐는데”면 토큰 만료·태그 삭제를, “처음부터 안 됨”이면 형식·아키텍처를 먼저 본다.
빠른 진단 체크리스트
- "전엔 됐는데 지금 실패"면 토큰 만료·태그 삭제를 먼저 의심한다
- ECR·GCR·ACR은 pull 토큰이 짧게 만료된다(정적 시크릿 주의)
- 정적 시크릿 대신 IRSA·워크로드 아이덴티티로 자동 발급한다
- dockerconfigjson의 base64 이중 인코딩·auth 필드 누락을 확인한다
- 시크릿의 레지스트리 host 표기가 이미지 host와 글자까지 같아야 한다
- arm64 노드에 amd64 전용 이미지면 no matching manifest가 난다
- 가변 태그 재사용 + 캐시로 옛 이미지가 뜨는지 확인한다
crictl pull로 인증·아키텍처를 노드에서 분리한다