/인프라/ImagePullBackOff/ErrImagePull 원인 6가지 진단·해결 가이드
인프라ImagePullBackOffErrImagePull

ImagePullBackOff/ErrImagePull 원인 6가지 진단·해결 가이드

Pod가 ImagePullBackOff/ErrImagePull에서 멈췄나요? kubectl describe로 Events를 읽어 이미지명 오타, imagePullSecrets 누락, 도커허브 rate limit 등 6가지 원인을 명령어·YAML로 해결합니다.

ImagePullBackOff/ErrImagePull 원인 6가지 진단·해결 가이드

kubectl ImagePullBackOff/ErrImagePull 원인 6가지 완벽 해결 가이드

"Pending은 넘겼는데 이번엔 ImagePullBackOff?"

1편(Pod Pending 진단)에서 우리는 스케줄러가 노드를 못 찾아 Pod가 Pending에 머무는 문제를 해결했습니다. 이제 Pod는 노드에 배정됐는데, 막상 컨테이너를 띄우려고 보니 이미지를 가져오는 단계에서 막힙니다. 바로 ErrImagePullImagePullBackOff입니다.

두 상태의 차이부터 정리하고 갑시다.

  • ErrImagePull: kubelet이 이미지 Pull을 시도했다가 방금 막 실패한 순간의 상태입니다.
  • ImagePullBackOff: Pull이 반복 실패하자 kubelet이 재시도 간격을 점점 늘려가며(back-off) 대기 중인 상태입니다. 즉 ErrImagePull이 누적된 결과입니다.

둘 다 근본 원인은 동일합니다. "이미지를 못 가져온다." 원인을 1분 안에 좁히는 진단부터 시작하겠습니다.

증상과 즉시 진단: 에러 메시지를 읽어라

먼저 상태를 확인합니다.

Bash
kubectl get pods
# NAME                     READY   STATUS             RESTARTS   AGE
# web-7d9f8c6b5-abcde      0/1     ImagePullBackOff   0          2m

핵심은 Events입니다. describe로 마지막 메시지를 봅니다.

Bash
kubectl describe pod web-7d9f8c6b5-abcde
CODE
Events:
  Type     Reason     Age   From     Message
  ----     ------     ----  ----     -------
  Normal   Pulling    2m    kubelet  Pulling image "myreg.io/app:v1.2"
  Warning  Failed     2m    kubelet  Failed to pull image "myreg.io/app:v1.2":
                                     rpc error: code = ... pull access denied
  Warning  Failed     2m    kubelet  Error: ErrImagePull
  Normal   BackOff    1m    kubelet  Back-off pulling image "myreg.io/app:v1.2"

시간순으로 전체 흐름을 보려면:

Bash
kubectl get events --sort-by=.lastTimestamp

에러 메시지별 원인 매핑 표

Failed 메시지의 문구만 봐도 원인의 70%는 결정됩니다.

Events 메시지 시그니처원인해당 섹션
... not found / manifest unknown이미지명·태그 오타, 없는 태그원인 1
pull access denied / unauthorized프라이빗 레지스트리 인증 누락원인 2
toomanyrequests: ... rate limit (429)Docker Hub rate limit원인 3
x509: certificate signed by unknown authority사설 레지스트리 TLS 신뢰 문제원인 4
no space left on device / ImageGCFailed노드 디스크풀원인 5
dial tcp ... timeout / connection refused레지스트리 다운·방화벽·DNS원인 6

원인 6가지 체크리스트와 해결법

원인 1. 이미지명·태그 오타 또는 없는 태그

시그니처: Failed to pull image ...: not found, manifest unknown

가장 흔하고 가장 허무한 원인입니다. 매니페스트의 image: 값을 직접 검증합니다.

Bash
# 태그가 실제 존재하는지 확인 (docker가 있는 환경에서)
docker manifest inspect myreg.io/app:v1.2

