GitHub Container registryで作ったイメージをGitHub Actions上で使ってみる

## 前置き
この記事は [GitHub ActionsからGitHub Container registryへイメージをプッシュする - Qiita](https://qiita.com/WakameSun/items/ca67fcfe9c5e43c3f2e3)　の続編です。
前の記事を前提にしているのでまずはそちらをご覧ください。

## GitHub Container registryで作ったイメージを使いたい
前回の記事ではGitHub Actions上で作ったイメージをGitHub Container registryにプッシュするまでを行いました。
今度はGitHub Actionsでそのイメージを使ってみましょう。
前回同様作ったソースコートは[https://github.com/Umekawa/github-actions-sample](https://github.com/Umekawa/github-actions-sample)にあります。



## docker-compose.ymlをロードしてみる
前回作ったDockerfileをそのまま使っても良いですが、今回はdocker-compose.ymlを作ってみましょう。
envとvolumesは特に指定がない場合は省略できますが、今回はサンプルとしてあえて記述しています。

```yml:docker-compose.yml
version: "3.8"
services:
  sample:
    image: $REGISTRY/$DOCKER_REPOSITORY:$DOCKER_TAG
    build:
      context: .
      dockerfile: ./Dockerfile
    environment:
      SAMPLE_ENV: SAMPLE
    volumes:
      - ./sample_folder:/app/sample_folder
networks:
  default:
```

dockerfileの指定に関しては前回作ったdockerfileを指定しています。

## イメージを使ってみる

それでは実際に前回作ったイメージを使ってGitHub Actionsでコンテナを動かしてみましょう。
今回は動いていることが確かめられればなんでも良いので、適当にcheck-envブランチにプッシュされた時にコンテナでenvコマンドを叩いてみます。

全容は以下になります(長いので閉じています)。
<details><summary>details</summary>

```yml:.github/workflows/check_env.yml
name: Check Environment

on:
  push:
    branches:
      - check-env
  workflow_dispatch:

permissions:
  contents: read
  packages: read

env:
  COMPOSE_DOCKER_CLI_BUILD: 1
  DOCKER_BUILDKIT: 1
  DOCKER_CACHE_TAG: master
  DOCKER_REPOSITORY: umekawa/github-actions-sample
  DOCKER_TAG: check_env
  REGISTRY: ghcr.io

jobs:
  use_cache_sample:
    runs-on: ubuntu-latest
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@v3
      - name: Log in to the Container registry (GitHub Container registry)
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/setup-qemu-action@v2
      - uses: docker/setup-buildx-action@v2
      - uses: docker/build-push-action@v3
        id: docker_build
        with:
          context: .
          file: ./Dockerfile
          load: true
          tags: ${{ env.REGISTRY }}/${{ env.DOCKER_REPOSITORY }}:${{ env.DOCKER_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: |
            type=gha
            ${{ env.REGISTRY }}/${{ env.DOCKER_REPOSITORY }}:${{ env.DOCKER_CACHE_TAG }}
          cache-to: type=gha,mode=max
      - name: Check Env
        run: >
          docker compose -f docker-compose.yml run
          sample bash -c "env"
        env:
          SAMPLE_TEXT: sample_text

```
</details>

まず、前回と違うのがpermissionsのpackages部分です。

```yml
permissions:
  contents: read
  packages: read
```

前回はpushをするためにwrite権限になっていましたが、今回はpushはせずに使いたいだけなのでreadになっています。
writeでも動きますが、権限に関しては最小限の方が良いのでreadにしておきましょう。

そこからログインしてbuildする手前までは変わりませんが、build-push-actionに関して若干変わっています。

```yml
      - uses: docker/build-push-action@v3
        id: docker_build
        with:
          context: .
          file: ./Dockerfile
          load: true
          tags: ${{ env.REGISTRY }}/${{ env.DOCKER_REPOSITORY }}:${{ env.DOCKER_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: |
            type=gha
            ${{ env.REGISTRY }}/${{ env.DOCKER_REPOSITORY }}:${{ env.DOCKER_CACHE_TAG }}
          cache-to: type=gha,mode=max
```

注目するのはまずloadオプションです。
前回はイメージをプッシュすればよかったですが、今回はそのイメージを使って作ったコンテナを利用するためloadオプションが必要になります。
このloadオプションはpushと併用できない点に注意してください(loadオプションとpushオプションが両方trueの時エラーになります)。
次に注目するのは、tagsオプションとcache-fromオプションです。
tagsが環境変数を読み替えると`ghrc.io/umekawa/github-actions-sample:check-env`となっているので、前回作った、`ghrc.io/umekawa/github-actions-sample:master`と異なっています。
そのため、cache-fromには明示的にどのイメージを使うのか記述する必要があります。
また、tagsはdocker-compose.ymlのimageと合致させるようにしましょう。

## イメージを動かしてみる
それではロードしたイメージを使って、実際に動かしてみましょう。

```yml
      - name: Check Env
        run: >
          docker compose -f docker-compose.yml run
          sample bash -c "env"
```

コマンド自体は特に変哲もなく問題ないと思います。`: >`の形で長くなりそうな時簡単に複数行かけるので便利ですね。

## 実際にキャッシュ効いてるかみてみる
それではこちらのコードをGitHubに上げてみて、キャッシュを使っているかみてみましょう。
こちらのリンクで実際に動かしてみています。(数日経ったら消えるかも)
https://github.com/Umekawa/github-actions-sample/actions/runs/3194753301/jobs/5214652322

長いので、各フェーズでわけて折りたたんでいます。


### Run docker/build-push-action@v3


<details><summary>詳細を見る</summary>

```
Run docker/build-push-action@v3
Docker info
Buildx version
/usr/bin/docker buildx build --cache-from type=gha --cache-from ghcr.io/umekawa/github-actions-sample:master --cache-to type=gha,mode=max --file ./Dockerfile --iidfile /tmp/docker-build-push-lpBITm/iidfile --label org.opencontainers.image.title=github-actions-sample --label org.opencontainers.image.description= --label org.opencontainers.image.url=https://github.com/Umekawa/github-actions-sample --label org.opencontainers.image.source=https://github.com/Umekawa/github-actions-sample --label org.opencontainers.image.version=check-env --label org.opencontainers.image.created=2022-10-06T05:30:08.333Z --label org.opencontainers.image.revision=921dbfbe14b1bc67dd695035c0c550d981786d26 --label org.opencontainers.image.licenses= --tag ghcr.io/umekawa/github-actions-sample:check_env --load --metadata-file /tmp/docker-build-push-lpBITm/metadata-file .
#1 [internal] load .dockerignore
#1 transferring context: 2B done
#1 DONE 0.0s

#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 201B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/ruby:3.1.2-buster
#3 ...

#4 [auth] library/ruby:pull token for registry-1.docker.io
#4 DONE 0.0s

#3 [internal] load metadata for docker.io/library/ruby:3.1.2-buster
#3 DONE 0.9s

#5 importing cache manifest from ghcr.io/umekawa/github-actions-sample:master
#5 ...

#6 [internal] load build context
#6 DONE 0.0s

#7 [1/5] FROM docker.io/library/ruby:3.1.2-buster@sha256:d7f09eedf86908861a1812ea89016c264e5758d80fa716e6fe50045e6f007b52
#7 resolve docker.io/library/ruby:3.1.2-buster@sha256:d7f09eedf86908861a1812ea89016c264e5758d80fa716e6fe50045e6f007b52 done
#7 DONE 0.0s

#8 [auth] umekawa/github-actions-sample:pull token for ghcr.io
#8 DONE 0.0s

#9 importing cache manifest from gha:13843386142859174583
#9 DONE 0.1s

#5 importing cache manifest from ghcr.io/umekawa/github-actions-sample:master
#5 DONE 0.5s

#7 [1/5] FROM docker.io/library/ruby:3.1.2-buster@sha256:d7f09eedf86908861a1812ea89016c264e5758d80fa716e6fe50045e6f007b52
#7 DONE 0.0s

#6 [internal] load build context
#6 transferring context: 105B done
#6 DONE 0.0s

#10 [3/5] RUN gem install bundler -v 2.3.8 --no-document
#10 CACHED

#11 [4/5] COPY   Gemfile   /app/
#11 CACHED

#12 [2/5] WORKDIR /app
#12 CACHED

#13 [5/5] RUN bundle install
#13 sha256:891cd9999eacd9540296509832b337c2a4f494e5be634cc6582691614350baf4 175B / 175B 0.0s done
(中略)
#13 sha256:2a0ae5ed3318e8db87d129f9c2652dddc78c89f223625a345ec1b7b84149a882 192.51MB / 192.51MB 6.1s done
#13 DONE 6.9s
#14 exporting to oci image format
#14 exporting layers done
#14 exporting manifest sha256:0c138c0ef7902e650c54557a191a665e087f8730b19b2b5449e878951d32d868 0.0s done
#14 exporting config sha256:324bedbe234325cbedccf5800bcefbb3ee87bf7d96cc5e6e5a8c546ffe85b799 done
#14 sending tarball
#14 ...

#15 importing to docker
#15 DONE 18.1s

#14 exporting to oci image format
#14 sending tarball 24.3s done
#14 DONE 31.1s

#16 exporting cache
#16 preparing build cache for export
#16 preparing build cache for export 0.2s done
#16 DONE 0.5s
ImageID
Digest
Metadata
```

</details>

### Check Env

<details><summary>詳細を見る</summary>

```
Run docker compose -f docker-compose.yml run sample bash -c "env"
  docker compose -f docker-compose.yml run sample bash -c "env"
  shell: /usr/bin/bash -e {0}
  env:
    COMPOSE_DOCKER_CLI_BUILD: 1
    DOCKER_BUILDKIT: 1
    DOCKER_CACHE_TAG: master
    DOCKER_REPOSITORY: umekawa/github-actions-sample
    DOCKER_TAG: check_env
    REGISTRY: ghcr.io
    SAMPLE_TEXT: sample_text
Network github-actions-sample_default  Creating
Network github-actions-sample_default  Created
HOSTNAME=2194e2bc6ed4
RUBY_DOWNLOAD_SHA256=ca10d017f8a1b6d247556622c841fc56b90c03b1803f87198da1e4fd3ec3bf2a
SAMPLE_ENV=SAMPLE
RUBY_VERSION=3.1.2
***
BUNDLE_APP_CONFIG=/usr/local/bundle
RUBY_MAJOR=3.1
HOME=/root
LANG=C.UTF-8
BUNDLE_SILENCE_ROOT_WARNING=1
GEM_HOME=/usr/local/bundle
SHLVL=0
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env
```

</details>

キャッシュが効いていない場合、Dockerfileに沿ってビルドが実行されますが、それがうまく省略されキャッシュが効いているのがわかります。

## まとめ
前回の記事からこの記事までで、GitHub ActionsでGitHub Container registryにイメージをプッシュして、さらにそれを別のアクションで使う例を作成してみました。
細かいところを間違えると、キャッシュが効かない場合がある(自分の場合、docker-compose.ymlにcacheの設定を書いてしまって逆にビルドが走ってしまった)など、罠は多いのですが正しく記述することで強力な恩恵を得ることができます。
実用に乗せるにはまあまあな料金がかかってきますが、ちょっと軽いイメージを動かしてみるくらいなら問題ないので是非お試しください。