Docker 基礎 #5 レジストリ — Docker Hub, GHCR, push/pull
#3 まで作ったイメージは 自分のマシンの中だけ で生きていました。この記事ではそのイメージを 別のマシン — 同僚、CI サーバー、運用サーバー — でも使えるようにする仕組みを扱います。それが レジストリ (registry) です。
Docker 基礎 シリーズでこの記事の位置:
- #1 コンテナとは
- #2 Dockerfile を初めて書く
- #3 イメージとコンテナ
- #4 ボリュームとネットワーク
- #5 レジストリ — Docker Hub, GHCR, push/pull ← この記事
- #6
.dockerignoreとビルドコンテキスト
レジストリとは何か #
GitHub がコードのリモートリポジトリなら、レジストリはイメージのリモートリポジトリ です。例えのまま。
| コード | イメージ | |
|---|---|---|
| 作る | git commit | docker build |
| 上げる | git push | docker push |
| 取る | git pull | docker pull |
| ホスト | GitHub、GitLab | Docker Hub、GHCR、ECR |
レジストリの種類は多いですが、最初に出会うのは普通二箇所です。
- Docker Hub — Docker 公式レジストリ。
python:3.14、nginx:1.27のような公式イメージがここにあります。無料アカウントでパブリックリポジトリを作れます。 - GitHub Container Registry (GHCR) — GitHub が運営。GitHub リポジトリと権限を共有して、GitHub Actions と相性が良いので最近よく使われます。
これ以外にクラウドプロバイダーのレジストリ (AWS ECR、Google Artifact Registry、Azure Container Registry) がありますが、Docker から見ると全て同じ OCI 規約に従うので ログインコマンドだけ違って push/pull の流れは同じ です。
イメージ名の構造 #
Docker がイメージ名をどう解釈するかを一度解きほぐす価値があります。よく混乱するポイントだからです。
[REGISTRY/]NAMESPACE/REPOSITORY[:TAG][@DIGEST]例:
| 名前 | 解きほぐすと |
|---|---|
nginx | docker.io/library/nginx:latest |
python:3.14-slim | docker.io/library/python:3.14-slim |
myuser/myapp:1.0 | docker.io/myuser/myapp:1.0 |
ghcr.io/curtis/myapp:1.0 | GHCR の curtis/myapp 1.0 |
ghcr.io/curtis/myapp@sha256:abc... | タグの代わりにダイジェストで固定 |
Docker はレジストリを省略すると docker.io (= Docker Hub) を仮定します。namespace を省略すると library (公式イメージの namespace)。タグを省略すると latest。だから nginx 一文字が実は docker.io/library/nginx:latest の短縮形です。
latest の落とし穴
#
latest は「最も最近」という意味ではありません。ただ タグを渡さなかったときのデフォルトの名前 に過ぎません。誰かが 1.27 をビルドして latest を一緒に付けないと、latest は古いイメージを指すこともあります。だから 運用では latest に依存せず明示的なバージョンタグ を使うのが定石です。
タグ戦略はこんな風によく組みます。
- セマンティックバージョン:
myapp:1.2.3 - メジャー / マイナーエイリアス:
myapp:1.2、myapp:1 - 環境:
myapp:staging、myapp:prod - Git コミット SHA:
myapp:a1b2c3d - 常に同時に:
myapp:latest
CI が一回のビルドで myapp:1.2.3、myapp:1.2、myapp:1、myapp:latest を同時に刻んでおくと使う側が楽です。
docker tag — 別名を付ける
#
イメージをビルドするとき -t で一度名前を付けましたが、同じイメージにさらに別名を付けることもできます。
docker tag hello-docker myuser/hello-docker:1.0
docker tag hello-docker myuser/hello-docker:latestdocker tag はデータコピーをせず ポインタだけ追加 します。だから即終わります。docker images で見ると同じ IMAGE ID に名前が複数付いているはずです。
レジストリにアップロードするには通常 名前にレジストリホスト / ユーザー名が入った形にタグを付け直します。
docker tag hello-docker ghcr.io/curtis/hello-docker:1.0こうすることで Docker が「どこに送るべきか」を知ります。ただの hello-docker のような短い名前ではプッシュできません。
Docker Hub にプッシュ #
Docker Hub にアカウントを作って (hub.docker.com)、ターミナルでログインします。
docker login
# Username: myuser
# Password: ******
# Login SucceededPersonal Access Token を推奨: Docker Hub はパスワードの代わりに PAT (個人アクセストークン) の使用を推奨します。アカウント設定 → Security → New Access Token で作って、パスワード欄に PAT を入れてください。CI でも PAT が定石です。
ログイン情報は ~/.docker/config.json に保存されます。macOS/Windows の Docker Desktop は OS のキーチェーンに保存してより安全に保管します。
ではプッシュ:
docker tag hello-docker myuser/hello-docker:1.0
docker push myuser/hello-docker:1.0
# The push refers to repository [docker.io/myuser/hello-docker]
# 5e7d4abc...: Pushed
# 1.0: digest: sha256:abcdef... size: 1234プッシュは レイヤー単位 で行われます。すでにレジストリにあるレイヤー (例: python:3.14-slim のベースレイヤー) はもう一度上がらず、変わったレイヤーだけ上がります。二度目のプッシュからとても速い理由がこれです。
別のマシンで取得:
docker pull myuser/hello-docker:1.0
docker run --rm myuser/hello-docker:1.0GitHub Container Registry (GHCR) にプッシュ #
GHCR は GitHub の権限モデルをそのまま使い、同じリポジトリの PAT で認証します。
1. PAT を作る #
GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic) で新しいトークンを作って、権限に write:packages をチェックします。(private イメージ pull までするなら read:packages も。)
export CR_PAT=ghp_xxxxxxxxxxxxxxxxxx2. ログイン #
echo $CR_PAT | docker login ghcr.io -u curtis --password-stdin
# Login Succeeded--password-stdin でトークンを stdin から読めばシェル履歴に残らず安全です。CI でも同じパターンを使います。
3. タグ + プッシュ #
docker tag hello-docker ghcr.io/curtis/hello-docker:1.0
docker push ghcr.io/curtis/hello-docker:1.0プッシュ直後は GHCR のパッケージはデフォルトで private です。公開に変えるには GitHub のパッケージページで「Change package visibility」→ public。
GitHub Actions で #
GitHub Actions の中では PAT の代わりに自動発行される GITHUB_TOKEN をそのまま使います。
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository_owner }}/myapp:${{ github.sha }}手動で PAT を管理する必要がなく綺麗です。(このパターンは Docker 実践シリーズで深く扱います。)
ダイジェスト — 最も正確な固定 #
タグは人が読みやすいですが 不変 (immutable) ではありません。 誰かが myapp:1.2.3 を同じタグで再プッシュできます。だから正確な再現が大事な場面では ダイジェスト (SHA-256) を使います。
docker pull python@sha256:abc123def456...プッシュのとき出力に digest: sha256:... が出ますが、それがそのイメージの永久識別子です。同じダイジェストは永遠に同じイメージ を指します。運用環境、セキュリティスキャン、コンプライアンスが大事な場面ではダイジェストで固定するのが定石です。
docker inspect でダイジェスト確認:
docker inspect --format '{{index .RepoDigests 0}}' myuser/hello-docker:1.0
# myuser/hello-docker@sha256:abcdef...docker pull — 取得
#
docker pull nginx:1.27
docker pull ghcr.io/curtis/hello-docker:1.0よく使うオプション:
docker pull --platform linux/amd64 myimage # 特定アーキテクチャだけ
docker pull -a myuser/myapp # 全タグ--platform は Apple Silicon マシンで amd64 イメージを強制で取るときによく使います。運用サーバが amd64 なのにローカル ARM でビルド / テストしようとする状況などで。(マルチアーキテクチャビルドは Docker 上級のテーマです。)
プライベートレジストリ — 一段落 #
会社の内部に自前のレジストリを置くことが多いです。Docker はイメージ自体をコンテナ化した registry イメージを提供します。
docker run -d --restart unless-stopped \
-p 5000:5000 --name registry \
-v registry-data:/var/lib/registry \
registry:2
# プッシュ
docker tag myapp localhost:5000/myapp:1.0
docker push localhost:5000/myapp:1.0研究室 / 社内ネットワークでセルフホストする流れですが、運用では普通 GHCR / ECR / Harbor のようなマネージド / フル機能レジストリを使います。とりあえずこういうことができるという程度で覚えておいてください。
よく出会う落とし穴 #
docker pushでdenied: requested access to the resource is denied— ほぼ常にログイン / 権限 / タグ名のどれか。タグがmyuser/...ではなくsomeoneelse/...になっているのがよくあります。- プッシュはできたのに別のマシンで pull できない — パッケージが private なのにそのマシンでログインしていない可能性。GHCR は private がデフォルトなのでよく出会います。
- macOS でビルドしたイメージが運用サーバ (amd64) で立ち上がらない — Apple Silicon の arm64 イメージをプッシュしたケース。
--platform linux/amd64でビルドするか、Buildx でマルチアーキテクチャビルド。 latestが更新されていない感じがする — そのマシンの Docker がキャッシュされたlatestを指しています。docker pull myimage:latestをもう一度叩けば OK。
まとめ #
この記事で掴んだ絵:
- レジストリはイメージのリモートリポジトリ。GitHub ↔ Docker Hub / GHCR の例えそのまま
- イメージ名 =
[REGISTRY/]NAMESPACE/REPO[:TAG][@DIGEST]。省略時はdocker.io/library/...:latestがデフォルト docker tagは別名追加、docker pushはレイヤー単位アップロード- Docker Hub は Personal Access Token、GHCR は GitHub PAT (または Actions の
GITHUB_TOKEN) でログイン - 運用では
latest依存を避けてセマンティックバージョンタグ + Git SHA を一緒に刻む - 正確な固定が必要なら ダイジェスト (@sha256:…) で参照
次の記事 (#6 .dockerignore とビルドコンテキスト) ではイメージが膨れたりビルドが遅くなる最も多い原因 — ビルドコンテキスト の正体と、それを扱う .dockerignore の作成パターン、そして #2 で一度触れた レイヤーキャッシュ を本格的に組みます。