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%になってなかったら失敗とする処理
- 最新化可能ライブラリレポートの生成
- コーディングスタイルチェックと結果レポートの生成
1 2 3 4 5 6 7 |
- name: Build with Gradle run: | set -e -x ./gradlew jacocoTestReport ./gradlew jacocoTestCoverageVerification ./gradlew dependencyUpdates ./gradlew detekt |
なんと。 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
よりも有利だよ!」(意訳)と言っている。 本家謹製ならキャッシュの管理は正確丁寧であろう。こっちにしよう。
<<ドタドタバタリ>>
設定して動かしてみた!ログメッセージによると確かにキャッシュが効いているように見えるぜ。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Restore Gradle state from cache Received 658234 of 658234 (100.0%), 1.1 MBs/sec Cache Size: ~1 MB (658234 B) /usr/bin/tar --use-compress-program zstd -d -xf /home/runner/work/_temp/7d09a523-d060-4bca-a1de-466b48e6d324/cache.tzst -P -C /home/runner/work/blogsampleappli/blogsampleappli Cache restored successfully Restored Gradle User Home from cache key: v4-gradle|Linux|build[37a6259cc0c1dae299a7866489dff0bd]-09e653b85d4c0b8426ee998404a1f7eb37aa10a2 Received 126210 of 126210 (100.0%), 0.4 MBs/sec <<略>> /usr/bin/tar --use-compress-program zstd -d -xf /home/runner/work/_temp/9baa466c-504a-4991-83ef-1332ada7ec9e/cache.tzst -P -C /home/runner/work/blogsampleappli/blogsampleappli Cache restored successfully Restored Project configuration cache from cache key: v4-project|Linux|build[37a6259cc0c1dae299a7866489dff0bd]-09e653b85d4c0b8426ee998404a1f7eb37aa10a2 |
gradle daemon
ところでログを見ていると、gradleが --no-daemon
で動いている。 gradle デーモンが無効だ。なんでだ?
1 2 3 4 |
Writing init script: /home/runner/work/_temp/build-scan-capture.init.gradle /home/runner/work/blogsampleappli/blogsampleappli/gradlew jacocoTestReport --no-daemon --init-script /home/runner/work/_temp/build-scan-capture.init.gradle To honour the JVM settings for this build a single-use Daemon process will be forked. See https://docs.gradle.org/7.3/userguide/gradle_daemon.html#sec:disabling_the_daemon. Daemon will be stopped at the end of the build |
ソースを調べてみると --no-daemon
がアクションにハードコーディングされてるな。これ設定可能にならないかな? gradleを1回だけ実行する用途だけならデーモン無効で構わないが、複数回続けて実行する場合はデーモン起動しておいたほうが良いはずだ。
<<ドタドタバタリ>>
あ、設定可能にしたいっていう 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を使うようにしたぜ!最新バージョン以外に用はないのだ。