nykergoto’s blog

機械学習とpythonをメインに

docker pull をすると toomanyrequests でエラーになるやつ

docker pull をすると toomanyrequests でエラーになる

ERROR: Preparation failed: Error response from daemon: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit (executor_docker.go:188:2s)

原因はエラーメッセージにも書いてますが dockerhub からの pull 回数の制限に引っかかったこと。ちょっと前から制限が追加されたようでした。

matsuand.github.io

まずこの制限によって pull が出来ないようになっているかは API へリクエストを送って返ってくる status で確認できます。以下の shell を実行しましょう。 jqcurl が必要なので適宜 install しましょう。

TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest

こんな response が帰ってきます。

HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: "sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"
date: Sat, 22 May 2021 15:01:23 GMT
strict-transport-security: max-age=31536000
ratelimit-limit: 100;w=21600
ratelimit-remaining: 45;w=21600

中にある ratelimit-remaining がこのマシン (おそらくIPごと) に対して割り当てられている残りの pull 可能回数です。この例だと残り回数は 45 になっています。先のエラーになっている場合ここが 1 とか 0 になってるはずで、回数制限にかかっていることが確かめられます。

対策方法

例えば CI で docker pull を使っている場合を想定します。この場合毎回 0 から build すると都度 dockerhub へのリクエストが発生してしまいます。これを別の registry へ都度 image を push しておいて build 時には前の image をキャッシュとして利用する戦略を使うことで dockerhub へのリクエスト数を減らすことが出来ます。その代わり代わりレジストリへのアクセスは増えるので場合によっては (たとえば ECR など)、お金がかかる用になるのは注意点。

以下は gitlab ci の形式の例です。

.build-and-push-ecr:
  stage: build
  script:
    # Pull cache docker image from AWS ECR
    # ここは場合によっては gitlab-registry でも良いと思う 
    - docker pull ${ECR_REPO}:latest || true

    # pull image を cahche として指定して build
    - docker build --cache-from ${ECR_REPO}:latest -t ${CI_PROJECT_NAME}:latest -f ${DOCKERFILE} .

    # ECR へ push
    - docker tag ${CI_PROJECT_NAME}:latest ${ECR_REPO}:latest
    - docker tag ${CI_PROJECT_NAME}:latest ${ECR_REPO}:${CI_COMMIT_SHA}
    - docker push ${ECR_REPO}:latest
    - docker push ${ECR_REPO}:${CI_COMMIT_SHA}
  variables:
    DOCKERFILE: ./docker/django/Dockerfile
    ENV_PATH: 
    ECR_REPO: # your-ecr-repo-name

余談: azure だと関係ないかもしれない

Azure のインスタンスの場合、先の ratelimit-remaining を確認すると値が設定されていないようでした(n=2)。 dockerhub の公式を見ると一部認められた publisher 等は制限なしになっているみたいなので azure と dockerhub とがパートナーシップを持っているからなのかな?

I don’t see any RateLimit headers If you do not see these headers, that means pulling that image would not count towards pull limits. This could be because you are authenticated with a user associated with a Legacy/Pro/Team Docker Hub account, or because the image or your IP is unlimited in partnership with a publisher, provider, or open source organization.

一応 Azure 公式には認証して pull しようねという記載があるので、上記現象はあまり pull を行っていないからたまたま ratelimit が表示されていないだけかもしれないです。謎。

最初の手順として、現在、ビルドまたはデプロイのワークフローの一環として Docker Hub からパブリック イメージをプルしている場合、匿名のプル要求を行うのではなく、Docker Hub アカウントを使用して認証することをお勧めします。

docs.microsoft.com