- Published on
K8s ImagePullBackOff - registry auth·CA 10분 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 멀쩡해 보이는데 Pod만 ImagePullBackOff로 멈추면, 대부분 원인은 두 갈래로 수렴합니다.
- Registry 인증(auth) 문제:
imagePullSecrets, 토큰 만료, 잘못된 Secret 타입/네임스페이스, IRSA/ECR 권한 등 - TLS/CA 문제: 사설 레지스트리 인증서 체인 누락, 프록시/미들박스, 노드의 trust store 불일치 등
이 글은 kubectl 몇 줄과 노드/런타임 관점의 확인으로 10분 내에 auth vs CA를 분리하고, 가장 흔한 케이스를 빠르게 복구하는 절차를 제공합니다.
> 참고: x509 계열로 확정되면 아래 글도 함께 보면 원인 좁히기가 빨라집니다. EKS Pod에서 x509 unknown authority 오류 해결
0. 증상 정리: ImagePullBackOff는 “결과”일 뿐
ImagePullBackOff는 kubelet이 이미지를 당기려다 실패했고, 재시도 백오프(backoff)에 들어갔다는 상태입니다. 실제 원인은 이벤트에 적나라하게 찍힙니다.
가장 먼저 할 일은 Pod 이벤트에서 실패 메시지 원문을 확보하는 겁니다.
# 1) 이벤트 포함 상세 출력
kubectl -n <ns> describe pod <pod>
# 2) 이벤트만 빠르게 필터링
kubectl -n <ns> get events --sort-by=.lastTimestamp | tail -n 30
여기서 흔히 보는 메시지 패턴:
- 인증 계열
Failed to pull image ... unauthorized: authentication requireddenied: requested access to the resource is deniedpull access denied
- CA/TLS 계열
x509: certificate signed by unknown authoritytls: failed to verify certificatecertificate is valid for ..., not <registry-host>(SAN 불일치)
- 네트워크/이름해석 계열(이번 글의 범위 밖이지만 자주 섞임)
dial tcp ... i/o timeoutno such host
이제부터는 auth와 CA를 각각 5분 내로 확인하는 루틴으로 나눕니다.
1. 1분 컷: “어떤 노드에서” 실패하는지부터 확인
동일 이미지/동일 Secret인데도 어떤 노드에선 되고 어떤 노드에선 안 되면, 대개 노드 런타임 설정(프록시/CA/credential helper) 문제입니다.
kubectl -n <ns> get pod <pod> -o wide
# NODE 컬럼 확인
노드가 특정 AZ/노드그룹에 편중되어 있으면, 해당 노드그룹 AMI/부트스트랩 설정 차이를 의심하세요.
2. Auth 5분 진단: imagePullSecrets와 레지스트리 권한
2.1 ServiceAccount에 imagePullSecrets가 붙어 있는지
가장 흔한 실수는 Secret은 만들었는데 SA에 연결을 안 한 경우입니다.
# Pod가 사용하는 ServiceAccount 확인
kubectl -n <ns> get pod <pod> -o jsonpath='{.spec.serviceAccountName}{"\n"}'
# 해당 SA에 imagePullSecrets 설정 확인
kubectl -n <ns> get sa <sa-name> -o yaml
SA에 아래가 없다면, Pod spec에 직접 붙였거나(혹은 아예 누락)입니다.
imagePullSecrets:
- name: regcred
> 팁: 운영에서는 Pod마다 붙이기보다 SA에 기본으로 붙이는 편이 실수 확률이 낮습니다.
2.2 Secret 타입/내용이 올바른지 (dockerconfigjson)
K8s가 기대하는 타입은 보통 이겁니다.
kubernetes.io/dockerconfigjson
kubectl -n <ns> get secret regcred -o jsonpath='{.type}{"\n"}'
kubectl -n <ns> get secret regcred -o yaml
올바른 Secret 생성 예시:
kubectl -n <ns> create secret docker-registry regcred \
--docker-server=<registry.example.com> \
--docker-username=<user> \
--docker-password='<password-or-token>' \
--docker-email=<email>
그리고 Pod/Deployment에 연결:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
serviceAccountName: app-sa
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/team/app:1.2.3
2.3 네임스페이스 함정: Secret은 “같은 ns”에 있어야 함
imagePullSecrets는 Pod과 같은 네임스페이스에서만 참조됩니다. kube-system에 만들어두고 default에서 당기려 하면 실패합니다.
kubectl get secret regcred -A | grep regcred
2.4 ECR 같은 클라우드 레지스트리: “노드/파드 권한” 문제 분리
ECR(또는 GCR/ACR)에서 unauthorized가 나면 두 가지가 섞입니다.
- 토큰을 Secret으로 박아둔 경우: 만료되면 바로
ImagePullBackOff - 노드 IAM(Role) 기반으로 당겨야 하는데 권한이 없음:
ecr:GetAuthorizationToken,ecr:BatchGetImage등
EKS에서 특히 흔한 패턴:
- Managed NodeGroup IAM Role에 ECR read 권한이 빠짐
- Private ECR + VPC 엔드포인트/라우팅 문제(이건 네트워크 축)
권한 이슈는 이벤트에 denied/unauthorized로 나오지만, 정확한 정책 누락은 클라우드 로그/정책 확인이 필요합니다.
3. CA/TLS 5분 진단: x509, SAN, 체인, 런타임 trust
이 구간은 에러 메시지가 꽤 직설적입니다.
x509: certificate signed by unknown authority→ CA 체인 신뢰 실패certificate is valid for A, not B→ 인증서 SAN/CN에 레지스트리 호스트명이 없음remote error: tls: handshake failure→ TLS 버전/암호군/중간장비 가능성
3.1 Pod 이벤트에서 x509 문구 확인
kubectl -n <ns> describe pod <pod> | sed -n '/Events:/,$p'
여기서 x509가 보이면, auth보다 먼저 CA/trust를 의심하는 게 시간 절약입니다.
3.2 노드에서 레지스트리 TLS를 직접 확인 (openssl)
가능하면 실패가 발생한 해당 노드에 접속해서 확인합니다.
# 레지스트리 인증서 체인/호스트 매칭 확인
openssl s_client -connect <registry.example.com>:443 -servername <registry.example.com> -showcerts </dev/null
확인 포인트:
Verify return code: 0 (ok)인가?- 체인이 중간 인증서까지 포함되어 전달되는가?
- 인증서 Subject Alternative Name에
<registry.example.com>이 있는가?
3.3 containerd/Docker가 신뢰하는 CA는 “노드 OS/런타임 설정”에 달림
Kubelet은 이미지를 직접 당기지 않고, 노드의 컨테이너 런타임(containerd/Docker/CRI-O)에 위임합니다. 즉 노드가 신뢰하지 않는 CA면 무조건 실패합니다.
- 사내 프라이빗 레지스트리(자체 CA)
- TLS를 프록시가 가로채는 환경(기업망 MITM)
이 경우 해결은 보통 둘 중 하나입니다.
- 노드 OS trust store에 CA 추가
- containerd에 레지스트리별 CA 설정
containerd 기준(경로는 배포판/버전에 따라 다름) 예시:
# 예: /etc/containerd/certs.d/<registry-host>/hosts.toml
sudo mkdir -p /etc/containerd/certs.d/registry.example.com
cat <<'EOF' | sudo tee /etc/containerd/certs.d/registry.example.com/hosts.toml
server = "https://registry.example.com"
[host."https://registry.example.com"]
capabilities = ["pull", "resolve"]
ca = "/etc/ssl/certs/company-root-ca.crt"
EOF
sudo systemctl restart containerd
> 운영 팁: 노드그룹 AMI/부트스트랩(UserData)에 반영해 노드 교체 시에도 자동 적용되게 만드세요.
3.4 “임시 회피”로 insecure registry를 쓰면 안 되는 이유
긴급 장애에서 insecure_skip_verify 같은 설정을 떠올리기 쉽지만, 이는 공급망 공격에 취약해집니다. 최소한 사내 CA를 배포하는 방식으로 해결하는 게 정석입니다.
4. 재현/분리 테스트: 같은 노드에서 curl vs crictl
원인이 애매할 때는 같은 노드에서 네트워크/TLS와 런타임을 분리해 봅니다.
4.1 curl로 TLS만 확인
curl -Iv https://registry.example.com/v2/
- 여기서도 x509가 나면: 노드 OS trust 문제
- curl은 OK인데 pull만 실패하면: containerd trust 설정/인증 문제 가능
4.2 crictl로 실제 pull 확인
# 노드에서
sudo crictl pull registry.example.com/team/app:1.2.3
crictl에서 동일 에러가 나면 Kubernetes 오브젝트 문제가 아니라 노드 런타임/인증서/자격증명 문제로 확정할 수 있습니다.
5. 가장 흔한 원인 TOP 7과 빠른 처방
imagePullSecrets 누락
- 처방: SA 또는 Pod spec에
imagePullSecrets추가
- 처방: SA 또는 Pod spec에
Secret이 다른 네임스페이스에 존재
- 처방: 동일 ns에 Secret 재생성
dockerconfigjson 타입/키가 잘못됨
- 처방:
kubectl create secret docker-registry로 재생성
- 처방:
레지스트리 주소가 다름(호스트/포트/프로토콜)
- 처방: Secret의
--docker-server와 image reference의 host를 정확히 일치
- 처방: Secret의
ECR 토큰을 Secret으로 고정해 만료
- 처방: 노드 IAM 기반 또는 External Secrets/컨트롤러로 자동 갱신
사설 CA 미배포로 x509 unknown authority
- 처방: 노드 trust store/containerd certs.d에 CA 추가 (권장)
인증서 SAN 불일치(예: registry는 cert에 registry.internal만 있음)
- 처방: 올바른 FQDN이 SAN에 포함된 인증서 재발급 또는 접근 도메인 정정
6. 운영 체크리스트: “10분 안에 끝내는” 순서 고정
아래 순서를 그대로 따르면, 대부분 10분 내에 결론이 납니다.
kubectl describe pod로 에러 원문 확보 (1분)get pod -o wide로 특정 노드 편향 여부 확인 (1분)- 인증 에러면
- SA/Pod의
imagePullSecrets확인 - Secret 타입/네임스페이스 확인 (3분)
- SA/Pod의
- x509/TLS 에러면
- 노드에서
openssl s_client,curl -Iv로 체인/SAN 확인 - containerd CA 설정/노드 trust store 반영 (5분)
- 노드에서
EKS 환경에서 노드 상태/리소스 문제로 파드가 뜨지 못하는 케이스도 자주 섞이니, 이미지 풀 이전 단계에서 막힌 상황이라면 아래 글도 함께 점검하세요.
7. 마무리: 결론은 “이벤트 문자열”과 “노드 신뢰체인”
ImagePullBackOff는 Kubernetes의 고장이라기보다, **레지스트리 접근 경로(인증/네트워크/TLS)**가 어딘가에서 끊겼다는 신호입니다. 진단의 핵심은 두 가지입니다.
- Pod 이벤트의 실패 메시지에서 auth vs x509를 즉시 분기
- 같은 노드에서
curl/openssl과crictl pull로 OS trust vs 런타임 pull을 분리
이 루틴을 팀의 런북으로 고정해두면, 새벽 장애에서도 “레지스트리 인증서냐, 시크릿이냐”로 헤매는 시간을 크게 줄일 수 있습니다.