こういった問題に直面したことはないだろうか。
Docker Hubは無料プランだと6時間ごとに100回のpull制限がある。開発チームが増えてくると、CIが同時走行するたびに
toomanyrequestsエラーが返ってきてビルドが止まる。この記事では、Dockerが公式に提供する
registryコンテナを使って、社内LAN上にプライベートDockerレジストリを構築する方法を解説する。docker composeによる基本構成から、TLS証明書・htpasswd認証によるセキュア化、さらにDocker HubのPull through Cacheとして使う設定まで、RHEL 9.4 / Ubuntu 24.04 LTSで動作確認した実行例付きで紹介する。この記事のポイント
・docker run registry:2 で5分以内にプライベートレジストリを起動できる
・push/pullはイメージに「ホスト:ポート/」タグを付けるだけでよい
・TLS + htpasswd認証でセキュアな社内専用レジストリを構築できる
・pull through cache設定でDocker Hub rate limitを実質回避できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
プライベートDockerレジストリとは(Docker Hubとの違い・用途)
Dockerのイメージ配信は、デフォルトでDocker Hub(hub.docker.com)に接続する。便利ではあるが、実務でいくつかの問題が顕在化する。Docker Hubの主な制約:
・無料プランは6時間ごとにpull数が100回に制限される(認証済みユーザーは200回)
・プライベートリポジトリ数に上限がある(無料プランは1リポジトリのみ)
・インターネット経由のため、pull速度がネットワーク環境に依存する
・社内のカスタムイメージを外部サービスに置くことによるセキュリティリスク
プライベートDockerレジストリを社内に構築すれば、これらの問題をまとめて解決できる。
プライベートレジストリの主なユースケース:
・CI/CDパイプラインでのベースイメージをキャッシュして、rate limitとビルド時間を削減する
・社内ネットワーク内でカスタムイメージを安全に共有する
・Docker Hub rate limitが原因のビルド失敗を根絶する
Dockerが公式に提供するレジストリイメージは
registry:2(Distribution)と呼ばれる。Go製で軽量・高速であり、本番利用にも耐える。registryコンテナを立ち上げる(docker composeによる基本構成)
まずはHTTPSなしのシンプルな構成で動かしてみる。動作を理解してから認証・TLSを追加するのが、遠回りに見えて結局は近道だ。1. ディレクトリ構成を準備する
作業ディレクトリを作成する。# create working directories for registry mkdir -p ~/registry/{data,certs,auth} cd ~/registry
data/はイメージデータの保存先、certs/はTLS証明書、auth/はhtpasswdファイルを置く場所だ。2. docker-compose.yml を作成する
以下の内容でdocker-compose.ymlを作成する。services: registry: image: registry:2 container_name: registry restart: always ports: - "5000:5000" volumes: - ./data:/var/lib/registry environment: REGISTRY_STORAGE_DELETE_ENABLED: "true"
REGISTRY_STORAGE_DELETE_ENABLED: "true"を設定しておくことで、後からイメージの削除(garbage collect)が行えるようになる。3. コンテナを起動して動作確認する
# start the registry container docker compose up -d # confirm it is running docker compose ps
curlでAPIに応答があれば起動成功だ。$ curl -s http://localhost:5000/v2/ {}
{}が返れば正常に動作している。ポートの開放状態を確認したい場合は、Linux ポート確認の全コマンドを参照してほしい。イメージのpush/pullを試す(タグ付け・ローカル転送の実例)
registryが起動したら、実際にイメージをpush・pullしてみる。1. ローカルイメージにタグを付ける
プライベートレジストリへpushするには、イメージ名に「ホスト名:ポート番号/」のプレフィックスを付けたタグが必要だ。# pull from Docker Hub (needed only on first time) docker pull nginx:latest # tag the image for our private registry docker tag nginx:latest localhost:5000/nginx:latest
2. プライベートレジストリへpushする
$ docker push localhost:5000/nginx:latest The push refers to repository [localhost:5000/nginx] 9e3f92c03290: Pushed d0e26daf1f58: Pushed 2c7e9b31bd18: Pushed d2a37b3d0ba5: Pushed fccbe3c16ded: Pushed latest: digest: sha256:a4b2c3d4e5f6789012abcdef1234567890abcdef1234567890abcdef12345678 size: 1862
3. プライベートレジストリからpullする
ローカルのキャッシュを削除してから、プライベートレジストリからpullしてみる。# remove local cache to verify pull from private registry docker rmi localhost:5000/nginx:latest $ docker pull localhost:5000/nginx:latest latest: Pulling from nginx d2a37b3d0ba5: Pull complete 2c7e9b31bd18: Pull complete fccbe3c16ded: Pull complete 9e3f92c03290: Pull complete Digest: sha256:a4b2c3d4e5f6789012abcdef1234567890abcdef1234567890abcdef12345678 Status: Downloaded newer image for localhost:5000/nginx:latest localhost:5000/nginx:latest
HTTPS/認証を付加してセキュアに運用する(TLS証明書・htpasswd)
ここまでの構成はHTTPで動いているため、社内LAN内での評価目的なら問題ないが、本番運用ではTLS暗号化と認証が必須だ。DockerクライアントはデフォルトでHTTPSを要求する(localhost以外のホストに対して)。1. TLS証明書を準備する
検証サーバーで自己署名証明書を作成する例を示す。本番ではLet's Encryptや社内CAの証明書を使うことを推奨する。なお、TLS証明書の有効性は時刻同期が前提となるため、事前にntpd 時刻同期設定を確認しておくこと。# generate a self-signed certificate valid for 365 days $ openssl req -newkey rsa:4096 -nodes -sha256 -keyout ~/registry/certs/domain.key -x509 -days 365 -out ~/registry/certs/domain.crt -subj "/CN=registry.local" Generating a RSA private key ...............++++ writing RSA key # 192.168.x.x registry.local (実際のIPに置き換えること)
registry.localを解決できるようにする場合は、Linux DNS 設定の基本を参考にしてほしい。Dockerクライアント側では、自己署名証明書を信頼済みCAとして登録する必要がある。
# register the self-signed cert as trusted CA (RHEL/Rocky Linux) sudo mkdir -p /etc/docker/certs.d/registry.local:5000 sudo cp ~/registry/certs/domain.crt /etc/docker/certs.d/registry.local:5000/ca.crt # Ubuntu: update system trust store sudo cp ~/registry/certs/domain.crt /usr/local/share/ca-certificates/registry-local.crt sudo update-ca-certificates
2. htpasswdで認証ファイルを作成する
httpd-tools(RHEL系)またはapache2-utils(Ubuntu系)パッケージに含まれるhtpasswdコマンドで認証ファイルを作成する。# install htpasswd tool (RHEL/Rocky Linux) sudo dnf install -y httpd-tools # install htpasswd tool (Ubuntu) sudo apt-get install -y apache2-utils # create password file with bcrypt hashing (-B is recommended) $ htpasswd -Bc ~/registry/auth/htpasswd admin New password: Re-type new password: Adding password for user admin
3. docker-compose.ymlを更新する
TLSと認証を有効にしたdocker-compose.ymlに更新する。services: registry: image: registry:2 container_name: registry restart: always ports: - "5000:5000" volumes: - ./data:/var/lib/registry - ./certs:/certs - ./auth:/auth environment: REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_REALM: "Registry Realm" REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_STORAGE_DELETE_ENABLED: "true"
4. docker loginで認証テストをする
# restart the registry to apply TLS settings docker compose down && docker compose up -d # test login to the secure registry $ docker login registry.local:5000 Username: admin Password: WARNING! Your password will be stored unencrypted in /home/user/.docker/config.json. Login Succeeded
docker pull registry.local:5000/nginx:latestのように認証済みでpullできるようになる。Docker Hub rate limitの実態と対策(pull through cacheとして使う方法)
Docker Hubのrate limitは、開発チームが増えると深刻な問題になる。CIパイプラインで複数のジョブが並走する環境では、認証なしだと6時間ごとにわずか100pullしか使えない。1. rate limitの現状を確認する
現在の残りpull数は以下のコマンドで確認できる。# check remaining pull quota from Docker Hub TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])") curl -s --head -H "Authorization: Bearer ${TOKEN}" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest | grep -i ratelimit
ratelimit-limit: 100;w=21600 ratelimit-remaining: 47;w=21600 docker-ratelimit-source: 203.0.113.xx
ratelimit-remaining: 47は残り47回であることを示す。このまま放置するとCIパイプライン停止の原因になる。2. pull through cacheの設定
registryをDocker Hubのキャッシュとして動作させる。チームで一度pullしてしまえば、以降は社内レジストリから取得できる。services: registry-cache: image: registry:2 container_name: registry-cache restart: always ports: - "5001:5000" volumes: - ./cache-data:/var/lib/registry environment: REGISTRY_PROXY_REMOTEURL: "https://registry-1.docker.io" # optional: add docker hub credentials to increase rate limit # REGISTRY_PROXY_USERNAME: "your-dockerhub-username" # REGISTRY_PROXY_PASSWORD: "your-dockerhub-token"
/etc/docker/daemon.jsonを以下の内容で作成または編集する(既存の設定を上書きしないよう注意すること)。{ "registry-mirrors": ["http://registry.local:5001"] }
sudo systemctl restart docker # verify the mirror is registered $ docker info | grep -A 5 "Registry Mirrors" Registry Mirrors: http://registry.local:5001/
docker pull nginx:latestのように通常通りpullするだけで、2回目以降はDocker Hubには接続せず社内キャッシュから取得される。実運用Tips(容量管理・garbage collect・バックアップ)
1. ストレージ使用量を確認する
data/ディレクトリの使用量はduコマンドで確認できる。# check storage usage $ du -sh ~/registry/data/ 4.8G /home/admin/registry/data/ # list all repositories via API $ curl -s https://registry.local:5000/v2/_catalog --cacert ~/registry/certs/domain.crt -u admin:yourpassword {"repositories":["nginx","ubuntu","myapp"]}
/v2/リポジトリ名/tags/listエンドポイントで確認できる。2. garbage collectで不要レイヤーを削除する
docker rmiでイメージを削除しても、ディスクのレイヤーファイルはすぐには消えない。garbage-collectコマンドを実行して物理削除する。# dry-run: show what would be deleted without actually deleting $ docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml myapp: marking manifest sha256:1a2b3c4d... nginx: marking manifest sha256:a4b2c3d4... 3 blobs marked, 15 blobs and 5 manifests eligible for deletion blob eligible for deletion: sha256:deadbeef... # stop the registry, run garbage-collect, then restart docker compose stop docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml docker compose start
3. レジストリデータをバックアップする
レジストリのデータはすべてdata/ディレクトリ以下に集約されている。このディレクトリをそのままアーカイブするだけでバックアップが完了する。マウントの管理についてはmount コマンドの使い方も参照してほしい。# stop the registry before backup to ensure data consistency docker compose stop tar czf /backup/registry-data-$(date +%Y%m%d).tar.gz -C ~/registry data/ docker compose start # verify the backup file was created $ ls -lh /backup/registry-data-*.tar.gz -rw-r--r-- 1 admin admin 4.2G Jul 17 03:00 /backup/registry-data-20260717.tar.gz
よくあるエラーと対処法
1. 「http: server gave HTTP response to HTTPS client」エラー
HTTPのレジストリにDockerクライアントがHTTPSで接続しようとした場合に発生する。Error response from daemon: Get "https://registry.local:5000/v2/": http: server gave HTTP response to HTTPS client
方法A: TLSを設定する(推奨)
本記事の「HTTPS/認証を付加してセキュアに運用する」セクションを参照してTLSを設定する。
方法B: insecure-registriesに追加する(開発・検証環境のみ)
# /etc/docker/daemon.json に insecure-registries を追加する { "insecure-registries": ["registry.local:5000"] }
insecure-registriesの使用は推奨しない。2. 「x509: certificate signed by unknown authority」エラー
自己署名証明書がDockerクライアントに信頼されていない場合に発生する。Error response from daemon: Get "https://registry.local:5000/v2/": x509: certificate signed by unknown authority
/etc/docker/certs.d/registry.local:5000/ca.crtに証明書が正しくコピーされているか確認すること。また、TLS証明書の時刻有効性は時刻同期が正しく設定されているかどうかに依存するため、ntpd 時刻同期設定も確認してほしい。3. 「no basic auth credentials」エラー
認証を設定したレジストリに未ログインの状態でアクセスしようとした場合に発生する。Error response from daemon: Head "https://registry.local:5000/v2/nginx/manifests/latest": no basic auth credentials
docker login registry.local:5000を実行してから再試行すること。まとめ
プライベートDockerレジストリの構築・運用に必要な手順を一覧にする。| やりたいこと | コマンド |
|---|---|
| レジストリを起動する | docker compose up -d |
| API動作確認 | curl -s http://localhost:5000/v2/ |
| イメージにタグを付ける | docker tag nginx:latest localhost:5000/nginx:latest |
| プライベートレジストリへpushする | docker push localhost:5000/nginx:latest |
| プライベートレジストリからpullする | docker pull localhost:5000/nginx:latest |
| レジストリへログインする | docker login registry.local:5000 |
| garbage collectを実行する | docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml |
| リポジトリ一覧を取得する | curl -s https://registry.local:5000/v2/_catalog |
プライベートDockerレジストリは、インフラが小規模なうちから導入しておくことを強くすすめる。Docker Hub rate limitはチームが大きくなるほど影響が大きくなる。pull through cache設定を加えれば、既存のDockerワークフローを変えずにrate limit問題を根絶できる。
次に読む記事:
・Linux ポート確認の全コマンド(registryが使うポート5000の開放確認に)
・Linux DNS 設定の基本(registry.localのDNS解決設定に)
Linux無料マニュアルを受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:Dockerコンテナの自動起動設計|restart policyとsystemd連携でサーバー再起動に耐える構成
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます