投稿日:
更新日:

Docker Hub の Rate Limit 対策

Authors

目次

banner.png

はじめに

2025 年 4 月 1 日から Docker Hub の Rate Limit が引き締められ、未認証ユーザによるイメージ Pull は 1 時間あたり 10 回まで に制限されました。

why-discord-is-switching-from-go-to-rust.png

Rate Limit に引っかかると、Docker Hub からのイメージ Pull が一定の期間規制されるため、コンテナが起動できなくなる危険があります。 特に、マネージド Kubernetes の場合、イメージ Pull はクラスタの IP(NAT IP 等)アドレスを送信元としてリクエストされるため、同一の IP アドレスから大量の Pull リクエストを送信すると、その IP に対して Docker Hub の Rate Limit が適用されます。

このため、単一の Pod が Rate Limit に引っかかると、他の Pod もイメージの取得ができなくなり、ImagePullBackOff や ErrImagePull に陥る可能性があります。

You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

今回は Google Cloud Artifact Registry の Remote Repository と Amazon ECR の Pull Through Cache を活用した Docker Hub の Rate Limit 対策を紹介したいと思います。

Docker Hub の Rate Limit

ユーザタイプ1 時間あたりのプルレート制限パブリックリポジトリ数プライベートリポジトリ数
Business / Team / Pro(認証済)無制限(フェアユース)無制限無制限
Personal(認証済)100無制限最大 1 つ
未認証ユーザIPv4 アドレスまたは IPv6/64 サブネットごとに 1 時間あたり 10 回該当なし該当なし

依然として、Docker Hub の Rate Limit は未認証ユーザに対して大きな影響を与えます。 Docker Hub にログインしている場合や、PAT(Personal Access Token) で認証している場合は、Rate Limit の影響が緩和されます。

Kubernetes(GKE / EKS)の場合は、未認証ユーザとして Docker Hub(Public Repository)からイメージを直接 Pull します。 そのため、ワークロードを一斉に起動して Docker Hub からイメージを Pull したり、imagePullPolicy: Always 等により、短時間の間に繰り返し削除・作成操作が発生したりすると、簡単に Rate Limit に達してしまう可能性があります。

今回は、未認証ユーザとして Pull 動作が行われるマネージド Kubernetes を対象として Docker Hub の Rate Limit 対策を考えます。

※ CI/CD でも Rate Limit の問題がクローズアップされますが、近年では Hosted runner のキャッシュ機能が充実したことで改善傾向にあるため、一旦は触れません。

GAR:Remote Repository

GAR の Remote Repository モード を使用すると、Docker Hub 等のオリジンレジストリからアーティファクト(成果物)をダウンロードして複製することができます。 GAR Remote Repository では、Docker Hub の他にも Maven Central、PyPI(Python Package Index)、Debian、CentOS 等の外部リソースをサポートしています。

コンテナイメージをリクエストした場合は、対象イメージをダウンロードして GAR で管理することで、Docker Hub のプロキシリポジトリとして機能させることができます。

これにより、Docker Hub に対する Pull リクエストの回数を抑えるとともに、イメージキャッシュを使用することでレイテンシの短縮や、外部の公開リポジトリが障害等の影響でオフラインになった場合も継続して利用することが可能になります。

gar-remote-repostiroy.png

GAR Remote Repository の流れ

まず、GKE のイメージ Pull の指定先を Remote Repository が設定された GAR に向けます。 これにより、Docker Hub へのリクエストを Remote Repository でプロキシできるようになります。

GAR Remote Repository に対象イメージが既に存在する場合はそれを使用して、無ければ Docker Hub オリジンからイメージをダウンロードします。 この時、Secret Manager 等で管理されている PAT を GAR の サービスエージェント が取得し、認証済みユーザとして Docker Hub にリクエストを送信します。

これにより、Rate Limit が 10 回から 100 回に緩和され、Business / Team / Pro Tier を利用している場合は、Pull 回数が無制限になります。

Remote Repository の Cleanup Policy

