エラーの発生通常、最悪のタイミングで発生します。CI/CDパイプラインが順調に動いている最中、ビルドがステータスコード429で失敗します。Dockerエンジンがベースイメージのプルを拒否し、次のメッセージが表示されます。
toomanyrequests: You have reached your pull rate limit.
発生の理由Docker Hubは無制限に無料の帯域を提供しているわけではありません。2020年後半以降、Dockerはコスト管理のために厳格な制限を導入しました。ログインせずにイメージをプルする場合、DockerはIPアドレスを追跡します。ログインしている場合は、特定のアカウントを追跡します。
- **匿名ユーザー:**6時間あたり100プル(IPアドレスで追跡)。- **認証済みの無料ユーザー:**6時間あたり200プル。- **Pro/Team/Businessアカウント:**1日あたり5,000プル、または無制限。共有環境が最も問題を引き起こします。大規模なオフィスやクラウドのVPCでは、数十人の開発者が1つのNAT IPアドレスを共有することがよくあります。つまり、1人のローカルテストがエンジニアリングチーム全体のプル制限を使い果たしてしまう可能性があるのです。
解決策1:Docker CLIで認証を行う最も手っ取り早い解決策はログインすることです。無料アカウントであっても、制限は100回から200回へと倍増します。さらに重要なのは、制限が共有オフィスのIPではなく、個人のユーザー名に紐付けられることです。
# ローカルマシンまたはサーバーでログインする
docker login --username your_dockerhub_username
CI/CDパイプラインでメインのパスワードを使用するのは避けてください。代わりに、Docker HubのAccount Settings > Securityで**個人アクセストークン(PAT)**を生成します。このトークンを自動化スクリプトのシークレット変数として使用してください。
GitHub Actionsの例:```
- name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }}
## 解決策2:プルスルーキャッシュを設定するもしKubernetesクラスターや大規模なビルドファームを管理している場合、毎回Docker Hubからプルするのは非効率です。HarborやSonatype Nexusのようなローカルレジストリミラーを使用すると、イメージをローカルにキャッシュできます。最初の要求はDocker Hubからプルされますが、それ以降の要求はローカルネットワーク内で完結します。
ミラーを設定するには、Linuxホスト上の `/etc/docker/daemon.json` を編集します:
{ "registry-mirrors": ["https://"] }
Dockerサービスを再起動して変更を適用します:
sudo systemctl restart docker
## 解決策3:代替レジストリに切り替える多くの公式イメージは、クラウドユーザー向けに制限が緩和されている、あるいは無料枠があるレジストリにミラーリングされています。Dockerfileの `FROM` 行を変更することで、Docker Hubを完全に回避できます。
- **GitHub Container Registry:** `ghcr.io/owner/image:tag`- **Google Artifact Registry:** `mirror.gcr.io/library/image:tag`- **Amazon ECR Public Gallery:** `public.ecr.aws/docker/library/image:tag`例えば、Hubの制限を回避するためにAWSから公式のPythonイメージをプルします:
次の代わりに: FROM python:3.11
FROM public.ecr.aws/docker/library/python:3.11
## 解決策4:KubernetesでのImagePullBackOffを修正するノードがレート制限に達すると、Kubernetesのポッドは `ImagePullBackOff` 状態で停止することがよくあります。これは `imagePullSecrets` オブジェクトを作成することで解決できます。この認証情報により、K8sはDocker Hubに対して自身を識別できるようになります。
1. ネームスペースにシークレットを作成する
kubectl create secret docker-registry dockerhub-creds
--docker-username=YOUR_USER
--docker-password=YOUR_PAT_TOKEN
--docker-email=YOUR_EMAIL
2. deployment.yamlでシークレットを参照する
spec: template: spec: containers: - name: my-app image: nginx:1.25 imagePullSecrets: - name: dockerhub-creds
## 残りの制限を確認する方法残りのプル回数を推測する必要はありません。`curl` を使用してトークンをリクエストし、レスポンスヘッダーを確認してください。これにより、認証が正しく機能しているかどうかも確認できます。
レート制限サービス用のトークンを取得する
TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
レート制限のヘッダーを確認する
curl -I -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
ターミナル出力の以下の行を確認してください:
ratelimit-limit: 200;w=21600 ratelimit-remaining: 185;w=21600
`w=21600` は、6時間のウィンドウを秒単位で表しています。`ratelimit-limit` が200または5000と表示されていれば、認証は有効です。
## 本番環境におけるベストプラクティス- **バージョンを固定する:** `:latest` の使用はやめましょう。これを使うと、Dockerは毎回レジストリに更新がないか確認しに行き、プル枠を無駄に消費します。- **依存関係を内部化する:** ミッションクリティカルなアプリの場合は、パブリックイメージをプライベートなECRやGCRリポジトリにコピーしてください。これにより、Docker Hubのダウンタイムや料金体系の変更から保護されます。- **レイヤーキャッシュ:** DockerレイヤーをキャッシュするようにCIプロバイダーを設定してください。これにより、コミットのたびにベースイメージを再ダウンロードするのを防ぐことができます。

