Published on

AKS ImagePullBackOff - ACR 인증 문제 해결

Authors

서버는 멀쩡한데 배포만 하면 Pod가 ImagePullBackOff에 걸리는 상황은 운영에서 자주 만납니다. 특히 Azure Kubernetes Service(AKS)에서 Azure Container Registry(ACR)를 쓰는 구성이라면, 원인의 상당수가 “이미지 자체”가 아니라 “인증(Authorization) 경로”에 있습니다.

이 글은 AKS에서 ImagePullBackOff가 떴을 때 ACR 인증 문제를 증상으로 분류하고, kubectlaz원인을 확정한 뒤, 가장 안전한 순서로 해결책을 적용하는 흐름으로 정리합니다.

참고로 ImagePullBackOff는 컨테이너가 뜨기 전 단계에서 막히므로 CrashLoopBackOff와 진단 포인트가 다릅니다. 런타임 크래시 진단은 별도 글인 Kubernetes CrashLoopBackOff 10가지 원인과 15분 진단도 함께 보면 좋습니다.

1) 먼저 “진짜 원인”을 이벤트에서 확정하기

ImagePullBackOff는 결과일 뿐이고, 실제 원인은 Pod 이벤트에 들어 있습니다. 가장 먼저 아래 두 가지를 확인하세요.

1-1. Pod 이벤트 확인

kubectl -n <namespace> describe pod <pod-name>

MDX 빌드 에러 방지를 위해 &lt;namespace&gt; 같은 형태를 쓰지 않고, 위처럼 인라인 코드로 감쌌습니다.

Events: 섹션에서 다음 메시지 패턴을 찾습니다.

  • Failed to pull image / Back-off pulling image
  • unauthorized: authentication required
  • denied: requested access to the resource is denied
  • pull access denied / manifest unknown
  • x509: certificate signed by unknown authority (프록시/사설 CA)
  • dial tcp ... i/o timeout (네트워크/방화벽/DNS)

여기서 **unauthorized / denied**가 보이면 거의 확실하게 ACR 권한 혹은 자격 증명 문제입니다.

1-2. 어떤 노드에서 실패하는지 확인

노드별로만 실패하면(특정 노드풀에서만) 네트워크 또는 노드 정체성(Managed Identity)이 꼬였을 가능성이 큽니다.

kubectl -n <namespace> get pod <pod-name> -o wide

NODE 컬럼을 확인하고, 같은 노드풀에서만 반복되는지 체크합니다.

2) AKS에서 ACR Pull 인증이 동작하는 3가지 방식

AKS가 ACR에서 이미지를 당겨오는 대표적인 방식은 아래 3가지입니다.

  1. AKS와 ACR을 --attach-acr로 연결 (권장, 가장 간단)
  2. AKS 클러스터 또는 kubelet Managed Identity에 AcrPull 역할 부여 (정석, 운영 친화적)
  3. imagePullSecrets로 Docker registry secret 사용 (레거시/특수 상황에서 유용)

운영 관점에서는 1과 2가 가장 안전합니다. 3은 시크릿 로테이션/노출 관리 부담이 있습니다.

3) 가장 흔한 원인: ACR 권한(AcrPull)이 없다

3-1. AKS가 사용하는 정체성 확인(Managed Identity)

AKS는 이미지 pull 시점에 보통 kubelet identity를 사용합니다. 먼저 클러스터의 kubelet identity를 확인합니다.

az aks show \
  --resource-group <rg> \
  --name <aks-name> \
  --query identityProfile.kubeletidentity \
  -o json

출력에서 clientIdobjectId를 확인합니다.

  • objectId: Azure RBAC 역할 할당에 필요
  • clientId: 식별용

3-2. ACR 리소스 ID 확인

az acr show \
  --resource-group <acr-rg> \
  --name <acr-name> \
  --query id -o tsv

3-3. AcrPull 역할 할당 여부 확인

az role assignment list \
  --assignee <kubelet-object-id> \
  --scope <acr-resource-id> \
  -o table

여기서 AcrPull이 없다면 할당합니다.

