GitHub Actions 講座 #6 Docker ビルドと GHCR プッシュ — タグ戦略とビルドキャッシュ
今回から 3 回は、組み立ての実践です。最初のパイプラインで作るのは、CI の代表的な成果物であるコンテナイメージです。コードが main にマージされたらイメージをビルドしてレジストリにプッシュする流れを作ります。レジストリには GitHub 内蔵の GHCR (GitHub Container Registry) を使います。GHCR とは何か、Docker Hub とどう違うかは GHCR の紹介記事にまとめてあり、Dockerfile の書き方自体は Docker 基礎講座を前提にします。ここでは GitHub Actions 側の組み立てに集中します。
完成形のワークフローから #
完成形を先に見てから、部品を順に解説していきます。
# .github/workflows/image.yml
name: build image
on:
push:
branches: [main]
tags: ["v*"]
permissions:
contents: read
packages: write # GHCR プッシュに必要
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max#2 と #3 で学んだ文法が、すべて実戦投入されています。ステップの出力 (steps.meta.outputs)、GITHUB_TOKEN、そして初登場の permissions ブロックです。
ログイン — 追加のシークレットは要りません #
GHCR の実務的な利点は、ログインステップにそのまま現れています。Docker Hub ならアカウントのトークンを作って secrets に登録する必要がありますが、GHCR は #3 で見た自動発行トークン GITHUB_TOKEN だけでログインが完了します。管理するシークレットが 1 つも増えません。その代わり、ワークフローに permissions: packages: write を宣言してトークンにパッケージ書き込み権限を与える必要があります。この宣言の形自体が、#9 で扱う最小権限の原則の実例です。必要な権限を、必要なワークフローにだけ明示する形だからです。
タグ戦略 — metadata-action に任せる #
イメージのタグを何にするかは、手で決めるとすぐ崩れる領域です。docker/metadata-action は、実行を起動したイベントに応じてタグを自動決定します。デフォルトの動作だけで次のようになります。
| イベント | 作られるタグ |
|---|---|
| main へのプッシュ | main (+ コミット SHA タグのオプション) |
| v1.4.0 タグのプッシュ | 1.4.0、1.4、latest |
| PR | pr-123 |
つまりワークフロー 1 つが「開発の最新は main タグ、リリースはバージョンタグ」というポリシーを自動で実行してくれます。タグの規則を変えたい場合は metadata-action の tags 入力で規則を宣言する方式なので、タグポリシーが YAML にコードとして残ります。運用で大事な習慣は、デプロイは latest ではなく固定タグ (バージョンまたは SHA) で行うことです。latest は昨日と今日で別のイメージかもしれず、どのコードが動いているのか答えられなくなるからです。
ビルドキャッシュ — gha タイプのペア #
Docker のビルドはレイヤーキャッシュがあってこそ速いのですが、ランナーは毎回まっさらなマシンなので、そのままではキャッシュがありません (#4 の問題が Docker で再現される形です)。build-push-action の 2 行がその答えです。
cache-from: type=gha
cache-to: type=gha,mode=maxビルドのレイヤーキャッシュを GitHub Actions のキャッシュストレージに保存し、次の実行で取り出して使う設定です。mode=max は中間レイヤーまですべて保存してヒット率を高めます。Dockerfile がキャッシュに優しく書かれているほど (依存関係インストールのレイヤーとコードコピーのレイヤーの分離) 効果が大きくなりますが、その書き方は Docker 中級講座のテーマなので、ここではつなげるだけにしておきます。マルチアーキテクチャビルド (amd64 と arm64 の同時ビルド) が必要なら、docker/setup-qemu-action と platforms 入力を追加する拡張の道筋も同じアクションの上にあります。
ビルドしたイメージはどこへ行くのか #
プッシュされたイメージはリポジトリの Packages タブに現れ、アドレスは ghcr.io/<owner>/<repo> です。このイメージを実際のインフラにデプロイするのが次回のテーマで、Terraform 実践講座 #8 で「イメージは用意されている前提」として後回しにしていた、その手前の段階が今日で埋まったことになります。
まとめ #
今回扱った内容です。
- GHCR のログインは GITHUB_TOKEN だけで完了し、permissions の packages write 宣言がペアになります
- タグは手で決めず metadata-action に任せます。イベントごとのタグポリシーが YAML のコードとして残ります
- デプロイは latest ではなく、バージョン・SHA の固定タグで行います
- ビルドキャッシュは cache-from/to の gha タイプのペアで、効果は Dockerfile のキャッシュ親和性に比例します
- 成果物は ghcr.io/owner/repo にアップロードされ、これをインフラにデプロイするのが次回です
次回(#7 AWS デプロイと OIDC)では、アクセスキーなしでワークフローが AWS の権限を得る OIDC 連携を完成させ、ビルドしたものを実際にデプロイします。