ハンドルネームの敬称は省略できます

🦀パソコンを叩く日々🐈

GitHub Actions から GitHub Container Registry へ丁寧にコンテナイメージを Push する

こんにちは、 id:rokuokun です。

Docker Hub の利用制限がいよいよ厳しくなってきたので、これを機に全部 GitHub Container Registry にアップロードしちゃいましょう。

せっかくなら GitHub Actions を使っていい感じにやります。

Docker Hub の Rate Limit

docs.docker.com

Starting April 1, 2025, all users with a Pro, Team, or Business subscription will have unlimited Docker Hub pulls with fair use. Unauthenticated users and users with a free Personal account have the following pull limits:

  • Unauthenticated users: 10 pulls/hour
  • Authenticated users with a free account: 100 pulls/hour

認証ユーザーでも1時間あたり 100 回しか pull できないのでそれなりに辛い感じになりました。

ビルド時に pull するのは仕方ないとして、成果物は GitHub Container Registry(GHCR) においておけばこの Rate Limit をある程度回避できます。

Docker Hub の PAT を発行する

ベースイメージの Pull は Docker Hub からされるケースが多いと思うでの、Actions 内でも一度ログインされるようにします。

まず、docker account center の Personal Access Tokens から Personal Access Token (PAT) を作成します。 Permission は特に書き込みはしないので Read Only で良いでしょう。

Docker Hub で Read only な Token を発行する

Repository Settings から Secrets を登録しておきます。

Secrets and variables > Actions > New repository secrets

Workflow を書いていく

実際に動いている様子は以下のリポジトリで見ることができます。 参考までにどうぞ。

github.com

上記リポジトリで動作している Workflow を以下に表示します。

name: image-build-and-push

on:
  push:
    branches:
      - main
  pull_request:
    types:
      - opened
      - synchronize
      - reopened
    paths:
      - ".github/workflows/image-build-and-push.yaml"
      - "Dockerfile"
  workflow_dispatch:

env:
  IMAGE_NAME: ghcr.io/${{ github.repository }}/sample

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      pull-requests: read
      id-token: write
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PAT }}
      - uses: docker/metadata-action@v4
        id: meta
        with:
          # コンテナイメージの名前を指定する。
          # 以下の指定指定方法だと以下のようなイメージが作成される。
          # ghcr.io/username/repository-name/sample:tag
          images: ${{ env.IMAGE_NAME }}
          # デフォルトブランチの時だけ latest タグをつけてそれ以外は日時とコミットハッシュをつける
          # ref: https://github.com/docker/metadata-action?tab=readme-ov-file#customizing
          tags: |
            type=raw,value=latest,enable={{is_default_branch}}
            type=raw,value={{date 'YYYYMMDD-HHmmss' tz='Asia/Tokyo'}}-{{sha}}
          # Open Container Initiative (OCI) の Image Spec に基づくラベル
          # ref:
          # - https://github.com/opencontainers/image-spec/blob/fbb4662eb53b80bd38f7597406cf1211317768f0/annotations.md?plain=1#L18-L26
          labels: |
            org.opencontainers.image.authors=${{ github.repository_owner }}
            org.opencontainers.image.url=https://github.com/${{ github.repository }}
            org.opencontainers.image.documentation=https://github.com/${{ github.repository }}
            org.opencontainers.image.source=https://github.com/${{ github.repository }}
      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          file: Dockerfile
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
          provenance: false
          cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:cache
          cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:cache,mode=max
      - name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

ビルドキャッシュ

今回は丁寧にやるので Build するついでにキャッシュもできるようにしています。

キャッシュは docker/build-push-actioncache-fromcache-to で行っていますが、デフォルトのドライバーでは実行できないため、 docker/setup-buildx-actiondocker-containerドライバを使用可能にしています。

詳細はそれぞれのドキュメントを見てください。

github.com

github.com

docs.docker.com

キャッシュで使用するコンテナイメージは :cache というタグが付けられています。 イメージ詳細からタグ一覧を確認できます。

https://github.com/rokuosan/ghcr-example/pkgs/container/ghcr-example%2Fsample

cache と latest のタグが付けられているイメージ

Image Metadata

コンテナイメージにはラベルがあり、Open Container Initiative がイメージにつけるラベルを決めてくれています。 今回はそれに従う形でラベルを付けてみました。

イメージに対してラベルをつける作業は docker/metadata-actionで行っています。 実際につけられたラベルは GitHub Actions のログから確認できます。

GitHub Actions でイメージにつけられた Labels

${{ github.owner }}で出した自分の名前がマスクされていますが、実際に Pull して中身を見てみるとしっかりとラベル付けられていることが確認できます。

ラベルとつけると一気に丁寧感が出ました。

$ docker pull ghcr.io/rokuosan/ghcr-example/sample:latest
$ docker inspect ghcr.io/rokuosan/ghcr-example/sample:latest | jq ".[0].Config.Labels"
{
  "org.opencontainers.image.authors": "rokuosan",
  "org.opencontainers.image.created": "2025-02-25T12:48:29.182Z",
  "org.opencontainers.image.description": "",
  "org.opencontainers.image.documentation": "https://github.com/rokuosan/ghcr-example",
  "org.opencontainers.image.licenses": "",
  "org.opencontainers.image.revision": "65c41aa9dbb0f351c6d99f668c3f3120a2eb8188",
  "org.opencontainers.image.source": "https://github.com/rokuosan/ghcr-example",
  "org.opencontainers.image.title": "ghcr-example",
  "org.opencontainers.image.url": "https://github.com/rokuosan/ghcr-example",
  "org.opencontainers.image.version": "latest"
}

詳しく知りたい方はこちらからどうぞ。

github.com

その他

書くことでもないですが Workflow の起動タイミングや Image の Push タイミングを少しだけ調整しています。

これで少し丁寧ポイントが上がりました。

おわりに

各言語の公式イメージなどが GitHub Container Registry にあればさらにレートリミットの影響を受けなくてすみますが、もしそうなると利用率がさらに増えて今度は ghcr が制限されるということになりそうですね。

ghcr でも特に困ることはほとんどないので、今後イメージを作るなら全部 ghcr でいいじゃんという気持ちになっています。(各クラウドのコンテナレジストリは別の話題ということで)