GitHub ActionsでDockerコンテナをGitHub Package RegistryやDocker Hubにpushした話

スポンサーリンク

こんにちは。ファガイです。
本日はGitHub ActionsでDockerコンテナをGitHub Package Registryにpushした話をしようと思います。

想定読者

  • dockerのビルドがなんとなくわかる

背景

現在私は会社のベースイメージとして使えるように

GitHub - fagai/docker-php: personal dockerfiles
personal dockerfiles. Contribute to fagai/docker-php development by creating an account on GitHub.

というgithubリポジトリでDocker Hubにautomated buildを設定してDocker Hubからビルドを行っていました。

Docker Hubのビルドタイムラインは非常に遅くて、ちょいちょいストレスになりつつもなんとか続けていました。

ただ、最近になってDocker Hubのpull制限だったりとか、GitHubがContainer Registryがパブリックベータになったりしてどうにかしたいなぁと思いまして、GitHub ActionsでBuild+Pushすることにしました。

GitHub Container Registry(GCR)とGitHub Package Registry(GPR)は違う

ここから、GitHub Container RegistryをGCRと省略します、決してGoogle Container Registryではないですので注意してください。(略が被るの困るね)

前者は先日パブリックベータとして出たほうで、後者は少し前からあったものです。
2つの違いは

  • URLが異なる(ghcr.ioとdocker.pkg.github.com)
  • ユーザ(or Organization)に紐づくか、Repositoryに紐づくかが違う

といったところです。

紐づく部分に関してはURLのパスも以下のように違います。

  • ghcr.io/OWNER/IMAGE_NAME
  • docker.pkg.github.com/OWNER/REPOSITORY/IMAGE_NAME

こんな感じでユーザに紐づくかリポジトリに紐づくか違います。
まあ、以下に書いていく内容はどちらでも同じように書くことが可能なので、Package Registryなんだ・・・ってなっても見ると良いと思います。

もともと私はGitHub Container Registryを使おうとしてましたが、ドメインがghcr.ioとなっておりなんだろうこれ・・・GitHubじゃないんじゃね・・・?とか思っちゃってGitHub Package Registryを使いだした次第です。
現状で言えばGCRのほうがmanifestのバージョンが新しかったりして使う分にはGCRのほうがいいと思います。パッケージに紐付けたいときはGPRを利用しましょう。

最初やったとき

最初はstarter-workflowのdocker publishを使ってました。

https://github.com/actions/starter-workflows/blob/master/ci/docker-publish.yml

当時のやつ
https://github.com/fagai/docker-php/blob/32988eacf16e697e5376ee0af778079fa1e0fb62/.github/workflows/docker-publish.yml

DockerHubと比べてQueueに積まれる時間もないのでめちゃくちゃ良かったです。

次第に

次第にDockerHubにもpushできないかと思って、探した結果 https://github.com/docker/build-push-action を見つけてこちらに書き換えることに。

GitHub - docker/build-push-action: GitHub Action to build and push Docker images with Buildx
GitHub Action to build and push Docker images with Buildx - GitHub - docker/build-push-action: GitHub Action to build an...

当時の:https://github.com/fagai/docker-php/blob/12d060efd0b91924f0edf85f3b127e19d47cd18a/.github/workflows/docker-publish.yml

- name: Push to GitHub Packages
  uses: docker/build-push-action@v1
  with:
    dockerfile: ${{ matrix.images }}/Dockerfile
    username: ${{ github.actor }}
    password: ${{ secrets.CR_PAT }}
    registry: docker.pkg.github.com
    repository: fagai/docker-php/${{ env.IMAGE_NAME }}
    tags: ${{ env.IMAGE_VERSION }}

スリム~~。
CR_PATの箇所ですが、Container Registry Personal Access Tokenとかの略なんでしょうか、実はstarter-workflowのときにそう書いてあったのでその名前でgithubのtokenをセットしました。(github.tokenでも良いのですが、access tokenをできる限り絞ったほうが良いらしくaccess repositoryとwrite repositoryだけの権限をつけました)

後にDocker Hubのpushの設定もつけました。

- name: Push to Docker Hub
  uses: docker/build-push-action@v1
  with:
    dockerfile: ${{ matrix.images }}/Dockerfile
    username: ${{ github.actor }}
    password: ${{ secrets.DOCKER_HUB_TOKEN }}
    repository: fagai/${{ env.IMAGE_NAME }}
    tags: ${{ env.IMAGE_VERSION }}

