docker/k8s『Image Pull BackOff』の原因と対処法
- 作成日 2025.11.05
- その他
KubernetesのPodがコンテナイメージ取得に失敗し、一定間隔で再試行し続ける状態。典型原因はイメージ名・タグの誤り、プライベートレジストリの認証不備、ネットワークやDNSの障害、プロキシ/ファイアウォール、レート制限、レジストリ側の障害など。失敗理由はノード側のイベント/ログに必ず痕跡が残るため、原因を特定してからタグ・資格情報・ネットワークのいずれかを修正する。
- 1. 症状と発生条件(まず把握)
- 2. 最短切り分け(「どこで」「なぜ」落ちたかを確認)
- 3. イメージ名とタグを正規化(404/manifest unknown対策)
- 4. プライベートレジストリ認証(imagePullSecretsで401/403を解消)
- 5. ネットワーク/DNS/プロキシの検査(name resolution/timeout対策)
- 6. レート制限/スロットリング(429/Too Many Requests対策)
- 7. レジストリ証明書/社内CA(x509/証明書エラー対策)
- 8. イメージの存在確認と公開設定(404/deniedの根治)
- 9. Podテンプレートの更新と再作成(BackOffの持続解消)
- 10. ノード/CRIのpull並列制御と再試行(一時障害に強くする)
- 11. 最小再現テンプレ(小さな公開イメージで健全性確認)
- 12. NG→OK早見(クイック修正)
- 13. チェックリスト(上から順に潰す)
症状と発生条件(まず把握)
・PodがPending→ContainerCreating→ImagePullBackOff / ErrImagePullを繰り返す。
・ノードCRI(containerd/CRI-O/Docker)でのpullが401/403/404/429/500系などで失敗。
・誤ったレジストリホスト名/リポジトリ名/タグ、認証情報がない/期限切れ、ネットワーク不可達、DNS解決失敗、プロキシ設定不整合、レート制限到達などで再現。
最短切り分け(「どこで」「なぜ」落ちたかを確認)
# Podの概要と直近イベント
kubectl get pods -n <ns>
kubectl describe pod <pod> -n <ns> | sed -n '/Events/,$p'
# イメージ別のpullログ(失敗理由のHTTPコードやメッセージ)
kubectl logs <pod> -n <ns> --previous --all-containers=true 2>/dev/null || true
# ノードで直接pull(権限・タグ・ネットワーク検証)
crictl pull <image:tag> # containerd/CRI-O
# or
sudo nerdctl -n k8s.io pull <image:tag>
# or (Dockerホストなら)
docker pull <image:tag>イメージ名とタグを正規化(404/manifest unknown対策)
・<レジストリ/リポジトリ/タグ/ダイジェスト>の4要素を明示。latest依存は避け、ダイジェスト固定を推奨。
# NG: あいまいなlatest
image: ghcr.io/org/app:latest
# OK: 明示タグ or ダイジェスト固定
image: ghcr.io/org/app:v1.2.3
image: ghcr.io/org/app@sha256:... # 再現性重視
プライベートレジストリ認証(imagePullSecretsで401/403を解消)
# docker login 相当のSecretを作成
kubectl create secret docker-registry regcred \
--docker-server=ghcr.io \
--docker-username=YOUR_USER \
--docker-password=YOUR_TOKEN \
--docker-email=dev@example.com -n <ns>
# Pod/ServiceAccountへ紐付け
kubectl patch serviceaccount default -n <ns> \
-p '{"imagePullSecrets":[{"name":"regcred"}]}'# 直接Podマニフェストへ指定する例
apiVersion: v1
kind: Pod
metadata: { name: app, namespace: default }
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: ghcr.io/org/app:v1.2.3ネットワーク/DNS/プロキシの検査(name resolution/timeout対策)
# ノード内でレジストリFQDNを解決・到達確認
nsenter -t $(pgrep -f kubelet | head -n1) -m -n -- sh -lc "
getent hosts ghcr.io && \
nc -vz ghcr.io 443 || true"
# Podからも確認(BusyBox等)
kubectl run dnstest --rm -it --image=busybox --restart=Never -- sh -c "
nslookup ghcr.io && wget -qO- https://ghcr.io || true"・企業プロキシ下ではノード側のCRIにHTTP(S)_PROXY/NO_PROXYを設定し、レジストリFQDNをNO_PROXYに含める。
レート制限/スロットリング(429/Too Many Requests対策)
・Docker Hub等のpull制限に到達すると連続失敗→BackOff。
・自前レジストリ/キャッシュ(proxy)の導入、CI/CDでのpull節約(イメージ再利用・ダイジェスト固定)を検討。
# ダウンロード重複を抑える(同一ノードでのWarm-upなど)
crictl images | grep <image> || crictl pull <image:tag>レジストリ証明書/社内CA(x509/証明書エラー対策)
・社内レジストリ/自己署名ではノードにCAを配布。containerdなら/etc/containerd/certs.d//hosts.tomlを用意。
# containerd の hosts.toml 例(TLS/ミラーを定義)
sudo mkdir -p /etc/containerd/certs.d/registry.intra.local
sudo tee /etc/containerd/certs.d/registry.intra.local/hosts.toml <<'EOF'
server = "https://registry.intra.local"
[host."https://registry.intra.local"]
capabilities = ["pull", "resolve"]
ca = "/etc/ssl/certs/intra-ca.pem"
EOF
sudo systemctl restart containerdイメージの存在確認と公開設定(404/deniedの根治)
# GitHub Container Registry 例:可視性・権限を確認
# 1) リポジトリの"packages"が公開か、PATにread:packagesが含まれるか
# 2) 該当タグ/ダイジェストが存在するか(UI/gh apiで確認)
# CLIでダイジェストを固定確認(Docker)
docker pull ghcr.io/org/app:v1.2.3
docker inspect ghcr.io/org/app:v1.2.3 --format '{{.RepoDigests}}'Podテンプレートの更新と再作成(BackOffの持続解消)
# Deployment のイメージ差し替え
kubectl set image deploy/app app=ghcr.io/org/app:v1.2.4 -n <ns>
kubectl rollout status deploy/app -n <ns>
# ロールアウトが止まる場合のイベント確認
kubectl describe deploy/app -n <ns> | sed -n '/Events/,$p'
・誤タグ修正やimagePullSecrets追加など、Spec差分を適用後にBackOffは解消される。
ノード/CRIのpull並列制御と再試行(一時障害に強くする)
# containerdのコンフィグ(/etc/containerd/config.toml)を作成/編集
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
# 必要に応じて timeout や registry 設定、mirrors を記述
sudo systemctl restart containerd・CI/CDの大量同時デプロイ時はロールアウトのmaxUnavailable/Surgeを調整して集中を緩和。
最小再現テンプレ(小さな公開イメージで健全性確認)
# まずは確実にpullできる公式イメージでノード健全性を確認
kubectl run pull-test --image=busybox:1.36 --restart=Never --command -- sh -c "echo ok && sleep 5"
kubectl get pod pull-test -w
kubectl logs pull-test || true
kubectl delete pod pull-testNG→OK早見(クイック修正)
・NG:latestに依存 → OK:固定タグ/ダイジェストで再現性を確保。
・NG:プライベートなのにimagePullSecrets未設定 → OK:docker-registry SecretをSA/Podに紐付け。
・NG:ノードからレジストリFQDN解決不可 → OK:CoreDNS/ノードDNS/NO_PROXYを整備。
・NG:レート制限に突入 → OK:キャッシュ/ミラー/pull削減で回避。
・NG:自己署名レジストリでx509失敗 → OK:CA配布+containerd hosts.toml。
・NG:404/manifest unknown → OK:リポジトリ名/タグを正規化し存在確認。
・NG:BackOffのまま放置 → OK:Spec修正→rollout監視で確実に復旧。
チェックリスト(上から順に潰す)
・describeのEventsに出ているHTTPコード/理由を読む(401/403/404/429/5xx)。
・イメージ名・タグ・ダイジェストが正しいか、非公開ならimagePullSecretsは正しく紐付いているか。
・ノードからレジストリFQDNへDNS解決/443到達できるか(プロキシ/Firewall/NO_PROXY)。
・レート制限やレジストリ障害の可能性はないか(時間を置く・ミラー使用)。
・自己署名/社内CAはノードCRIへ配布されているか。
・Deployment/PodSpecを更新してロールアウトが完了したか。
・小さな公開イメージでpull健全性をまず確認し、問題の切り分けを終える。
-
前の記事
GAS『Unknown Error Occurred』の原因と対処法 2025.11.05
-
次の記事
docker『Cannot Connect to the Docker Daemon』の原因と対処法 2025.11.06
コメントを書く