エラーの内容
Podをデプロイしても Running 状態にならず、代わりに以下が表示されます:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-app-7d4b9c6f8-xk2p9 0/1 ImagePullBackOff 0 2m
ErrImagePull と ImagePullBackOff が交互に表示されることもありますが、どちらも同じ問題として扱ってください。Kubernetesはイメージのプルを試みて失敗し、リトライ間隔を徐々に延ばしながら待機しています(30秒 → 1分 → 2分 → 最大5分)。
まず診断する
推測で動くのはやめましょう。何かを変更する前に、実際のエラーメッセージを確認してください:
kubectl describe pod <pod-name> -n <namespace>
下部の Events セクションまでスクロールします。authentication required、manifest unknown、pull access denied、または 429 Too Many Requests といった具体的な情報が見つかります。
その一行が、どの修正を適用すべきかを正確に教えてくれます。
根本原因1:イメージ名またはタグの誤り
最もよくある原因です。イメージ名のタイポ、存在しないタグ、またはプッシュし忘れたイメージが該当します。
# Podが実際にプルしようとしているイメージを確認する
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].image}'
# クラスターノードから手動でプルを試みる
docker pull nginx:lates # タイポ — 'latest'ではなく'lates'になっている
Eventsセクションに manifest unknown または not found と表示されます。デプロイメントマニフェストのイメージ名またはタグを修正してください:
kubectl set image deployment/my-app my-app=nginx:latest
# またはマニフェストを直接編集する
kubectl edit deployment/my-app
デプロイ前にレジストリにイメージが存在することを確認してください:
# Docker Hub の場合
curl -s https://hub.docker.com/v2/repositories/<org>/<repo>/tags/ | jq '.results[].name'
# ECR の場合
aws ecr describe-images --repository-name <repo-name> --region <region>
根本原因2:プライベートレジストリ — imagePullSecret の不足
プライベートレジストリ(ECR、GCR、Docker Hubのプライベートリポジトリ、Harbor)には認証情報が必要です。それがなければ、Kubernetesは認証する手段がありません。
Eventsに pull access denied または authentication required が表示されているか確認してください。
プルシークレットを作成する
# Docker Hub / 汎用レジストリの場合
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>
# AWS ECR の場合 — 先にログイントークンを取得する
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>
Pod specでシークレットを参照する
spec:
imagePullSecrets:
- name: regcred
containers:
- name: my-app
image: myprivateregistry.com/my-app:v1.2
すでに稼働中のデプロイメントがある場合は、再デプロイせずにパッチを適用してください:
kubectl patch deployment my-app \
-p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}'
デフォルトのサービスアカウントにシークレットを付与する
これにより、名前空間内のすべてのPodが自動的にシークレットを使用するようになります。各デプロイメントに imagePullSecrets を追加する必要がなくなります:
kubectl patch serviceaccount default \
-p '{"imagePullSecrets":[{"name":"regcred"}]}' \
-n <namespace>
根本原因3:期限切れまたは無効な認証情報
ECRトークンは12時間ごとに期限切れになります。昨日まで使えていたプルシークレットが、今日は気づかないうちに機能しなくなることがあります。これは多くの人が不意を突かれる問題です。
シークレットを削除して再作成してください:
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>
本番環境では手動管理は避けましょう。Kubernetes CronJobを使って8〜10時間ごとにトークンを更新するか、EKSではIRSA(IAM Roles for Service Accounts)に移行して静的シークレットを完全に排除することをお勧めします。
根本原因4:Docker Hub のレート制限
Docker Hubは匿名プルを6時間あたり100回、認証済みプルを6時間あたり200回に制限しており、IPアドレス単位でカウントされます。クラウドクラスターのノードはNATゲートウェイを共有することが多く、少数のデプロイメントだけで数分以内にクォータを使い果たしてしまうことがあります。
Eventsには 429 Too Many Requests または toomanyrequests: You have reached your pull rate limit と表示されます。
手早い修正 — 認証済みプルを設定する:
kubectl create secret docker-registry dockerhub-creds \
--docker-server=https://index.docker.io/v1/ \
--docker-username=<username> \
--docker-password=<access-token> \
-n <namespace>
長期的な対策として、必要なイメージを自分のレジストリ(ECR、GCR、またはHarbor)にミラーリングしてそこからプルするようにしましょう。レート制限の問題がなくなり、Podの起動も高速化されます。
根本原因5:ネットワークまたはファイアウォールによるレジストリのブロック
ノードがレジストリに全くアクセスできない場合、Eventsには dial tcp: i/o timeout や context deadline exceeded のような接続タイムアウトが表示されます。
ノードにSSHして直接テストしてください:
# ノードにSSHして接続性をテストする
curl -v https://registry-1.docker.io/v2/
curl -v https://<account-id>.dkr.ecr.<region>.amazonaws.com/v2/
確認すべき点は3つあります:ECR用のVPCエンドポイントが設定されていない(アウトバウンドトラフィックが内部に留まらずパブリックインターネットに出てしまう)、アウトバウンドポート443をセキュリティグループがブロックしている、またはDockerには設定されているがcontainerdには設定されていないHTTPプロキシがある、などです。修正方法は、どの問題が環境に当てはまるかによって異なります。
修正を確認する
# Podのステータスをリアルタイムで監視する
kubectl get pods -w -n <namespace>
# 正常な遷移: Pending → ContainerCreating → Running
# ImagePullBackOffに戻る場合は再度describeを実行する
kubectl describe pod <pod-name> -n <namespace>
# 確認のため最近のイベントをチェックする
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | tail -20
READY 1/1 の Running 状態が確認できれば、イメージのプルは成功しています。
予防策
- デプロイ前にレジストリにイメージタグが存在することを確認してください。特定のダイジェストまたはsemverタグに固定し、本番環境では
:latestを絶対に使わないようにしましょう - クラスターと同じ場所にあるレジストリを使用してください(EKSならECR、GKEならGCR)。クロスリージョンのレイテンシ、レート制限、ファイアウォールの複雑さを回避できます
- Kubernetes CronJobまたはExternalSecretオペレーターを使って有効期間の短いトークンを自動更新してください。手動でシークレットを再作成することに依存しないようにしましょう
- EKSでは、IRSA + ECRプルスルーキャッシュが最もクリーンな構成です。管理すべきシークレットがなく、トークンは自動的にローテーションされます
- 迷ったときは、デプロイ前にクラスターノードから
docker pull <image>を実行してください。Kubernetesがイメージの存在しないことを教えてくれるまで待つより早く確認できます