az role assignment create \
  --assignee-object-id <kubelet-object-id> \
  --assignee-principal-type ServicePrincipal \
  --role AcrPull \
  --scope <acr-resource-id>

주의할 점:

  • --assignee-object-idclientId를 넣으면 실패하거나 엉뚱한 대상에 붙을 수 있습니다. objectId를 쓰는 습관이 안전합니다.
  • 역할 부여 후 전파에 수십 초에서 수 분 걸릴 수 있습니다.

4) 더 쉬운 해결: az aks update --attach-acr

클러스터와 ACR을 연결하는 가장 간단한 방법입니다. 내부적으로 적절한 권한을 설정해 줍니다.

az aks update \
  --resource-group <rg> \
  --name <aks-name> \
  --attach-acr <acr-name>

이미 붙어 있는데도 ImagePullBackOff가 계속된다면 아래를 의심하세요.

  • ACR이 다른 구독/테넌트에 있음
  • 클러스터가 업데이트되며 identity가 바뀌었는데 역할이 예전 identity에 붙어 있음
  • 프라이빗 네트워크(Private Link) 구성에서 DNS가 잘못됨

5) imagePullSecrets로 해결(특수 상황)

Managed Identity를 쓸 수 없거나(예: 외부 클러스터, 특수 네트워크), 임시로 빠르게 우회해야 한다면 imagePullSecrets를 씁니다.

5-1. ACR Admin 사용자 사용은 가급적 피하기

ACR에는 Admin user(기본 비활성)를 켜서 아이디/비번으로 로그인할 수 있지만 운영 보안상 권장되지 않습니다. 가능하면 토큰 기반 또는 서비스 프린시플을 고려하세요.

그래도 단기 우회가 필요하다면 다음처럼 시크릿을 만들 수 있습니다.

kubectl -n <namespace> create secret docker-registry acr-pull \
  --docker-server=<acr-name>.azurecr.io \
  --docker-username=<username> \
  --docker-password=<password>

5-2. Deployment에 imagePullSecrets 연결

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      imagePullSecrets:
        - name: acr-pull
      containers:
        - name: app
          image: <acr-name>.azurecr.io/app:1.0.0

시크릿을 네임스페이스에 만들었는데도 실패한다면 다음을 확인합니다.

  • Deployment가 참조하는 네임스페이스가 맞는지
  • docker-server가 정확히 <acr-name>.azurecr.io인지
  • 태그가 존재하는지(실수로 latest만 가정)

6) “권한은 있는데도” unauthorized가 뜰 때 체크리스트

권한을 줬는데도 unauthorized가 지속되면, 대개 아래 중 하나입니다.

6-1. 이미지 레퍼런스 오타 또는 레지스트리 호스트 불일치

ACR은 보통 <acr-name>.azurecr.io를 씁니다. 간혹 잘못해서 다음처럼 넣습니다.

  • https://를 붙임
  • 레지스트리 이름 철자 오류
  • 다른 ACR로 배포했는데 매니페스트는 다른 곳을 바라봄

Pod 스펙의 image: 값을 다시 확인하세요.

kubectl -n <namespace> get deploy <deploy-name> -o jsonpath='{.spec.template.spec.containers[0].image}'

6-2. AKS가 실제로 쓰는 identity가 바뀌었다

클러스터 업데이트, 노드풀 재생성, 클러스터 재구성 과정에서 kubelet identity가 바뀌면 예전 objectId에 붙어 있던 AcrPull은 무용지물이 됩니다.

  • az aks show로 현재 kubelet objectId 재확인
  • ACR scope에 역할이 “현재 objectId”에 붙어 있는지 재확인

6-3. ACR이 방화벽/네트워크 제한 중

ACR에 방화벽이 걸려 있거나 Private Endpoint만 허용하면, 인증 이전에 네트워크에서 막힐 수 있습니다. 이 경우 이벤트에는 i/o timeout이나 no such host가 더 흔하지만, 환경에 따라 인증 에러처럼 보일 때도 있습니다.