오타(nginx:latset)거나 빌드 파이프라인이 아직 푸시하지 않은 태그인 경우가 대부분입니다. 매니페스트를 수정해 재배포하세요.

원인 2. 프라이빗 레지스트리 인증 누락

시그니처: pull access denied, unauthorized: authentication required

imagePullSecrets가 없거나 잘못된 경우입니다. 해결법은 4번 섹션에서 전체 흐름으로 다룹니다.

원인 3. Docker Hub rate limit (429)

시그니처: toomanyrequests: You have reached your pull rate limit

Docker Hub 무료 티어는 익명 IP 기준 한도가 빡빡합니다. 노드 IP를 공유하는 클러스터에서 특히 자주 터집니다.

구분Pull 한도(6시간)
익명(미인증)100
인증 무료 계정200

해결은 인증 추가 또는 미러 레지스트리 전환입니다. 역시 4번 섹션 참고.

원인 4. 사설 레지스트리 TLS / 네트워크·DNS

시그니처: x509: certificate signed by unknown authority

자체 서명(self-signed) 인증서를 쓰는 Harbor·Nexus 등에서 발생합니다. 노드의 컨테이너 런타임이 해당 CA를 신뢰하지 않는 것이죠.

Bash
# 노드에서 직접 확인 (containerd 기준)
crictl pull myreg.io/app:v1.2
# x509: certificate signed by unknown authority

CA 인증서를 런타임이 신뢰하도록 등록해야 합니다(아래 심화 참고).

원인 5. 노드 디스크풀 (ImageFS eviction)

시그니처: no space left on device, 노드 컨디션 DiskPressure=True

Bash
kubectl describe node <node>
# Conditions:
#   DiskPressure   True   ... ImageGCFailed

노드에 SSH로 들어가 실제 사용량과 이미지를 점검합니다.

Bash
df -h /var/lib/containerd
crictl images          # 쌓인 이미지 확인
crictl rmi --prune     # 미사용 이미지 정리

원인 6. 레지스트리 다운 / 방화벽 / DNS

시그니처: dial tcp ... i/o timeout, connection refused

노드에서 레지스트리까지의 네트워크 경로를 확인합니다.

Bash
nslookup myreg.io          # DNS 해석 확인
curl -v https://myreg.io/v2/   # 443 도달 여부

방화벽·프록시·사내망 정책이 변경됐는지 인프라팀과 확인하세요.

핵심 해결 예시 심화

imagePullSecrets 생성과 연결 (원인 2 해결)

먼저 docker-registry 타입 시크릿을 만듭니다.

Bash
kubectl create secret docker-registry regcred \
  --docker-server=myreg.io \
  --docker-username=<id> \
  --docker-password=<pw> \
  --docker-email=<email>

연결 방식은 두 가지입니다.

방식 A — Pod(또는 Deployment) spec에 직접 연결

YAML
spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - name: app
      image: myreg.io/app:v1.2

방식 B — ServiceAccount에 기본 연결 (네임스페이스 전체 적용)

Bash
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets":[{"name":"regcred"}]}'
비교방식 A (Pod)방식 B (ServiceAccount)
적용 범위해당 Pod만SA를 쓰는 모든 Pod
관리 편의매니페스트마다 반복한 번 설정으로 끝
추천 상황특정 워크로드만네임스페이스 표준 레지스트리

실무 팁: 운영 클러스터에서는 방식 B를 기본으로 깔아두는 편이 사고를 줄입니다. 신규 배포할 때마다 imagePullSecrets를 빼먹어 ImagePullBackOff를 내는 일이 의외로 잦거든요. 단, SA가 분리된 멀티테넌트 환경에서는 각 SA에 적용해야 한다는 점만 주의하세요.

Docker Hub rate limit 우회 (원인 3 해결)

가장 간단한 해결은 Docker Hub 계정으로 인증 시크릿을 만들어 위와 동일하게 연결하는 것입니다.

Bash
kubectl create secret docker-registry dockerhub \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=<dockerhub_id> \
  --docker-password=<token>

