【GMOリサーチではエンジニアを募集中です】採用情報はこちら!

Github ActionsでのCIを高速化した話

Github ActionsでのCIを高速化した話

CIが遅ーーーーーい!!!

今やっているプロジェクトでは普通に開発が進んで来た。 だからコード量増えてきてビルドやテストに時間がかかるようになるのは当然の成り行きではある。 が、ちょっと時間がかかるようになってきた。

待たされている感が出始めた以上なにか手を打たねば。とりあえずできる範囲で無駄処理を削減できないだろうか。

やろう。

現状

対象のプロジェクトは Kotlin で RESTAPI を開発しているプロジェクトだ。コミットごとに単体テスト全部実行などをやっている。

まずはGithub Actionsの実行ログを見てみるか。全体では17分20秒かかっているな。

むむ? Build with Gradle で15分11秒か。全体の大半の8割がこいつにかかっている。

あと start backing service で34秒。Upload report artifact to S3 で19秒。Archive report で39秒だな。このへんを高速化できないか検討しよう。

Gradleのビルド

現状 Gradleでビルドするところはこういうことをやっている。

  • 単体テスト全件実行&カバレッジレポートの生成
  • カバレッジが100%になってなかったら失敗とする処理
  • 最新化可能ライブラリレポートの生成
  • コーディングスタイルチェックと結果レポートの生成

なんと。 gradlew をそのまま使ってました。つまり Gradle 本体を毎回 gradle.org からダウンロードするところからやってるんだ。それは確かに時間がかかるなー。gradleのキャッシュを使うようにならないかな?よくあるCIの高速化ではないか。キャッシュのやり方調べよう。

それから dependencyUpdates は使っているライブラリで最新化可能なものレポートを出してくれる。けれどライブラリの見直し更新はそこまで頻繁にやらないな。これは develop にマージしたときだけにしよう。ちょろっと短縮できる。

<<ドタドタバタリ>>

CIでは actions/setup-java を使ってJavaをセットアップしている。これは GitHub が出している action だが、 gradle のキャッシュを作ったり戻したりする機能があるらしいな。 これ行ってみようか。

https://github.com/marketplace/actions/setup-java-jdk#caching-gradle-dependencies

あ、でも Gradle本家もアクション出しているな。 ドキュメントによると、「actions/setup-javaよりも有利だよ!」(意訳)と言っている。 本家謹製ならキャッシュの管理は正確丁寧であろう。こっちにしよう。

https://github.com/marketplace/actions/gradle-build-action#why-is-this-better-than-running-gradle-directly

<<ドタドタバタリ>>

設定して動かしてみた!ログメッセージによると確かにキャッシュが効いているように見えるぜ。

gradle daemon

ところでログを見ていると、gradleが --no-daemonで動いている。 gradle デーモンが無効だ。なんでだ?

ソースを調べてみると --no-daemon がアクションにハードコーディングされてるな。これ設定可能にならないかな? gradleを1回だけ実行する用途だけならデーモン無効で構わないが、複数回続けて実行する場合はデーモン起動しておいたほうが良いはずだ。

https://github.com/gradle/gradle-build-action/blob/a7174b82a2b531587932c8603ae9ca52e2eebc03/src/execution.ts#L14

<<ドタドタバタリ>>

あ、設定可能にしたいっていう issueが既にあるな。良いねつけとこうっと。

gradle/gradle-build-action#113

<<ドタドタバタリ>>

その後調べたら、対応がされていることを確認できた。リリースされるのはまだ先だろうからこの件は置いておこう。

gradle/gradle-build-action#123

他の処理の対応

start backing service は、docker-compose を使って単体テストに必要な MySQL や redis などのミドルウェアを起動する処理だ。これの高速化に必要なことはイメージサイズの削減ぐらいだろうか。でも使っているイメージは公式のイメージそのままだ。これを公式イメージを高速化するのは難しそうだぞ。とりあえず置いておこう。

Upload report artifact to S3 は、レポート成果物をS3に置く処理だ。これは通信量次第な話だな。中でやっていることは、aws s3 cp を実行しているだけ。高速化は無理そうだ。置いておこう。

Archive report は、Githubのインフラにレポート成果物などを置くための処理だ。 こういうのは今はS3にアップロードしててこっちは誰も見てなさそうだからやめてしまおう。これでちょろっと短縮だ。

https://github.com/marketplace/actions/upload-a-build-artifact#where-does-the-upload-go

まとめ

色々見直して、8分4秒まで絞ることができたぜ!約半分まで下げられた。

gradle の処理はアクションの仕様によりstepを分けることになった。それでも合わせて6分25秒になった。8分少々も縮まるとは。

最新化可能ライブラリレポートの生成はやめたからS3のアップロードがちょっと短縮された。 Archive reportはまるっと消えた。

更に高速化を考えるとどうなるだろうか。例えばstepを並列実行するようにすれば全体として高速になるかもしれない。detekt でのスタイルチェックはテストと並列でも構わないはずだ。またやりたいことが増えたりしたら考えるとしよう。

よし、だいたいこれで良さそうだな。次へ行こう。 ついでに高速化と関係ないがこれを機にJava 17を使うようにしたぜ!最新バージョン以外に用はないのだ。

開発ブログカテゴリの最新記事