Skip to content

필사 모드: Docker Hub の前にプロキシキャッシュを置く三つの方法 — registry:2、Nexus、ECR pull-through cache

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

問題は速度ではなく制限だ

CI が一回回るたびに FROM python:3.12-slim が Docker Hub から降りてくる。ノードが十台なら十回、パイプラインが一日に百回回れば百回。Docker Hub はそれを数えている。

Docker の文書が示す制限はこうだ。ログインしていないクライアントは IPv4 アドレス一つ(または IPv6 /64)あたり 6 時間に 100 回、個人アカウントでログインすれば 200 回、Pro・Team・Business は無制限。会社のオフィスやクラスタのように複数の機械が NAT の後ろから一つのアドレスで出て行くなら、その 100 回を全員で分け合う。

数字を文書だけで信じず、ヘッダを読んでみた。Docker が制限確認用に置いている ratelimitpreview/test リポジトリに匿名トークンでマニフェストを要求すると、応答ヘッダに残り回数が載る。

TOK=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
curl -s -I -H "Authorization: Bearer $TOK" \
  https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest | grep -i ratelimit
ratelimit-limit: 100;w=3600
ratelimit-remaining: 85;w=3600

何度か実験する間に remaining は 91 → 88 → 85 と減った。ヘッダの窓(w=3600)は秒単位で 3,600 と書いてあるが、文書は 6 時間と言っている。どちらが正しいにせよ、この確認要求そのものも一回として数えられることは確かだ。

プロキシキャッシュはこの数字を守る装置だ。クラスタのすべてのノードが Docker Hub ではなく自分のキャッシュに聞けば、同じイメージは上流から 一度だけ 降りてくる。

最小の実験:registry:2 を pull-through キャッシュに

Docker が配布する registry:2 イメージは、環境変数一つで読み取り専用プロキシになる。

docker run -d --name regmirror -p 5001:5000 \
  -v $PWD/data:/var/lib/registry \
  -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
  registry:2

この状態で localhost:5001/library/nginx:1.27-alpine を引くと、キャッシュは無いレイヤを Docker Hub から受け取ってディスクに置き、クライアントに渡す。ローカルのデーモンに nginx のレイヤが一つも無いことを確認してから(docker images | grep nginx が 0 行)、同じイメージを三回引き、毎回ローカルの複製を消した。

試行所要時間キャッシュディレクトリ備考
1 回目(キャッシュ経由)7.81 秒22M(ブロブファイル 15 個)キャッシュが空で上流から満たす
2 回目(キャッシュ経由)3.95 秒22M(変化なし)ディスクから直接
3 回目(キャッシュ経由)4.01 秒22M(変化なし)ディスクから直接
Docker Hub 直接2.86 秒比較用

docker images が報告するイメージの大きさは 76.8MB だが、キャッシュは 22M だ。キャッシュが保管するのは圧縮されたレイヤのブロブで、デーモンが報告するのは展開後の大きさだからだ。

実験が語ること

最初の pull は遅くなる。 上流から受け取り、ディスクに書き、また渡す二段階なので、直接引くこと(2.86 秒)より三倍近くかかった。

二回目以降も直接より速くはなかった。 家庭の回線で Docker Hub を直接引くのが 2.86 秒、キャッシュが満たされた後にプロキシを通すのが約 4 秒。プロキシコンテナが同じノートパソコンで動いていて、ホップが一つ多いことになる。回線が速くクライアントが一台だけなら、プロキシキャッシュは 速度のための装置ではない。

得られるのは上流への要求数だ。 2・3 回目の pull でキャッシュディレクトリは 1 バイトも増えなかった。レイヤは上流から受け取り直していないということだ。ノードが二十台のクラスタなら、Docker Hub が数える回数は二十ではなく一に近づく。これがプロキシキャッシュの価値だ。

正直に書いておくことが一つある。registry:2 は上流に出した要求を info ログに残さないので、pull ごとにマニフェスト確認を何回するかは数えられなかった。ディスクが増えなかったという事実から、レイヤの再ダウンロードが無かったことまでしか言えない。

Nexus:プロキシ、ホステッド、そしてグループ

registry:2 のプロキシは上流を一つしか見ない。会社ではふつう Docker Hub も見て、自分で作ったイメージも上げて、両方を一つのアドレスで使いたい。Sonatype Nexus Repository の Docker リポジトリは三種類ある。

  • proxy — リモートリポジトリをキャッシュする。Docker Hub なら Remote storage に https://registry-1.docker.io を書き、Docker Index は「Use Docker Hub」を選ぶ(検索は index.docker.io が担当するので、二つを別々に書く)。
  • hosted — 自分が push する場所。
  • group — 複数の proxy・hosted を 一つの URL にまとめる。文書はグループを「ユーザーに読み取り権限ですべてのリポジトリを公開する推奨の方法」と呼び、メンバーは「望む順序で」入れる。グループに新しいリポジトリを入れれば、クライアント設定を変えなくてもすぐ見える。