build-push-actionの例ではpasswordを設定するようになっていましたが、Docker Hubにもaccess tokenがあるのでそっちを設定してからつけました。

Create and manage access tokens
Learn how to create and manage your personal Docker Hub access tokens to securely push and pull images programmatically.

キャッシュとか色々ちゃんとしたい

現状のままではキャッシュを参考にしてくれません。(ただ、2回目のbuild-push-actionはcacheを元にしてくれるので片方でbuild+pushしてしまえばもう片方はすぐにpush出来ます)

build-push-actioncache_fromsというパラメータがあるのでこれをうまく使いたいです。

色々試していく結果、cache_fromsを利用するためにはBuildkitを使う必要があることがわかりました。
また、build_args: BUILDKIT_INLINE_CACHE=1をセットしないといけないことも判明。
どうやらcache_fromsはドメイン部分も指定しないとダメっぽい雰囲気でした。←ここ結構悩んだ

ということでこんな感じになりました。

- name: Push to GitHub Packages
  uses: docker/build-push-action@v1
  env:
    DOCKER_BUILDKIT: 1
  with:
    dockerfile: ${{ matrix.images }}/Dockerfile
    username: ${{ github.actor }}
    password: ${{ secrets.CR_PAT }}
    registry: docker.pkg.github.com
    repository: fagai/docker-php/${{ env.IMAGE_NAME }}
    tags: ${{ env.IMAGE_VERSION }}
    build_args: BUILDKIT_INLINE_CACHE=1
    cache_froms: docker.pkg.github.com/fagai/docker-php/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}

これで行ける!と思ったのですが

キャッシュされてない・・・となりまして、Actionsのほうを見てみると

#4 importing cache manifest from docker.pkg.github.com/fagai/docker-php/php...
#4 ERROR: httpReaderSeeker: failed open: could not fetch content descriptor sha256:65d9f276544048f140bb1a1cceea52f86e7e704b351c56b8d6b9f18c5e9c0e4d (application/vnd.docker.distribution.manifest.v2+json) from remote: not found

こんな感じでエラーが出てキャッシュが取得できずにそのままビルドが走ってしまっていました。
どうやらGitHub Package Registryのほうはdockerの新しいmanifestにまだ対応してないようです。(GitHub Container Registryのほうは対応している様子)

Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over...

ということで、先にdocker Hubの方からビルドすることにしました。
cache_fromsはdocker.ioから書かないとダメです。

- name: Push to Docker Hub
  uses: docker/build-push-action@v1
  env:
    DOCKER_BUILDKIT: 1
  with:
    dockerfile: ${{ matrix.images }}/Dockerfile
    username: ${{ github.actor }}
    password: ${{ secrets.DOCKER_HUB_TOKEN }}
    repository: fagai/${{ env.IMAGE_NAME }}
    tags: ${{ env.IMAGE_VERSION }}
    build_args: BUILDKIT_INLINE_CACHE=1
    cache_froms: docker.io/fagai/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}

ということでこんな感じになります。

#4 importing cache manifest from docker.io/fagai/php:7.2-alpine-fpm
#4 DONE 0.2s

その後の処理もCACHEDという表示がされ、ビルドが動かずにキャッシュをベースにしていることがわかります。

その後の後付け

- name: cancel old workflow
  uses: styfle/[email protected]
  with:
    access_token: ${{ github.token }}

これは過去にやったことがありました。過去のActionをキャンセルしてくれるActionです。
何度もcommitしたときに過去のActionがずっと残って動いてしまうことを解決してくれます。

また、scheduleもセットしまして、毎週月曜日にActionが動くようにしました。

on:
  push:
    branches:
      - master
  pull_request:
  # 定期更新をやる(毎週月曜日)
  schedule:
    - cron:  '0 0 * * 1'

最後に

GitHub Actionsにbuildとpushをするようにした結果、Docker HubがずっとQueueで待ちになる問題も解決されたし、何より並列でビルドができるので短時間でpushするように出来ました。

build-push-actionはv2でbuildxというDocker 19.03で新しく追加されたビルドが利用されるようになるみたいです。
BuildKitは半公式みたいな感じでBuildxが公式のマルチCPUビルド対応の機能みたいです。
macとかではdocker buildx installと打つことでdocker buildへのエイリアスが貼られて勝手にbuildxを使ってくれるようになります。いいね。

ではでは。

コメント

タイトルとURLをコピーしました