基本的に、一度 GAR にダウンロードされたコンテナイメージは永続化されますが、Cleanup Policy を使用すると不要になったバージョンを自動的に削除することができます。

Cleanup Policy は JSON 形式で定義され、一つのリポジトリに最大 10 個まで 設定することができます。

  • 削除ポリシ
{
  "name": "DELETE_POLICY_NAME",
  "action": { "type": "Delete" },
  "condition": {
    "tagState": "TAG_STATUS",
    "tagPrefixes": ["TAG_PREFIXES"],
    "versionNamePrefixes": ["VERSION_PREFIXES"],
    "packageNamePrefixes": ["PACKAGE_PREFIXES"],
    "olderThan": "OLDER_THAN_DURATION",
    "newerThan": "NEWER_THAN_DURATION"
  }
}
  • 条件付き保持ポリシ
{
  "name": "KEEP_POLICY_NAME",
  "action": { "type": "Keep" },
  "condition": {
    "tagState": "TAG_STATUS",
    "tagPrefixes": ["TAG_PREFIXES"],
    "versionNamePrefixes": ["VERSION_PREFIXES"],
    "packageNamePrefixes": ["PACKAGE_PREFIXES"],
    "olderThan": "OLDER_THAN_DURATION",
    "newerThan": "NEWER_THAN_DURATION"
  }
}

Remote Repository の制約

イメージパスの書き方

GAR Remote Repository を利用するためには、マニフェストのイメージパスの向き先を、以下のような形式で統一する必要があります。