근본적으로는 **pull-through cache(미러 레지스트리)**를 권장합니다. containerd라면 레지스트리 미러를 설정해 Docker Hub 요청을 사내 Harbor/ECR로 우회시킵니다.

TOML
# /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://registry-1.docker.io"

[host."https://mirror.myreg.io"]
  capabilities = ["pull", "resolve"]

사설 레지스트리 CA 신뢰 (원인 4 해결)

containerd에 CA를 등록합니다.

Bash
# 각 노드에서
mkdir -p /etc/containerd/certs.d/myreg.io
cp ca.crt /etc/containerd/certs.d/myreg.io/ca.crt
systemctl restart containerd

결론: 1페이지 요약과 재발 방지

진단 흐름을 한 줄로 정리하면 이렇습니다.

  1. kubectl get pods로 상태 확인
  2. kubectl describe podEvents 메시지 읽기
  3. 위 매핑 표로 6가지 원인 중 하나로 분기
  4. 해당 명령/YAML 적용 후 재배포

재발 방지 코드

latest 금지, digest로 고정하면 "어제는 됐는데 오늘 안 돼요"를 막을 수 있습니다.

YAML
containers:
  - name: app
    # 태그 대신 digest로 불변 고정
    image: myreg.io/app@sha256:9f86d081884c7d659a2feaa0c55ad015...
    imagePullPolicy: IfNotPresent

imagePullPolicy 차이도 알아둡시다.

동작추천
Always매번 레지스트리에서 Pull (rate limit 위험↑):latest 사용 시
IfNotPresent노드에 있으면 재사용digest/고정 태그 운영 환경

여기에 레지스트리 미러/pull-through cache까지 깔아두면 Docker Hub 장애나 rate limit에도 클러스터가 흔들리지 않습니다.

참고: 공식 문서

이 글에서 다루는 동작·설정·에러의 1차 출처는 다음 공식 문서입니다. 버전별 옵션과 정확한 동작은 여기서 확인하세요.

자주 묻는 질문 (FAQ)

Q. ErrImagePull과 ImagePullBackOff는 뭐가 다른가요? A. ErrImagePull은 Pull이 방금 실패한 순간의 상태고, ImagePullBackOff는 실패가 반복돼 kubelet이 재시도 간격을 늘려가며 대기 중인 상태입니다. 원인은 동일하니 kubectl describe pod의 Events 메시지로 진단하면 됩니다.

Q. imagePullSecrets를 설정했는데도 pull access denied가 떠요. A. 시크릿이 Pod와 같은 네임스페이스에 있는지, --docker-server 주소가 이미지 경로의 레지스트리와 정확히 일치하는지 확인하세요. Docker Hub는 https://index.docker.io/v1/를 써야 합니다. SA에 연결한 경우 Pod가 해당 SA를 쓰는지도 점검하세요.

Q. 노드에 SSH가 안 되는 환경에서 디스크풀을 어떻게 진단하나요? A. kubectl describe node <node>에서 DiskPressure 컨디션과 ImageGCFailed 이벤트를 먼저 확인하세요. 디버그가 더 필요하면 kubectl debug node/<node>로 노드 디버그 컨테이너를 띄워 df -hcrictl images를 실행할 수 있습니다.


다음 편 예고 — 3편: CrashLoopBackOff, 컨테이너가 계속 재시작될 때 로그로 원인 추적하기. 이미지는 잘 받았는데 컨테이너가 떴다 죽기를 반복한다면 다음 글에서 만나요.

✦ ✦ ✦
편집 검토 · Editorial Review

AI 도구는 자료 조사와 초안 작성의 보조 수단으로 사용될 수 있습니다. Nodelog는 공개 전 내용과 출처를 검토하고, 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서를 함께 확인하도록 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

편집 책임 · Nodelog 기술 편집팀·발행 ·
관련 공식 문서Kubernetes 공식 문서

댓글

첫 번째 댓글을 남겨보세요.