devcontainers/ci で意識低めに続ける CI
私が個人的に開発している web アプリケーションでは、手元の開発環境の構築に Dev Container を使っています。また、CI/CD には GitHub Actions を使っています。
この時、開発環境と CI にはほぼ同じ環境を用意する必要があります。しかし、当然ながら両者で環境を定義する方法は異なっており、例えば Node.js のバージョンを上げる際など、環境に手を加える際には、Dev Container の定義と GitHub Actions の定義の両方を修正しなければならず、メンテナンスが面倒でした。
そんな折、GitHub Actions で Dev Container を使って CI を実行できる devcontainers/ci を知って試してみたところいい感じだったので、紹介します。
devcontainers/ci とは
devcontainers/ci は GitHub Actions 用のアクションで、GitHub Actions の中で Dev Container を立ち上げてその中でコマンドを実行する、というワークフローを簡潔に記述することができます。以下は devcontainers/ci を使用するワークフローファイルの抜粋で、このように uses を使って呼び出し、実行したいコマンドを引数として渡すのが基本的な使い方です。
- uses: devcontainers/ci
with:
env: |
DATABASE=postgres://postgres:postgres@db:5432/postgres
# ...
runCmd: |
npm ci
npm test
このように書くことで CI 上でイメージがビルドされ、指定したコマンドがコンテナの中で実行されます。
devcontainers/ci を使ってよかったこと
まず、当初期待していた通り、開発環境と CI 環境の定義が一つにまとまるので、環境のメンテナンスが楽になりました。具体的には devcontainer.json だけをメンテナンスすればよく、Node.js などのランタイムのアップデートなどが手軽に行えます。
また、CI 環境が手元にもあるので、CI が失敗した際のデバッグが楽になりました。例えば、GitHub Actions 上でテストが失敗した場合でも、手元で同じ Dev Container を立ち上げて同じコマンドを実行すれば基本的には同じ結果が得られます。これまで CI 特有の問題をデバッグする際には YAML を修正して push して (時には何分も) 待って結果を見て修正する、という苦行を繰り返す必要がありましたが、それが必要になる場面が減るのはよいことです。
総じて、CI 環境のメンテナンスコストが下がったと思います。
devcontainers/ci のよくない点
もちろん銀の弾丸ではないと思っており、向いていないであろう場合についても書いてみます。
まず、Dev Container は第一義的には開発環境のためのものなので、features などを使って開発用の便利ツールを入れていくことが多いと思います。すると CI には必要のないものも含めて CI 上でビルドすることになり、効率が悪いです。私の理解では devcontainers/ci は手軽さと引き換えに非効率を受け入れるソリューションであり、CI の実行速度をカリカリにチューニングしたいような場合には向かないと思います。
とはいえ、ビルド時間に関しては、以下のように YAML に数行追加することでビルド済みのイメージを GitHub Container Registry にキャッシュすることができるので、キャッシュが効いている限りそこまで気にならないと思います。
- uses: devcontainers/ci
with:
+ imageName: ghcr.io/${{ github.repository }}-devcontainer
+ cacheFrom: ghcr.io/${{ github.repository }}-devcontainer
+ push: always
env: |
DATABASE=postgres://postgres:postgres@db:5432/postgres
# ...
runCmd: |
npm ci
npm test
また、コンテナ内で実行するコマンドは文字列として渡す形になるため、uses を使って他のアクションを呼び出すといった GitHub Actions 固有の機能には依存できません。私が今回 devcontainers/ci に移行したワークフローは元々単純にテストを実行していただけだったこともあり比較的すんなり移行できましたが、GitHub Actions の機能に深く依存しているワークフローを移行するには、コンテナ内で実行できる部分 (純粋なコマンド実行で済む部分) を切り出すなど工夫が必要になるかもしれません。
まとめ
以上、devcontainers/ci について紹介しました。すでに Dev Container を使っていて、CI では速度を追求したり、込み入ったことをしたりしないと割り切れる場合には、CI のメンテナンスコストを下げるよい選択肢になると思います。
Discussion