Fix Kubernetes ImagePullBackOff: Nguyên Nhân và Giải Pháp

intermediate☸️ Kubernetes2026-03-17| Kubernetes 1.20+, mọi cloud provider (EKS, GKE, AKS) hoặc cluster tự host

Error Message

ImagePullBackOff
#kubernetes#image#pull#registry

Lỗi này là gì

Bạn deploy một pod nhưng nó không bao giờ đạt trạng thái Running. Thay vào đó:

$ kubectl get pods
NAME                        READY   STATUS             RESTARTS   AGE
my-app-7d4b9c6f8-xk2p9     0/1     ImagePullBackOff   0          2m

Bạn cũng có thể thấy nó chuyển qua lại giữa ErrImagePullImagePullBackOff — hãy xử lý chúng như nhau. Kubernetes đã cố pull image, thất bại, và hiện đang chờ ngày càng lâu hơn giữa các lần retry (30s → 1m → 2m → tối đa 5m).

Chẩn Đoán Trước

Đừng đoán mò. Hãy lấy thông báo lỗi thực sự trước khi làm bất cứ điều gì:

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

Kéo xuống phần Events ở dưới cùng. Bạn sẽ thấy thứ gì đó cụ thể — authentication required, manifest unknown, pull access denied, hoặc 429 Too Many Requests.

Một dòng đó cho bạn biết chính xác cần áp dụng cách sửa nào.

Nguyên Nhân 1: Tên Image hoặc Tag Sai

Nguyên nhân phổ biến nhất. Lỗi chính tả trong tên image, tag không tồn tại, hoặc image bạn quên push.

# Kiểm tra image mà pod đang thực sự cố pull
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].image}'

# Thử pull thủ công từ một node trong cluster
docker pull nginx:lates   # lỗi chính tả — 'lates' thay vì 'latest'

Phần Events sẽ hiển thị manifest unknown hoặc not found. Sửa tên image hoặc tag trong manifest deployment của bạn:

kubectl set image deployment/my-app my-app=nginx:latest
# hoặc chỉnh sửa manifest trực tiếp
kubectl edit deployment/my-app

Xác minh image tồn tại trong registry trước khi deploy:

# Với Docker Hub
curl -s https://hub.docker.com/v2/repositories/<org>/<repo>/tags/ | jq '.results[].name'

# Với ECR
aws ecr describe-images --repository-name <repo-name> --region <region>

Nguyên Nhân 2: Registry Private — Thiếu imagePullSecret

Các registry private (ECR, GCR, Docker Hub private repos, Harbor) yêu cầu credentials. Không có chúng, Kubernetes không có cách nào để xác thực.

Tìm pull access denied hoặc authentication required trong Events.

Tạo pull secret

# Với Docker Hub / registry thông thường
kubectl create secret docker-registry regcred \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=<your-username> \
  --docker-password=<your-password-or-token> \
  --docker-email=<your-email> \
  -n <namespace>

# Với AWS ECR — lấy login token trước
aws ecr get-login-password --region <region> | \
  kubectl create secret docker-registry ecr-secret \
    --docker-server=<account-id>.dkr.ecr.<region>.amazonaws.com \
    --docker-username=AWS \
    --docker-password-stdin \
    -n <namespace>

Tham chiếu secret trong pod spec của bạn

spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - name: my-app
      image: myprivateregistry.com/my-app:v1.2

Đã có deployment đang chạy? Patch nó thay vì redeploy:

kubectl patch deployment my-app \
  -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}'

Gắn secret vào service account mặc định

Cách này làm cho mọi pod trong namespace tự động sử dụng secret — không cần thêm imagePullSecrets vào từng deployment:

kubectl patch serviceaccount default \
  -p '{"imagePullSecrets":[{"name":"regcred"}]}' \
  -n <namespace>

Nguyên Nhân 3: Credentials Hết Hạn hoặc Không Hợp Lệ

