JavaScript ActionからDocker Container Actionへの移行
はじめに
もともとTypeScriptで書かれていたJavaScript Actionをメンテしていたのですが、モチベがなくなっていき、1年ほど放置していました。
ただ、最近読み終わったGitHub CI/CD実践ガイドという本でDocker Container Actionを知ったので、Rustで実装しなおしてみました。
この記事では、どのようにDocker Container Actionを実装するかについてや、ビルドしたDockerイメージの保存先であるGitHub Container Registryを使う方法などを解説していきます。
今回移行したリポジトリは以下となります。
Actionの自作について
Actionを自作する方法はいくつかの方法があり、現在は以下の3つの方法があります。
- Composite Action
- JavaScript Action
- Docker container Action
元々使っていたものはJavaScript Actionで、ロジックをTypeScriptで書いてそれをJavaScriptにトランスパイルして、出来上がったものをエントリーポイントと設定していました。
runs:
using: 'node20'
main: 'dist/index.js'
これに比べてDocker Container Actionは、DockerfileまたはDockerイメージをエントリーポイントとして設定します。
runs:
using: "docker"
image: "docker://ghcr.io/takashicc/slack-approval:2.0.0" # or image: Dockerfile
Docker Container Actionは、どんな言語でも書けるメリットがありますが、デメリットがいくつかあります。
-
image
にDockerfileを指定した場合、アクションを使う側が毎回Dockerfileビルドから行うため、ビルド時間が長い場合はDockerイメージを指定させたほうがいいです。 - Dockerイメージを指定させる場合も、GitHub Runnerのアーキテクチャによっては実行できない場合があるため、マルチアーキテクチャ対応したDockerイメージを作る必要があります。
- Linux Runnerでのみ動作するため、MacやWindowsで動作させたい場合は使えません。(Dockerなのに...?)
- Self-hosted RunnerでもLinuxだけみたいです。
- https://docs.github.com/en/actions/sharing-automations/creating-actions/about-custom-actions#docker-container-actions
Docker Container Actionのセットアップ
基本的には以下のドキュメントに載っている通りとなります。
action.yml
自作アクションの名前や入力値などを設定するファイルです。
using
にはdocker
を指定し、image
に後述するGitHub Container RegistryにアップロードしたDockerイメージを指定します。
Dockerfile
自作アクションのエントリーポイントとなるDockerfileです。
cargo-chefやsccacheを使ってキャッシュを活用したビルドができるようにしています。初回はビルドに時間がかかりますが、2回目以降はキャッシュが使えるため、ビルド時間が短縮できます。
GitHub Container Registry
GitHub Container RegistryはGitHubが提供しているサービスで、コンテナイメージをプッシュ、プルなどができます。
GitHubのウェブページからは直接作成できないため、まずはローカルでDockerfileをビルドしてDockerイメージを作成し、それをGitHub Container Registryにプッシュする必要があります。
# Dockerfileをビルド
# GitHub Container Registryにアップロードするタグ名に大文字は使えないため、小文字に変換しています
$ export USER=$(gh config get -h github.com user | awk '{print tolower($0)}')
$ export REPO=example
$ docker build -t ghcr.io/${USER}/${REPO}:latest .
# GitHub Packagesへのアクセスを有効化
$ gh auth refresh --scopes write:packages
# GitHub Container Registryにログイン
$ gh auth token | docker login ghcr.io -u ${USER} --password-stdin
# ビルドしたDockerイメージをプッシュ
$ docker push ghcr.io/${USER}/${REPO}:latest
プッシュが完了すると、GitHubのPackagesページから確認できます。(https://github.com/<OWNER>?tab=packages
)
クリックすると詳細ページに遷移するので、右下にあるPackage settings
をクリックします。
GitHub Packagesとリポジトリは論理的に独立したリソースですが、Manage Actions access
からリンクさせることができます。
また、デフォルトだとPrivateなので、Change package visibility
からPublicに変更します。
これでGitHub Container Registryのセットアップは完了です。
ビルドワークフロー
タグがプッシュされたらマルチアーキテクチャ対応したDockerイメージをビルドして、GitHub Container Registryにプッシュするワークフローです。
手動ビルドさせたい場合のために、workflow_dispatch
も追加しています。
strategy.matrix
を使ってamd64とarm64のビルドを並列で実行しています。
ロジックをRustで実装していた当時はarm runnerが有料のものを使うか、Self-hosted runnerしか手段がなかったのですが、最近になってレビュー版が発表されて使えるようになりました。
マルチアーキテクチャ対応イメージを作る方法については以下が参考になりました。
tagprを使ったリリース半自動化
マニュアルでタグのプッシュは間違えてしまうことがあるため、tagprを使ってリリースを半自動化しています。
詳細については以下の記事が詳しいですが、軽く解説します。
以下のファイルでリリースの際にバージョン値を書き換える対象のファイルを指定したりなどの設定し、
tagprが追従すべき対象のブランチを指定した以下のようなワークフローを用意してあげます。
そうすると、追従対象に指定したブランチにコミットがあるたびに上記のワークフローが動き、以下のようなPRが作成されます。
バージョン番号は前回リリースされたバージョンからパッチバージョンをインクリメントしたものがデフォルトで決まり、PR内でラベルをtagpr:major
やtagpr:minor
などを追加することで、任意のバージョンを設定することができます。
その後、適切なタイミングで上記のPRをマージすると、tagprによってtagがプッシュされ、ビルドワークフローが動き、GitHub Container RegistryにDockerイメージがプッシュされます。
まとめ
ちょっとセットアップが大変ですが、好きな言語で書きたい方にはおすすめです。
エラーハンドリングなど見直すべきところがいくつかあるので、これから改善していこうと思います。
Discussion