rrrspecを導入してCI実行時間を2.5時間から10分に短縮した話
こんにちは。
エンジニアの平形( @bigplants )です。
最近、ランナー膝を克服して、走りたくてウズウズしている今日この頃です。
今回は、私が弊社で最近導入したrrrspecのお話をします。
はじめに
弊社のプロダクトALL-INのrspecのCIを高速化すべくrrrspecを採用しました。
技術的な詳細の内容に踏み込むと、このブログでは伝えきれないので概要だけを書きました。
技術的な内容は近日中にQiita(bigplants)で公開します。
rrrspecとは?
Cookpad社が作ったrspecの分散テスト実行システムです。
rrrspec
rrrspecについては、こちらの記事で詳しく解説されています。
分散テスト実行システムRRRSpecをリリースしました
まずは結果から
rspecのテスト完走を高速化する事が目的だったが、どのくらい早くなったのか?
2.5時間 -> 10分!!!(最短の場合)
15倍高速化!!
ドラゴンボールの界王拳かっ!
今までなぜそんなに遅かったのか?
- rspecのspecファイル数: 1400以上
- rspecのexample数の合計: 30000以上
もともと、CircleCIの上で4コンテナでrspecを実行していました。
4コンテナでもこの遅さです。
CIが遅い事でどんな問題があったのか?
CIが2.5時間かかる状況を想像できるでしょうか?
以下のような問題が起こります。
- テストの結果が返ってくるのが半日後とかになる(複数人のジョブが行列待ちになる)
- 待つのがしんどい
- というか待っていられない
- 当日に結果を確認するのを諦める
- 翌日にCIの結果からバグを見つけ修正。そしてまた待つ
- 結果、大変なボトルネックに
なので、重要なブランチのテストを優先すべく他のジョブをすべてキャンセルするような運用を強いられていました。
日々、slackには
「急ぎのテストがあるので、CircleCIのジョブを全部キャンセルさせてもらいます」
というコメントが流れていました。
この状況は何とかせねば!と思ったわけです。
どうやってrrrspecのクラスタを構築したのか?
- AWSのAuto Scaling Groupにworkerをspotインスタンスで構築
- masterのインスタンスタイプ: t2.small
- workerのインスタンスタイプ: c4.large
- workerの台数: 常時1台, rspec実行中は31台
- CIツール: CodeBuild(rrrspecを開始し結果を受け取る目的)
- GitHubのPull Requestからフック
- 結果をslackで実行者にメンション付きで通知
躓いたポイント
rrrspec-client startが遅い問題
当初、rrrspecをトリガする場所をCircleCIにしようとしていました。
しかし、rrrspec-client start(rrrspecを開始するコマンド)が非常に遅い状況に直面しました。
テストが開始されるまで10分以上待たされました。
調査した結果、CircleCIのコンテナからrrrspecのmasterに対してRedisのアクセスがボトルネックになってる事が分かりました。
rrrspecでは1specファイルを1taskとしています。このtask毎にredisのアクセスが複数回発生します。
CircleCIのコンテナが動いている環境はプロダクトのリージョンとは別なので通信に大幅なレイテンシが発生します。
それについてはどうにもならないので思い切ってCIを乗り換える事にしました。
AWS CodeBuildを採用したのはそれが理由です。
テストがランダムでコケまくる
原因は、slaveの数でした。
rrrspecではデフォルトでworkerのCPUコア数分のslaveを立ち上げてrspecを並列実行します。
c4.largeは2コアなので2slaveのプロセスが起動します。
しかし、接続先のMySQL, ElasticSearchはそれぞれ単一のDBしかないのでテストの途中でデータが期待したものと相違が生じます。
並列化は今後の課題です。
暫定的な対応として明示的にslaveを1つに設定しました。
Auto Scaling
rrrspecのworkerノードのワークロードは完全に上がりきる事はありません。1コアしか活用されないのでMaxでも50%前後です。
そしてスケールアウトしてもCPU負荷が下がる事はありません。すべてのworkerでフルにrspecが走ります。
なのでよくあるリニアにインスタンスが増減するオートスケールの計画は適用できません。
少し特殊ですが、1台を常時起動させておき、CPU負荷が30%以上になったら30台起動させ、30%以下になったら30台落とすという風にAuto Scaling Groupでスケーリングポリシーを設定しています。
スケーリングポリシーでは5分間隔でチェックするようにしています。
すべてのworkerノード(30台)が立ち上がって待ち受け状態になるのに最長10分かかります。
なので実際にはworkerの起動が10分+rspec完了10分で合計20分ほどかかる場合があります。
それでも元々2.5時間かかっていたので十分、高速化できています。
コストかけて常時30台起動するようにすれば短縮できますが、今はそこまで必要に迫られていないので暫くはこのまま運用していきます。
コストは増えたのか?
CircleCIでは4コンテナで$150/monthかかっていました。
今回、刷新した結果、CircleCIはrspecの為に使う事はなくなったので$0/monthになりました。
※ CircleCIは別用途で2コンテナ利用していますがすべてCodeBuildに乗り換える予定です。
※ CodeBuildはオンデマンドに利用した時間だけ課金さえるので場合によってはかなり安くなります。
かかる費用は、
- CodeBuild: $40/month
- EC2 Spotインスタンス: $100/month
- rrrspec masterノード: $20/month
合計: $160/month
なのでほとんど増減はありませんでした。
Spotインスタンス万歳!
結果どうなったのか?
- 各開発者がすぐにテストの結果を確認できるので生産効率が非常に高くなった。
- 他の人のテストの状況はほとんど気にする必要がなくなった。
まとめ
rspecを高速化するツールは他にもありますが、specファイルが大量にある場合は高速化するツールとしてrrrspecは有力な一つの選択肢かと思います。
今回、高速化できた上にコストも抑えられたので万々歳でした。
現在のrspecのCI環境が遅いと感じたら思い切ってCI基盤ごと乗り換えるという選択もアリかと思います。
息をするようにCIを回していきたい。
エンジニア募集中!
ビジネスバンクグループではエンジニアを募集中しています。
弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!