こんにちは。ファガイです。
本日はGitHub ActionsでDockerコンテナをGitHub Package Registryにpushした話をしようと思います。
想定読者
- dockerのビルドがなんとなくわかる
背景
現在私は会社のベースイメージとして使えるように
という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を使ってました。
DockerHubと比べてQueueに積まれる時間もないのでめちゃくちゃ良かったです。
次第に
次第にDockerHubにもpushできないかと思って、探した結果 https://github.com/docker/build-push-action を見つけてこちらに書き換えることに。
- 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があるのでそっちを設定してからつけました。
キャッシュとか色々ちゃんとしたい
現状のままではキャッシュを参考にしてくれません。(ただ、2回目のbuild-push-actionはcacheを元にしてくれるので片方でbuild+pushしてしまえばもう片方はすぐにpush出来ます)
build-push-actionにcache_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のほうは対応している様子)
ということで、先に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を使ってくれるようになります。いいね。
ではでは。
コメント