Docker Hub の Rate Limitに引っかかったのでdocker loginで対策した
AWSのcodebuildで、docker buildが失敗するようになりその対応をしたので備忘録です。
Docker Hub の Rate Limitとは
英語になりますがこちらに記載があります。
The rate limits will be progressively lowered to a final state of 100 container image requests per six hours for anonymous usage, and 200 container image requests per six hours for free Docker accounts. Image requests exceeding these limits will be denied until the six hour window elapses.
ざっくり私の翻訳
rate limits を徐々に低くしていって、最終的には匿名ユーザーは100 image リクエスト/6時間、free Dockerユーザーには200 image リクエスト/6時間にするから!それ超えたら6時間拒否するからね!
ちなみに現状では以下のlimitとなっているようです。
これを見た私の感想は、「フーン、まぁそんなにたくさんimage リクエストなんてしないし大丈夫だろ、うちはそんなたくさんCICDする組織じゃないし( ̄σ・ ̄*)」という感じでした。
エラーに遭遇、原因判明
そんな気持ちのままAWSのCodeBuild(CodePipeline経由)でdocker buildを行っていると、以下のエラーが・・
toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit
!?まだ5回くらいしかbuildしていないのに?
少し考えて原因がわかりました。
匿名ユーザーのリクエストのカウントがIPアドレス単位なので、CodeBuildのIPアドレスでカウントされるということでした。
勝手に頭の中で以下のようにユーザー単位でカウントされると思い込んでしまっていたのですが、、
実際は以下のようなイメージですね。IPを共有するので、初めてdocker buildを行う場合でもほかのユーザーの影響を受けてエラーになる可能性があります。
※実際はCodeBuildのIPアドレスは複数ありますが、共有するというイメージで1つで書いています。
対策
対策は以下のようにいくつか考えられます。
- ECRに定期的にイメージをコピーしておく。
- CodeBuildをVPC内で実行させ、共用ではなくElastic IPなど固定のIPアドレス経由でDocker Hubにアクセスする
- Docker Hubユーザーを作成してログイン処理を行う
今回は手間が一番かからなそうなログイン処理を実装することにしました。手順をまとめておきます。
Docker Hub ユーザーの作成
Free Planでユーザーを作成します。メールアドレスが必要です。
Docker Hubのページから、Sign Upします。
画面の指示に従っていけばユーザーの作成が完了します。
作成、ログイン後、アクセストークンを発行します。パスワードでログイン処理も可能なのですが、トークンのほうが無効化できたり、プロジェクトごとで分けれるといった利点があるため今回はトークンにしました。
右上のユーザー名>Account Settingsを選択
左側のメニューからSecurityを選択し、右側のNew Access Tokenをクリックします。
Descriptionが必須のため、任意の文字を入力してCreateします。
Tokenが表示されるのでコピーしておきます。Closeするともう表示できなくなるため注意です。
SSM Parameter Storeへログイン情報格納
ログイン情報を作成できたので、AWS側で使用します。Codebuildで使用するbuildspec.ymlにログイン情報を直接記載するのはセキュリティ的によろしくないので、Systems ManagerのParameter Storeを使用します。 他にはSecrets Managerという選択肢もあるのですが、今回はローテーションを考慮しないかつ無料であるParameter Storeを使用します。
AWS Systems Manager>パラメータストア>パラメータを作成の順で選択します。
まずはdockerhub-userを作成します。(名前は任意でOKです)
情報を秘匿化したいため、安全な文字列を選択します。暗号化に使用するキーの指定が必要ですが、今回はAWS管理のデフォルトのものを使用します。
値にユーザー名を入力して作成します。
dockerhub-tokenも同じように作成します。
buildspec.ymlの修正
ログイン情報が格納できたので、buildspecに記載しているdocker buildの部分を修正します。
※Codebuildの仕様はこちらを参考にしてください。
以下のように、envのところにparameter-storeのところに作成したパラメータストアのKey名を書いておきます。
env: parameter-store: DOCKER_USER: dockerhub-user DOCKER_TOKEN: dockerhub-token
pre_buildフェーズのところで、ecrのみログインしていたところを、dockerhubにもログインするよう追記します。
「echo $DOCKER_TOKEN・・」の行です。パスワードとユーザー名は先ほどenvに記載したパラメータストアから取得しています。
pre_build: commands: - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com - echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin
CodeBuildを実行してログを見てみると、以下の通りログインに成功していることが見れます。
[Container] 2020/11/20 14:34:30 Running command echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [Container] 2020/11/20 14:34:32 Phase complete: PRE_BUILD State: SUCCEEDED
ログは割愛しますがdocker buildも無事成功しています。
これで作成したユーザーで200回/6時間まではlimitに引っかからないでしょう。
まとめ、感想
同じように失敗して困った方、なにかの参考になれば幸いです。
200回/6時間の制限はこの対策を行った後もあるため、多くのdocker buildを行う方は注意が必要ですね。
また今後は別の対策として、AWSのECRのパブリック化するようなので、今後はそちらも活用できるかもしれません。