inFablic | Fablic, inc. Developer's Blog.

フリマアプリ フリル (FRIL) を運営する Fablic の公式開発者ブログです。Fablic のデザイナー・エンジニア・ディレクターが情報発信していきます。

Android版フリルへのCircleCI 2.0の導入でCI実行時間を約5分の1に短縮

f:id:nakamuuu-muuu:20170808192110j:plain

こんにちは。Androidエンジニアの @nakamuuu です。

先日、Android版フリルでCI環境として使用していたCircleCIを最新版の CircleCI 2.0 に移行しました。

この記事では、これまでのAndroid版フリルにおけるCI環境について触れつつ、実際にCircle CI 2.0へ移行してどのような効果があったのかを簡単に紹介していきます。

circleci.com

Android版フリルにおけるCI環境

まずAndroid版フリルにおけるCI環境ですが、基本的にはCircleCI導入事例として以前紹介したものと大きくは変わっていません。

in.fablic.co.jp

細かなところでは、ビルド前の Android License Tools Plugin でのライセンス表記のチェック、Lintでの静的解析の結果をGitHubのPullRequestに表示する処理が新たに追加されました。

一連のフローを簡単にまとめると以下のようになります。

  1. CircleCIがGitHubへのpushをフックして実行開始
  2. Android License Tools Plugin でのライセンス表記のチェック
  3. Lintでの静的解析と reviewdog によるPullRequestへのレビューコメントの追加
  4. ユニットテストの実行
  5. develop / masterブランチではAPKを生成して DeployGate にアップロード*1

CIの実行に長大な時間がかかるように

一連のフローが大きく変化していないとはいえ、Lintでの静的解析の導入やプロダクトの成長に伴うテスト項目の増加もあり、直近ではCI実行時間がかなり長大になっていました。1年半ほど前の CircleCIのTipsをまとめたエントリ では「Robolectricの導入で実行時間が約10分へ短縮された」とありますが、最近は 通常時は約20分 / APKのビルド&アップロードを伴う場合は約30分 というのが当たり前の状態でした。

FablicではサーバーサイドのRailsアプリのテストでもCircleCIを利用しています。コンテナは共用されるため、AndroidのCIの実行時間が長いことが要因となって、サーバーサイドのキューを詰まらせてしまうことも多々ありました。

参考:CircleCI 2.0導入前の実行時間の内訳

タスク 実行時間 (累積)
環境設定 1分 -
Android SDKなどの取得 6分 7分
ライセンス表記のチェック 1分 8分
Lintの実行 8分 16分
ユニットテストの実行 5分 21分
APKのビルド&アップロード 8分 29分

CIの実行時間が長い原因

最新版のAndroid SDKの取得

CIの実行時間が長い原因の一つに最新版のAndroid SDKなどの取得に時間がかかっていたことが挙げられます。(上記の表内では6分)

CircleCIで予め用意されているバージョン*2以外のAndroid SDKなどを使用する場合、CIの実行中に自前で取得する必要があります。最新のバージョンへの追従はあまりされていないので、CircleCIを使用するAndroidのプロダクトの多くがこの取得を行っているのではないでしょうか。

キャッシュを上手く利用することでこの時間をできる限り削ることもできますが、Android版フリルではそこまで手を入れられていない状態でした。

CircleCI 1.0のパフォーマンス

CircleCI 1.0のパフォーマンス自体があまり良くなかったというのも実行時間が長くなっていた大きな要因です。

例えば、上記の表内では8分かかっているLintの実行は、手元のラップトップ上では2分未満で完了します。CircleCI 1.0では1つ1つのステップ、特にファイル操作を伴うものについては異常に時間がかかっている印象でした。

CircleCI 2.0の導入

移行作業自体は公式で用意されたドキュメントを参考にしつつスムーズに行うことができました。

1点だけ注意が必要なのは 1.0の設定ファイルとの互換性が全くない ということです。ファイルの置き場所もプロジェクト直下の circle.yml から .circleci/config.yml に変更されているほか、これまで dependencies test deployment などに分けて書いていた各ステップも全面的に書き換えが必要となります。

詳細な移行手順については以下のリンクを参照していただければと思います。

Migrating from 1.0 to 2.0 - CircleCI Language Guide: Android - CircleCI

参考:Android版フリルの .circleci/config.yml (一部省略)