ECR token hết hạn sau mỗi 12 giờ. Một pull secret hoạt động tốt hôm qua có thể âm thầm ngừng hoạt động hôm nay — điều này thường khiến người dùng bị bất ngờ.

Xóa và tạo lại secret:

kubectl delete secret ecr-secret -n <namespace>

aws ecr get-login-password --region <region> | \
  kubectl create secret docker-registry ecr-secret \
    --docker-server=<account-id>.dkr.ecr.<region>.amazonaws.com \
    --docker-username=AWS \
    --docker-password-stdin \
    -n <namespace>

Với workload production, đừng quản lý thủ công như vậy. Hãy dùng Kubernetes CronJob để làm mới token mỗi 8–10 giờ, hoặc tốt hơn là chuyển sang IRSA (IAM Roles for Service Accounts) trên EKS và bỏ hẳn việc dùng static secrets.

Nguyên Nhân 4: Docker Hub Rate Limiting

Docker Hub giới hạn anonymous pull ở mức 100 lần/6 giờ và authenticated pull ở mức 200 lần/6 giờ — tính theo IP. Các node cluster trên cloud thường chia sẻ NAT gateway, nên chỉ vài lần deployment có thể làm cạn kiệt quota trong vài phút.

Events sẽ hiển thị: 429 Too Many Requests hoặc toomanyrequests: You have reached your pull rate limit.

Cách sửa nhanh — xác thực các lần pull của bạn:

kubectl create secret docker-registry dockerhub-creds \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=<username> \
  --docker-password=<access-token> \
  -n <namespace>

Về lâu dài, hãy mirror các image bạn cần vào registry của riêng mình (ECR, GCR, hoặc Harbor) và pull từ đó. Cách này loại bỏ hoàn toàn vấn đề giới hạn rate và tăng tốc độ khởi động pod.

Nguyên Nhân 5: Mạng hoặc Firewall Chặn Registry

Khi node không thể kết nối tới registry, Events hiển thị connection timeout như dial tcp: i/o timeout hoặc context deadline exceeded.

SSH vào một node và kiểm tra trực tiếp:

# SSH vào node và kiểm tra kết nối
curl -v https://registry-1.docker.io/v2/
curl -v https://<account-id>.dkr.ecr.<region>.amazonaws.com/v2/

Ba điều cần kiểm tra: thiếu VPC endpoint cho ECR (traffic outbound đi ra internet công cộng thay vì ở lại nội bộ), security group chặn cổng 443 outbound, hoặc HTTP proxy được cấu hình cho Docker nhưng không cho containerd. Cách sửa phụ thuộc vào trường hợp nào áp dụng cho thiết lập của bạn.

Xác Minh Bản Sửa

# Theo dõi trạng thái pod theo thời gian thực
kubectl get pods -w -n <namespace>

# Chuỗi trạng thái bình thường: Pending → ContainerCreating → Running
# Nếu nó quay lại ImagePullBackOff, hãy describe lại
kubectl describe pod <pod-name> -n <namespace>

# Kiểm tra các event gần đây để xác nhận
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | tail -20

Khi bạn thấy Running với READY 1/1, image đã được pull thành công.

Phòng Ngừa

  • Xác minh image tag tồn tại trong registry trước khi deploy — ghim vào một digest cụ thể hoặc semver tag, không bao giờ dùng :latest trong production
  • Dùng registry cùng vùng với cluster của bạn (ECR cho EKS, GCR cho GKE) để tránh độ trễ cross-region, giới hạn rate, và sự phức tạp về firewall
  • Tự động hóa việc làm mới token ngắn hạn bằng Kubernetes CronJob hoặc ExternalSecret operator — đừng dựa vào việc tạo lại secret thủ công
  • Trên EKS, IRSA kết hợp ECR pull-through cache là thiết lập gọn nhất: không cần quản lý secrets, token tự động xoay vòng
  • Khi không chắc, hãy chạy docker pull <image> từ một node trong cluster trước khi deploy — nhanh hơn việc chờ Kubernetes báo image không tồn tại

Related Error Notes