PHP
CI
TravisCI
NewYearCalendar

【New Year Calendar】これだけはやっとこう、Travis CIを高速化する3つの方法

New Year Calendar 2日目
New Year Calendarって何ぞという方はまずこちらをご覧ください。

本日はTravis CIの話。
1ヶ月程前にプロジェクトでTravis CI高速化を行ったので、その時行った手法を共有します。

Travis CI高速化取り組みのきっかけ

以前から大量のPHPユニットテスト実行の為CIが遅く、ストレスフルだった。
そこにきて新たに外部サービスとのAPI連携部分のユニットテストが増えて更に重くなり、「おーそーいーだーろー!このハゲーー!」と例の元議員のような暴言を吐きたい気分になったとかならないとか。
これはなんとかせねばということで、真剣にCI高速化に取り組むことになったのである。

高速化する為の3つの方法

Cacheを利用しよう

CIを実行する際composerのinstallが毎回走っていました。
実行ログを見れば分かる通り、全てのライブラリをダウンロードしちゃってますね。。
cache not working.png

このような処理を毎回走らせていたら非常に時間を食ってしまいます。
というわけでただちにキャッシュしましょう。

設定は簡単。.travis.yml にcacheの項目を追加するだけです

.travis.yml
cache:
  directories:
  - $HOME/.composer/cache

公式に書いてある通りcomposerのキャッシュディレクトリを指定しています。

更新後に実行したCIのログを見てみましょう。
するとキャッシュを使用していることが分かります。
cache working.png

Yes! Travisクリニック! :thumbsup:

ちなみにrubyを使っている方はbundle installのキャッシュを行う為に↓のように設定してください。

.travis.yml
cache: bundler

キャッシュ設定全般はこちらに詳しく書いております。

コンテナベースの環境を使おう

CIのトータルの実行時間はCIジョブの実行時間だけではありません。
ジョブを実行する環境の起動時間を考慮する必要があります。

実行環境の一覧はこのようになっております。
https://docs.travis-ci.com/user/reference/overview/
大きく分けるとVMベースかコンテナベースの二つとなります。

やはりというべきかVMベースは起動が遅い!ひじょ〜に遅いのです:sweat:
大体約50秒〜1分程度の時間がかかります。

起動が高速であるコンテナベースの環境をぜひ使いましょう。
これも.travis.ymlの設定自体は簡単で、公式の通り以下の様に追加します。

.travis.yml
dist: trusty
sudo: false

ただここで大変なのは.travis.ymlの設定よりもsudoの廃止作業となります。
例えば僕のプロジェクトではmkdirのディレクトリ作成等でsudoでの実行が必要な箇所がありました。
そこでTravis CI実行時のみsudoが必要でない別のディレクトリを指定する様設定ファイルを置き換えたりと工夫しました。

ただそのように工夫までしてコンテナベースの環境を利用する価値は大いにあると思います。
実際コンテナベースの環境に変更したところ、それまで約50秒〜1分掛かっていた実行環境の起動が、わずか数秒で完了するようになりました・・!

Yes! Travisクリニック! :thumbsup_tone1:

CIジョブを並列化しよう

冒頭で述べた通り元々Travis CIが遅かった原因としては大量のPHPユニットテストの実行です。

これらのユニットテストの実行時間を短縮する為にCIジョブを並列化することにしました。
まず.travis.ymlにmatrixの設定を追加します
https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix

.travis.yml
env:
  global:
    - DB=mysql
  matrix:
    - TEST_TARGET=CIParallelTests1
    - TEST_TARGET=CIParallelTests2

envは環境変数を指し、その下にglobalとmatrixで分けています。
つまりはglobalは各ジョブ共通の環境変数、matrixはジョブ毎の環境変数となります。
そして僕のプロジェクトではmatrixでCIParallelTests1とCIParallelTests2でユニットテストを半分に分けて実行することにし、それらを識別する為にTEST_TARGETという環境変数に設定しています。

次にこの環境変数を参照するように変更します。

.travis.yml
script:
  - ./Console/cake test app $TEST_TARGET --stderr --configuration ../phpunit.xml.dist

ユニットテスト実行のコマンドで上記の様に$TEST_TARGETを指定することによって、CIParallelTests1を実行するジョブとCIParallelTests2を実行するジョブが並列で動くこととなります。

実際のビルド結果を見るとご覧の通り二つのジョブが実行されています。
image.png

これにより、ユニットテストの実行時間短縮化に成功しました。

Yes! Travisクリニック! :thumbsup_tone3:

結果発表

果たしてどれほど速くなったのでしょうか?

これは実際のTravis高速化に伴った修正のGitHub Pull Requestの説明です。
(ちなみに僕のプロジェクトは複数外国人エンジニアが居るので基本ドキュメントは英語です。この話はまたどこかでしようと思います)
travie_speed_improve_result.png

結果として改善前はCIの実行時間は環境起動も含めて平均5分程(遅い時は6分以上)掛かっていましたが、改善後は約2〜3分まで速くなりました!
たった数分とはいえ日常的にCIを実行するとなると開発の効率性が向上するのと、何と言ってもCI完了を待つストレスが軽減されます。

高速化やって良かった!