開発速度を高めるためのCIと自動化

はじめに

CYBIRDエンジニア Advent Calendar 2016 1日目担当の @keitarou です。
CYBIRDで女性向けの恋愛ゲームの開発とか運用とかやっています。
Golangを始めたと思ったら、最近はC#にハマっちゃってるエンジニアです。
新卒入社3年目。今年も3年連続でアドベントカレンダーをやっています。

CIとは?

CI(継続的インテグレーション)とは、
ソフトウエア開発においてコンパイルやテストといったビルド処理を一日に何度も実行することにより、フィードバックサイクルを短くし、品質を向上させる仕組みのことです。
CIを実施することにより、ソフトウエアの修正や変更によってコンパイルが通らなくなる不具合やデグレードなどを早期に発見できる上、その不具合がどの変更によって引き起こされたかを特定しやすくなります。そのため、品質の向上だけでなく、手戻り工数の削減も期待できます。
引用: http://itpro.nikkeibp.co.jp/article/COLUMN/20121023/431822/?rt=nocnt
↑の記事での説明が個人的に一番しっくりきているので、お借りしています。

なぜやるのか

  • 品質向上の為
  • 品質向上の為にフィードバックサイクルを短くする。(開発速度を高める)
  • フィードバックサイクルを短くするために、一連の手続きを自動化/無人化する。

というポリシーでやっています。

一連の手続き

具体的に、自分のチームでは以下の様なことをやっています。

  • テストコード(ユニットテスト)の実行の自動化
  • テスト結果のカバレッジの集計
  • ソースコードの静的解析(複雑度評価・違反検知)
  • 依存パッケージのアップデート/イシューの検知
  • アプリバイナリの出力

など
今回はこれらの一連の手続きに関してさらっとご紹介できればと考えています。

テストコード(ユニットテスト)の実行の自動化

CIの中でもおそらく、一番導入や運用が難しいところかと思います。
プロジェクトによって異なりますが、PHPのプロジェクトの場合はPHPUnit, Composer, Jenkinsなどのツールを利用してテストコードの作成、実行を行っています。
以下のようなイメージです。
スクリーンショット 2016-11-30 10.53.30

どこかでぶち当たる課題

テストコードの開発/自動化運用を行うにあたって必ずと言っていいほど、ぶち当たってきた課題があったので簡単に紹介です。

スローテスト

どれぐらいのテストコードの規模だったかは忘れましたが、1プロジェクトのテストコードを全て実行するのに30分ほどの時間がかかっていた時期がありました。
CIとして一連の手順を実行しないとリリースができないような運用をしていたため、開発者は30分待たされることになります。非常に困ります。
おおよそ、一連の手順を待つには3~5分ぐらいで完了しないとストレスになります。(個人的な観測)
3分で終わるのであれば、一度コーヒー飲んだりして一服するのにちょうど良いと考えているからです。
そんなスローテストですが、具体的には以下のような手段で解決すことに至りました。

テスト実行の並列化。

PHPUnitだとParaTest, Jenkinsだとマトリックスプロジェクトなどを利用することで並列実行することが出来ます。ちなみに自分の所はJenkinsでマトリックスプロジェクトを利用して並列実行させています。
Jenkinsのマトリックスプロジェクトは、マルチプラットフォームなどでのテストによく利用される機能だと思いますが、テストの実行時にも、適当なテストスイートの単位で分割して並列実行させることで、実行時間短縮に効果的です。
https://github.com/brianium/paratest

ストレージのRAM化

MySQLのサーバーを直接利用するテストケースなども多く運用していることもあり、ストレージのIO周りがボトルネックにもなることが多いです。(データストアのMock化していないプロジェクトの場合)
直接RAM領域をMySQLの”datadir”に指定することなどもやっています。
ストレージエンジンで、Memoryストレージなどもありますが、Memoryストレージでは一部の制約を正しく利用できないので、Memoryストレージは利用していないです。

プロファイラ動いてない?

実際にありました。(笑)
特にPHPの場合は、Xdebugが動いている可能性が高いです。
しかし、PHPUnitでカバレッジを集計するにはXdebugが必要なので、ここは注意です!!
今は、常時はXdebugは無効。カバレッジ集計するときは

php -dzend_extension=xdebug.so

とな感じで実行時にオプションとして付与するようにしています。

実行環境の物理的な改善