확인 포인트:

  • ACR Public network access 설정
  • Private Endpoint 사용 시 AKS가 해당 VNet DNS를 올바르게 사용 중인지
  • 노드 서브넷에서 ACR로 나가는 경로(UDR, 방화벽) 존재 여부

7) Private Link(사설 ACR)에서 자주 터지는 DNS 문제

ACR을 Private Endpoint로 붙이면, 노드가 *.azurecr.io를 공인 IP로 해석하지 않고 사설 IP로 해석해야 합니다. 이때 DNS 구성이 어긋나면 pull이 실패합니다.

증상 예:

  • no such host
  • dial tcp: lookup <acr-name>.azurecr.io 실패

해결 방향:

  • Private DNS Zone privatelink.azurecr.io가 VNet에 링크되어 있는지
  • 커스텀 DNS를 쓰면 해당 존 포워딩이 있는지
  • 노드에서 실제 해석 결과를 확인(디버그 Pod 사용)

디버그 Pod 예시:

kubectl -n <namespace> run netdebug --rm -it \
  --image=busybox:1.36 \
  --restart=Never -- sh

쉘 안에서 DNS를 확인합니다.

nslookup <acr-name>.azurecr.io

8) 실전 트러블슈팅 플로우(15분 컷)

운영에서 시간을 아끼려면 순서를 고정하는 게 좋습니다.

  1. kubectl describe pod로 이벤트에서 unauthorized인지 timeout인지 먼저 분기
  2. unauthorized
    • az aks show로 kubelet identity objectId 확인
    • ACR scope에 AcrPull 역할 있는지 확인
    • 없으면 역할 부여 또는 --attach-acr
  3. timeout 또는 no such host
    • ACR 네트워크(방화벽/Private Link/DNS) 점검
    • 노드풀 단위로 재현되는지 확인
  4. 임시 우회가 필요하면 imagePullSecrets 적용

이 플로우는 “원인 확정”을 먼저 하기 때문에 불필요한 재배포/재시작을 줄여줍니다. 대규모 시스템에서 원인 분기 없이 재시도만 반복하면, 다른 장애(예: API rate limit, 이미지 레지스트리 스로틀링)로 번질 수도 있습니다. 재시도/백오프 패턴 자체는 OpenAI 429/RateLimitError 재시도·백오프 패턴 글의 전략도 운영 관점에서 참고할 만합니다.

9) 자주 묻는 질문(FAQ)

9-1. ErrImagePullImagePullBackOff 차이는?

  • ErrImagePull: 당장 pull이 실패한 상태
  • ImagePullBackOff: kubelet이 재시도 간격을 늘리며(backoff) 당겨오기를 포기/지연하는 상태

둘 다 원인 분석은 동일하게 Pod 이벤트가 핵심입니다.

9-2. ACR에 Contributor 주면 해결되나요?

대부분 해결되지만 과권한입니다. 이미지 pull에는 AcrPull이면 충분합니다. 운영 보안/감사 관점에서 최소 권한을 권장합니다.

9-3. 같은 이미지가 어떤 네임스페이스에서는 되고, 어떤 곳에서는 안 됩니다

Managed Identity 기반이면 네임스페이스와 무관하게 pull이 되어야 정상입니다. 네임스페이스별로 다르면 다음을 보세요.

  • 특정 네임스페이스에서만 imagePullSecrets를 강제하고 있는데 시크릿이 잘못됨
  • Admission 정책이 이미지를 다른 레지스트리로 rewrite
  • Deployment 템플릿이 서로 다른 ACR을 참조

10) 마무리: 권장 구성 요약

  • 가장 권장: AKS Managed Identity 기반 + ACR에 AcrPull 역할 부여(또는 --attach-acr)
  • Private Link를 쓴다면: DNS(Private DNS Zone 링크)부터 검증
  • imagePullSecrets는 최후의 수단 또는 특수 환경에서만 사용

ImagePullBackOff는 증상이 단순해 보여도, 실제로는 “권한/정체성/네트워크/DNS”가 얽혀 있습니다. 위 순서대로 이벤트로 원인을 확정하고, identity와 AcrPull부터 정리하면 대부분의 케이스는 빠르게 끝납니다.