GitHub Actions 講座 #6 Docker ビルドと GHCR プッシュ — タグ戦略とビルドキャッシュ

読了 4分

今回から 3 回は、組み立ての実践です。最初のパイプラインで作るのは、CI の代表的な成果物であるコンテナイメージです。コードが main にマージされたらイメージをビルドしてレジストリにプッシュする流れを作ります。レジストリには GitHub 内蔵の GHCR (GitHub Container Registry) を使います。GHCR とは何か、Docker Hub とどう違うかは GHCR の紹介記事にまとめてあり、Dockerfile の書き方自体は Docker 基礎講座を前提にします。ここでは GitHub Actions 側の組み立てに集中します。

完成形のワークフローから #

完成形を先に見てから、部品を順に解説していきます。

.github/workflows/image.yml
# .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
PRpr-123

つまりワークフロー 1 つが「開発の最新は main タグ、リリースはバージョンタグ」というポリシーを自動で実行してくれます。タグの規則を変えたい場合は metadata-action の tags 入力で規則を宣言する方式なので、タグポリシーが YAML にコードとして残ります。運用で大事な習慣は、デプロイは latest ではなく固定タグ (バージョンまたは SHA) で行うことです。latest は昨日と今日で別のイメージかもしれず、どのコードが動いているのか答えられなくなるからです。

ビルドキャッシュ — gha タイプのペア #

Docker のビルドはレイヤーキャッシュがあってこそ速いのですが、ランナーは毎回まっさらなマシンなので、そのままではキャッシュがありません (#4 の問題が Docker で再現される形です)。build-push-action の 2 行がその答えです。

.github/workflows/image.yml
          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 連携を完成させ、ビルドしたものを実際にデプロイします。

X