クライアント側の設定は、Repository Connector のポート一つ(例:nexus.example.com:8082)をデーモンの mirror に登録すれば終わりだ。その後 docker pull nginx はグループ → メンバーの順に探し、hosted に無ければ proxy が Docker Hub から取ってきてキャッシュする。

Nexus が ECR を上流に 置くときは認証が特殊だ。ECR のトークンは 12 時間ものだが、Nexus の文書によれば最初の pull でそのトークンをキャッシュし、六時間ごとに更新する。AWS の資格情報を Nexus に一度渡せば、あとは Nexus が回す。

ECR pull-through cache:AWS の中でキャッシュする

クラスタが AWS にあるならキャッシュを別に運用する必要はなく、ECR がその仕事をする。ルール(pull through cache rule)に上流と接頭辞を書いておけば、<アカウント>.dkr.ecr.<リージョン>.amazonaws.com/<接頭辞>/library/nginx:1.27-alpine を引くときに ECR が上流から受け取り、自分のアカウントの ECR リポジトリに保管する。

文書で確認したルールはこうだ。

  • 認証なしで使える上流は ECR Public、Kubernetes コンテナイメージレジストリ、Quay の三つ。Docker Hub、Azure Container Registry、GitHub・GitLab Container Registry、Chainguard は Secrets Manager のシークレットが必須で、別アカウントの ECR は IAM ロールで認証する。
  • そのシークレットの名前は ecr-pullthroughcache/ で始まらなければならず、ルールと同じアカウント・リージョンにある必要がある。Docker Hub をアカウントで引くことになるので、匿名の 100 回ではなくそのアカウントの制限を使う。
  • 同じタグをもう一度引くとき、過去 24 時間以内に上流と照合していれば上流に問い合わせず キャッシュをそのまま返す。窓が過ぎていれば新しい版を確認して更新する。タグを上書きして配備する習慣があるなら、一日まで古いイメージを受け取ることがある。
  • 上流の更新に失敗しても、最後のキャッシュは引き続き降りてくる。Docker Hub が落ちても、すでに引いたイメージでの配備はできるということだ。
  • キャッシュリポジトリにタグの不変性(immutability)を有効にすると、同じタグの更新が止まる。
  • マルチアーキテクチャのイメージは、マニフェストリストとその中のアーキテクチャすべてを取ってくる。一つだけ欲しければ、そのアーキテクチャのダイジェストで引く。
  • 最初の pull は ECR が上流に出て行く必要があるので、インターネットへの経路が要ることがある。PrivateLink エンドポイントしか無い VPC は最初の pull が失敗しうるので、文書は経路をあらかじめ用意するよう勧めている。以降の pull には要らない。
  • AWS Lambda は pull-through cache ルールで作られたイメージをサポートしない。

どれを選ぶか

registry:2 proxyNexus(proxy + group)ECR pull-through cache
運用負担コンテナ一つサーバー一台とディスク管理なし(マネージド)
上流一つ複数をグループにルールごとに一つ
自分のイメージの push不可(読み取り専用)hosted へ通常の ECR リポジトリへ
更新確認pull ごとに上流のマニフェストを確認設定可能タグごとに 24 時間に一度
合う場所ホームラボ、ノード数台社内、言語パッケージまで一か所にAWS 内の EKS・ECS

私たちのパイプラインでは

LabHub のビルドは Jenkins が kaniko でイメージを作って私設 Harbor に上げ、ArgoCD がそのタグを配備する。ベースイメージは Dockerfile にダイジェストまで固定してある。

FROM python:3.12-slim@sha256:78387bc3…

プロキシキャッシュを前に置いてもこの行はそのままだ。ダイジェストは内容のハッシュなので、どのキャッシュを経由しても同じバイトでなければ通らない。キャッシュが制限を守り、ダイジェストが内容を守る。二つの装置は別の仕事をしている。

一行で

プロキシキャッシュは pull を速くする装置ではなく、上流が数える回数を一つに減らす 装置だ。ノードが数台なら registry:2 で十分、リポジトリが複数なら Nexus のグループで一つのアドレスを作り、AWS の中なら ECR に代わりにキャッシュさせればよい。

현재 단락 (1/54)

CI が一回回るたびに `FROM python:3.12-slim` が Docker Hub から降りてくる。ノードが十台なら十回、パイプラインが一日に百回回れば百回。Docker Hub はそれ...

작성 글자: 0원문 글자: 4,651작성 단락: 0/54