札束を利用します。
AWSを利用している場合は、EC2だと一度インスタンスを停止させて、『インスタンスタイプの変更』から、強そうなインスタンスタイプを選択するだけです。
ほら、簡単です。
とな感じでスローテストに関しては対応して来ました。
一旦今は30分が5分前後に短縮され、最近ですが、PHP7にマイグレーションさせると3分前後まで改善されました。
特にこれ以上チューニングしても体感的にはあまり変わりがないと感じているので結論満足はしています。

Jenkinsのキメラ化

他にはこんな課題もありました。
Jenkinsでテストを実行するためにJenkinsサーバーに対してランタイムや、ミドルウェアのインストールなどを行ってしまい。Jenkinsがキメラに・・・
キメラ化すること自体に課題は無くて、何が課題かというと1つのJenkinsサーバーで複数のプロジェクトのテスト実行などが行えない状態になってしまうことが問題。
それを解決するためにJenkinsノードサーバーを利用してテストの実行自体は全てノードサーバーに委譲するような仕組みにしています。
他の課題の解決手段としてDockerを利用する。CircleCIを利用するなどのアプローチもあります。
基本的にはDockerを利用してテスト実行をコンテナ内で独立的に実行するように新しいものはシフトさせていますが、先程のスローテストなどの課題があるので、コテコテにチューニングするために、ノードサーバーを利用しているケースが未だ多いです。

テスト結果のカバレッジの集計

カバレッジの集計に関しては、基本的にテストコードの実行が自動化できるレベルまで達していると、それほどコストにはならない印象です。
課題があるとすると、カバレッジは何%を守るべきか云々がありますが、それに対しては基本的には、『何%になっている』ではなく、『何%だったのが、何%になった』っということを評価するようにしています。
要は傾向を評価しています。下がってきていたら、何か課題が有るし、赤っているのであれば順調です。単純ですね。
なので、集計したカバレッジ結果を時系列で参照できる仕組みが必要です。
そこで、『Coveralls』を利用しています。
Covarallsを利用することで、何時カバレッジが何%だったかを直ぐに確認できるので、とても役に立っています。
スクリーンショット 2016-11-30 11.38.59

ソースコードの静的解析(複雑度評価・違反検知)

ソースコードの静的解析に関しては、大きくは2つの手段を利用しております。

違反検知

目的は、コーディング規約などに違反したコミットを反映させないことでプロジェクトの治安維持です。
コードレビューなどで人間が評価することも有ると思いますが、出来る限りは無人化したいです。
仕組みとしては、Scrutinizerを利用しています。
他にもESlintやPHPLintなどで自前で用意することも出来ます。

複雑度評価

何処に冗長した処理が有るか、条件分岐が多いかなどを集計し、プロジェクト単位でのソフトウェアの複雑度を評価できる仕組みも用意しています。
循環的複雑度(サイクロマチック複雑度)などと呼ばれています。
SonarQubeを利用することで、その複雑度の可視化ができるようにしています。
特に『Technical Debt 』という項目があり、ソフトウェアの技術的負債度を人日で計算する仕組みがお気に入りです。

依存パッケージのアップデート/イシューの検知

アプリバイナリの出力

これらに関しても自動化させてCIとして取り込んでいます。
詳しくは後日のアドベントカレンダーにて、ご紹介させて頂く予定です。

思い出

かれこれ、2,3年ほどテストの自動化、CI環境の構築などやってきましたが、簡単に振り返リます。

  • テスト書くこと、CI・自動化などの目的をハッキリさせないと導入運用は辛い。目的は『フィードバックサイクルを・・・』ぐらいがシックリくる
  • ストレス(実行遅いとか)のある環境ではみんな使わない。使わないってことは、CIが正常な状態で保守されない。結果、目的を果たせない
  • 時系列でソフトウェアの品質評価ができるツールは使い勝手が良い。最新の状態しか評価できないツールは使いづらい。

最後に

CYBIRDエンジニア Advent Calendar 2016 明日は、 @keitarou の「何かで」です。
また明日も私です。事務処理の都合、2日連続になってしまいました。
今からネタを考えます。
乞うご期待!
また、CYBIRDではテッキーなエンジニアを絶賛募集しております。
是非一度、遊びに来てください。
エンジニア採用ページ: http://www.cybird.co.jp/recruit/engineer/