### Public イメージの場合
image: [REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/{ImageName}:{ImageTag}

### ex. nginx:latest
image: [REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/nginx:latest
### アップストリームイメージの場合
image: [REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/{UpstreamRepositoryName}/{ImageName}:{ImageTag}

### ex. cloudflare/cloudflared:latest
image: [REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/cloudflare/cloudflared:latest

PAT の発行・管理

Remote Repository から Docker Hub に Pull リクエストを送信する際に PAT で認証します。 予め PAT を発行し、Secret Manager 等で管理 します。

また、GAR のサービスエージェントが Secret Manager の PAT にアクセスできるように roles/secretmanager.secretAccessor を付与します。

クロスリージョン

リージョンを跨いで Remote Repository を利用することはできません。(マルチリージョン利用は可能)

remote-repository-multi-region.png

複数のリージョンで GKE を運用している場合は、必要に応じてリージョン毎に Remote Repository を構築します。

GAR Remote Repository を使う

1. Docker Hub PAT を Secret Manager に登録

はじめに、事前に発行した Docker Hub PAT を Secret Manager に登録しておきます。 スコープは Read-only で十分です。

remote-repository-config-1.png

Secret Manager に PAT を登録しておきます。

remote-repository-config-1-2.png
Terraform
resource "google_secret_manager_secret" "docker_hub_proxy_secret" {
  secret_id = "${local.name}-pat"
  replication {
    auto {}
  }
}

data "google_secret_manager_secret_version" "docker_hub_proxy_secret_version" {
  secret  = google_secret_manager_secret.docker_hub_proxy_secret.id
  version = "latest"
}

2. GAR Remote Repository を構築

Remote Repository モードで GAR リポジトリを構築します。

認証モードは『Personal Access Token』とし、前述の手順で作成した Secret Manager のキーを指定します。

remote-repository-config-2.png

リポジトリを構築した時点では何のイメージも格納されていません。

remote-repository-config-2-2.png
Terraform
resource "google_artifact_registry_repository" "docker_hub_tky_proxy" {
  location      = "asia-northeast1" ## 東京リージョンに Remote Repository を構築
  repository_id = local.name
  description   = "Remote repository for Docker Hub"
  format        = "DOCKER"
  mode          = "REMOTE_REPOSITORY"

  remote_repository_config {
    description = "docker hub with custom credentials"

    disable_upstream_validation = false

    docker_repository {
      public_repository = "DOCKER_HUB"
    }

    upstream_credentials {
      username_password_credentials {
        username                = local.docker_username
        password_secret_version = data.google_secret_manager_secret_version.docker_hub_proxy_secret_version.name
      }
    }
  }

  vulnerability_scanning_config {
    enablement_config = "INHERITED"
  }
}

3. IAM ロールの付与

Remote Repository に対象イメージが存在しない場合、GAR のサービスエージェント(service-[GCP_PROJECT_NUMBER]@gcp-sa-artifactregistry.iam.gserviceaccount.com)は、PAT の認証トークンを使用して Docker Hub オリジンに Pull リクエストを送信します。

このため、サービスエージェントに Secret Manager へのアクセス権限として roles/secretmanager.secretAccessor を付与する必要があります。

remote-repository-config-2-3.png
Terraform
data "google_project" "project" {}

resource "google_secret_manager_secret_iam_member" "docker_hub_proxy_secret_access" {
  secret_id = google_secret_manager_secret.docker_hub_proxy_secret.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com"
}

4. Remote Repository の動作検証

GKE で Docker Hub からイメージを Pull してみます。

# 省略
spec:
  containers:
    - name: nginx
      image: [REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/nginx:latest # Remote Repository から Pull
      imagePullPolicy: Always

Pod が Running 状態になったことを確認します。

$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-6769c84686-gxzzf   1/1     Running   0          1m

$ kubectl get pod nginx-deployment-6769c84686-gxzzf -o jsonpath="{.spec.containers[*].image}"
[REGION]-docker.pkg.dev/[GCP_PROJECT_ID]/[GAR_REMOTE_REPOSITORY_NAME]/nginx:latest

オリジンから Pull されると、Remote Repository に対象バージョンのイメージが追加されます。

remote-repository-config-3.png

sha256:5ed8fcc6... というダイジェストを持った nginx イメージが Remote Repository にダウンロードされていることが分かります。

remote-repository-config-4.png

ログから、GKE のサービスアカウントが Remote Repository に Pull リクエストを送信していることが確認されます。

また、callerIp からリクエストの送信元がクラスタのパブリック IP アドレスになっていることも分かります。

remote-repository-config-5.png

同じタイミングで、GSA のサービスエージェントは Secret Manager にアクセスして PAT(docker-hub-proxy-pat)を取得していることが分かります。

remote-repository-config-6.png

ECR:Pull Through Cache

Pull Through Cache は Amazon ECR で使用できる機能で、Public Repository からのイメージを ECR にキャッシュして再利用するための機能を提供します。

ecr-pull-through-cache.png

Pull Through Cache の流れ

まず、Pull Through Cache を利用するための IAM ポリシを EKS ノードにアタッチします。

EKS のワークロードが Docker Hub の Public Repository(docker.io/*)にアクセスする際に、ECR を経由するように構成します。 ECR は、Pull Through Cache Rule に基づき、Private Repository 内にイメージキャッシュが存在するかを確認して、存在する場合はそれを利用、無ければ Docker Hub オリジンから Pull します。

Docker Hub から Pull する際は、PAT で認証します。 これにより、Rate Limit が 10 回から 100 回に緩和され、Business / Team / Pro Tier を利用している場合は、Pull 回数が無制限になります。

以降、同一のイメージを使用する場合は、ECR のキャッシュを使って Pull します。

キャッシュの更新

一度 Private Repository 上にキャッシュされたイメージは、Private Repository に対する Pull リクエストをトリガにキャッシュされたイメージが最新バージョンであるかの検証を行い、オリジンのイメージが更新されていた場合は自動的に Private Repository のイメージに反映・更新します。

キャッシュの更新は 24 時間で最大 1 回まで 実行されます。

GAR Remote Repository との違い

GAR Remote Repository との違いは、Pull Through Cache の場合は、あくまでもイメージキャッシュ用のルール(Pull Through Cache Rule)を定義・管理する点です。

GAR Remote Repository の場合は、一度 Pull したイメージは Cleanup Policy を設定しない限り永続的に保存されますが、ECR の場合は、使用されないイメージキャッシュは 24 時間毎に自動的にエビクションされます。

Pull Through Cache の制約

イメージパスの書き方

Pull Through Cache を利用するためには、マニフェストのイメージパスの向き先を、以下のような形式で統一する必要があります。

Public イメージの場合は、library/* プレフィックスを付けます。

### Public イメージの場合
image: [AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/[CACHE_REPOSITORY_PREFIX]/library/{ImageName}:{ImageTag}

### ex. nginx:latest
image: [AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/[CACHE_REPOSITORY_PREFIX]/library/nginx:latest

アップストリームイメージの場合は、アップストリームのリポジトリ名を指定します。

### アップストリームイメージの場合
image: [AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/[CACHE_REPOSITORY_PREFIX]/{UpstreamRepositoryName}/{ImageName}:{ImageTag}

### ex. cloudflare/cloudflared:latest
image: [AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/[CACHE_REPOSITORY_PREFIX]/cloudflare/cloudflared:latest

PAT の発行・管理

Pull Through Cache Rule は PAT で認証して Docker Hub からイメージを Pull します。 予め PAT を発行し、Secrets Manager 等で管理 します。

クロスリージョン

Pull Through Cache Rule はリージョン毎に適用されます。 従って、複数のリージョンで EKS を運用している場合は、リージョン毎に Pull Through Cache Rule を設定する必要があります。

また、次のリージョンでは Pull Through Cache Rule の作成がサポートされていません。

北京リージョン(cn-north-1)
寧夏リージョン(cn-northwest-1)
米国東部リージョン(us-gov-east-1)
米国西部リージョン(us-gov-west-1)

Lambda 未対応

Pull Through Cache は Lambda に対応していません。

Lambda はそれ自体にキャッシュ機構があり、他でキャッシュハンドリングすることが推奨されないためだと思われます。

Pull Through Cache を使う

1. Docker Hub PAT を Secrets Manager に登録

はじめに、事前に発行した Docker Hub PAT を Secrets Manager に登録しておきます。 スコープは Read-only で十分です。

ptc-config-1.png

Secrets Manager に登録するシークレットキーにはプレフィックスとして ecr-pullthroughcache/* を付与します。

ptc-config-2.png
Terraform
resource "aws_secretsmanager_secret" "docker_hub_proxy_secret" {
  name        = "ecr-pullthroughcache/docker-hub-proxy-pat"
  description = "Docker Hub credentials for ECR Pull Through Cache"
}

resource "aws_secretsmanager_secret_version" "docker_hub_proxy_secret_version" {
  secret_id = aws_secretsmanager_secret.docker_hub_proxy_secret.id

  secret_string = jsonencode({
    username    = local.docker_username
    accessToken = "" ## コンソールで入力する
  })
}

2. Pull Through Cache Rule を追加

ECR に Pull Through Cache Rule を追加します。

Amazon ECR > Private registry > Pull through cache

Docker Hub の場合は、Upstream Registry URL に registry-1.docker.io を指定します。

ptc-config-3.png
Terraform
resource "aws_ecr_pull_through_cache_rule" "docker_hub_proxy" {
  ecr_repository_prefix = local.name
  upstream_registry_url = "registry-1.docker.io"
  credential_arn        = aws_secretsmanager_secret.docker_hub_proxy_secret.arn
}

3. IAM Policy の追加

EKS ノードの IAM ロールに以下のポリシを追加します。

参考:IAM permissions required to sync an upstream registry with an Amazon ECR private registry

  • ecr:CreatePullThroughCacheRule:Pull Through Cache を新規に作成する
  • ecr:BatchImportUpstreamImage:アップストリームレジストリからイメージを取得してプライベートレジストリにインポートする
  • ecr:CreateRepository:ECR Private Repository を作成する
ptc-config-4.png
Terraform
data "aws_region" "current" {}

data "aws_caller_identity" "current" {}

### Inline Policy として追加
resource "aws_iam_policy" "ecr-ptc-operations" {
  name        = "pull-through-cache-policy"
  description = "Allow ECR Pull Through Cache operations"
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "ecr:CreatePullThroughCacheRule",
          "ecr:BatchImportUpstreamImage",
          "ecr:CreateRepository"
        ],
        Resource = "arn:aws:ecr:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:repository/*"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "eks-node-ptc-policy" {
  policy_arn = aws_iam_policy.ecr-ptc-operations.arn
  role       = aws_iam_role.eks-node-role.name ## EKS ノードに付与している IAM ロールを指定
}

4. Pull Through Cach の動作検証

Pull Through Cache Rule を追加しているだけなので、最初の時点では ECR Private Repository は作成されていません。

前述の IAM ポリシが付与されている EKS ノードでイメージ Pull を実行してみます。

# 省略
spec:
  containers:
    - name: nginx
      image: [AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/docker-hub-proxy/library/nginx:latest # Pull Through Cache を介してイメージを Pull
      imagePullPolicy: Always

Pod が Running 状態になったことを確認します。

$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-567bf6666d-8lwf4   1/1     Running   0          12m

$ kubectl get pod nginx-deployment-567bf6666d-8lwf4 -o jsonpath="{.spec.containers[*].image}"
[AWS_ACCOUNT_ID].dkr.ecr.[REGION].amazonaws.com/docker-hub-proxy/library/nginx:latest

※ ImagePullBackOff や ErrImagePull が発生している場合は Pull Through Cache が適用できていません。 認証エラーや IAM ポリシが適用できていない可能性があります。

Pull Through Cache Rule によってオリジンから Pull されると、初めて ECR Private Repository が作成され、イメージタグでキャッシュ管理されます。

ptc-config-5.png

Pull Through Cache のログは CloudTrail の Event history 等から確認することができます。

ptc-config-6.pngptc-config-7.png
### Pull Through Cache が有効(active)であるかどうかの確認
$ aws ecr describe-pull-through-cache-rules --region ap-northeast-1
{
    "pullThroughCacheRules": [
        {
            "ecrRepositoryPrefix": "docker-hub-proxy",
            "upstreamRegistryUrl": "registry-1.docker.io",
            "createdAt": "********",
            "registryId": "[AWS_ACCOUNT_ID]",
            "credentialArn": "arn:aws:secretsmanager:ap-northeast-1:[AWS_ACCOUNT_ID]:secret:ecr-pullthroughcache/docker-hub-pat-*****",
            "upstreamRegistry": "docker-hub",
            "updatedAt": "********"
        }
    ]
}

### Pull Through Cache が有効になっているイメージを確認
$ aws ecr describe-repositories | grep docker-hub-proxy
            "repositoryArn": "arn:aws:ecr:ap-northeast-1:[AWS_ACCOUNT_ID]:repository/docker-hub-proxy/library/nginx",
            "repositoryName": "docker-hub-proxy/library/nginx",
            "repositoryUri": "[AWS_ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com/docker-hub-proxy/library/nginx",

Google Cloud 透過プロキシ機構

transparent-image-registry-proxy.png

GAR Remote Repository や ECR Pull Through Cache を活用した Docker Hub の Rate Limit 対策を紹介しましたが、もともと Google Cloud では Public Repository に課せられる Rate Limit の影響を緩和するための仕組みとして、透過プロキシ(Transparent Image Registry Proxy) が提供されています。

GKE、Cloud Build、Cloud Run 等のマネージドサービスは、頻繁に利用される Docker Hub のイメージを、Rate Limit の無いレジストリミラー(mirror.gcr.io)にキャッシュします。 ワークロードが Pull する際は、キャッシュが存在すればそれを使用して、無ければオリジンから Pull します。

例えば、GKE の場合はインスタンステンプレートの configure-sh で、mirror.gcr.io をプロキシするよう /etc/default/docker が設定されています。

instance-template-configure-sh.png
# Do not move to the daemon.json file for backward compatibility.
# Command line and config file options cannot be both defined and custoemr customization may break.
# insecure-registry setting was inherited from the past, see b/203231428. Keeping for backward compatibility.
echo "DOCKER_OPTS=\"--registry-mirror=https://mirror.gcr.io --insecure-registry 10.0.0.0/8\"" > /etc/default/docker

echo "Docker command line and configuration are updated. Restart docker to pick it up"
systemctl restart docker
$ docker system info
Client:
 Version:    24.0.9
 Context:    default

(省略)

 Registry Mirrors:
  https://mirror.gcr.io/

所謂、ECR Pull Through Cache のような仕組みをユーザ側で用意することなく、透過的に利用できるようにしてくれているわけです。

ただし、こちら にも記載されている通り、キャッシュできるイメージは限定的なので、前で紹介したような GAR Remote Repository 等を別途準備しておくことが推奨されています。

Artifact Registry caches frequently-accessed public Docker Hub images on mirror.gcr.io.

キャッシュ対象のイメージ情報は公開されていませんが、以下のコマンドで確認することができます。

$ docker pull mirror.gcr.io/[IMAGE_NAME]

クラスタ内で使用している(キャッシュが効いていない)イメージのリストは以下のコマンドで確認することができます。

### クラスタ内の GCR / GAR 以外から Pull したイメージを確認
$ kubectl get pods --all-namespaces -o jsonpath="{..image}" \
  | tr -s '[[:space:]]' '\n' \
  | grep -vE '(^|\s)([a-zA-Z0-9.-]+-docker\.pkg\.dev|[a-zA-Z0-9.-]*\.gcr\.io)' \
  | sort | uniq -c

   2 cloudflare/cloudflared:latest
   2 docker.io/cloudflare/cloudflared:latest
   2 docker.io/enix/x509-certificate-exporter:3.6.0
   5 docker.io/envoyproxy/envoy:v1.29.3
   1 docker.io/excalidraw/excalidraw:latest
   4 docker.io/google/cloud-sdk:491.0.0-alpine
   2 docker.io/grafana/agent:v0.36.1
   2 docker.io/grafana/alloy:v1.1.1
   ...
   ...

また、現在使用しているイメージがキャッシュに保存されているかを確認する場合は以下のコマンドを使用します。(キャッシュは頻繁に変更されます)

  • Ubuntu イメージの例
### Ubuntu イメージがキャッシュ内にあるかどうかを確認
$ gcloud container images list --repository=mirror.gcr.io/library | grep ubuntu

mirror.gcr.io/library/ubuntu
mirror.gcr.io/library/ubuntu-debootstrap
mirror.gcr.io/library/ubuntu-upstart

### 現在キャッシュ内にある、タグ付きバージョンの Ubuntu をすべて表示
$ gcloud container images list-tags mirror.gcr.io/library/ubuntu

DIGEST        TAGS                               TIMESTAMP
3d1556a8a18c                                     2024-09-12T01:25:18
77d57fd89366                                     2024-08-28T00:55:04
075680e98339                                     2024-08-13T18:27:25

Cloud Run においても、例えば nginx:latest を指定する際に、キャッシュヒットすると mirror.gcr.io のイメージが使用されることが分かります。

cloud-run-image-cache.png

透過プロキシは、ユーザ側で Pull レジストリを変更しなくても、対象イメージであれば mirror.gcr.io でプロキシしてくれます。

まとめ

今回のブログでは、Google Cloud Artifact Registry の Remote Repository と Amazon ECR の Pull Through Cache を活用した Docker Hub の Rate Limit 対策について紹介しました。

マネージド Kubernetes の場合、基本的に Docker Hub へのイメージ Pull は、未認証ユーザとしてリクエストされます。

また、Rate Limit はクラスタ単位で適用されるため、単一のワークロードが制限に引っかかった場合、他のワークロードもイメージ Pull できなくなる危険性があります。

GCP や AWS では、コンテナレジストリの機能の一つとして、イメージキャッシュの機能が用意されており、Docker Hub へのリクエストをプロキシできます。 仕組み自体は非常に単純なので、Rate Limit の懸念がある場合は、Remote Repository や Pull Through Cache を整備しておくと良いでしょう。

参考・引用