version: 2
jobs:
  build:
    docker:
      # 現時点ではCircleCI公式で用意されているDockerイメージを使用している。
      - image: circleci/android:api-25-alpha
    environment:
        # 省略
    steps:
      - checkout
      - restore_cache:
          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
      - run:
          name: Download Dependencies
          command: ./gradlew androidDependencies
      - save_cache:
          paths: ~/.gradle
          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
      - run:
          name: Check Licenses
          command: # Android License Tools Pluginの実行
      - run:
          name: Run Lint & reviewdog
          command: # Lint&reviewdogの実行
      - run:
          name: Run Tests
          command: # テストの実行
      - store_test_results:
          path: app/build/test-results
      - store_artifacts:
          path: app/build/reports
          destination: reports
      - run:
          name: Build APK & Distribute DeployGate
          command: |
            # CircleCI 2.0の新機能であるWorkflowは使用せず、CIRCLE_BRANCHを見て分岐している。
            # テスト失敗時にビルドが上がらないでほしい(=並列に実行できない)のでWorkflowを使う旨みがあまりなかったため。
            if [ $CIRCLE_BRANCH = "master" ] || [ $CIRCLE_BRANCH = "develop" ]; then
                # 特定ブランチの場合のみAPKのビルド&DeployGateへのアップロード
            else
                echo "DeployGate distribution is skipped."
            fi
      - store_artifacts:
          path: app/build/outputs/apk
          destination: apks

CI実行時間が5分の1に短縮

f:id:nakamuuu-muuu:20170731000231p:plain

CircleCI 2.0への移行後は上のグラフのように CI実行時間が約5分の1に短縮されました 。今まで20分〜30分ほど掛かっていたものが、4分〜8分ほどで完了するようになっています。(グラフはDeployGateへのアップロードを伴うdevelopブランチでのCI実行時間)

実行時間が短くなった理由として一番大きいのは、Dockerのネイティブサポートによりカスタムイメージを使用できるようになったことです。コンテナに指定したDockerイメージがすでに存在すればそれが利用されるため、最新版のAndroid SDKなどの取得を含む環境設定が毎回行われずに済むようになりました。

現時点では特に不都合がなかったため、DockerイメージにはCircleCI公式のものを使用しています。このイメージはGitHub上でも公開されているので、将来的に最新版のAndroid SDKなどに追従しなくなった時には修正を投げたり、自身でフォークすることができます。もちろん、別のDockerイメージに乗り換えたり、今まで通りCIの実行中に自前で取得するという選択肢を取ることも可能です。

GitHub - circleci/circleci-images: Scripts to generate images for building projects on CircleCI 2.0

また、CircleCI 1.0であった全体的なパフォーマンスの問題も解消されているようです。Lintの実行やビルドなどの実行時間はほぼ手元の環境と同等のものとなりました。各ステップで短縮された時間の積み重ねで全体を通した実行時間もかなり短くなっている印象です。

参考:CircleCI 2.0導入後の実行時間の内訳

タスク 実行時間 (累積)
環境設定 10秒以下
(Dockerイメージの取得を伴う場合は1分ほど)
-
依存解決(Gradle) 10秒以下
(build.gradleに変更がある場合は1分30秒ほど)
-
ライセンス表記のチェック 10秒以下 -
Lintの実行 2分 -
ユニットテストの実行 1分 4分〜6分
APKのビルド&アップロード 2分 6分〜8分

まとめ

CircleCI 2.0への移行により、以前と比べて大幅にCI実行時間を短縮することができました。AndroidのCIの実行時間が要因となって、サーバーサイドのキューを詰まらせることも減ったため、かなり安心して開発を行えるようになっています。

少し移行作業は必要となりますが、CircleCIを使用していて実行時間の長さに困っていれば、CircleCI 2.0への移行を検討してみてはいかがでしょうか。Android版フリルのように単純なテストだけではなく、その他の解析やサービス連携、APKのビルドを行っている場合には大きな効果が出ることと思います。

弊社では以前からiOSアプリのCI環境に Bitrise を使用していますが、最近、AndroidでのBitriseへの移行例も多く耳にするようになった印象があります。CI環境の改善を行う際に様々な選択肢を持てるように、CIサービスやテストツールの変化へのキャッチアップも継続的に行っていきたいですね。

参考サイト


Fablicでは、CIサービスやモダンなテストツールの活用によるプロダクトの品質担保にも強く力を入れています。このような環境で共にプロダクトを作り上げていきたいエンジニアのご応募をお待ちしております!

*1:Fablicでの社内配布フローとの親和性を理由にFabric Betaから先日移行しました。

*2:https://circleci.com/docs/1.0/build-image-trusty/#android 参照