Android minne

minne Androidアプリのビルドをリモートビルドで高速化!

Android minne

minne事業部チーフテクニカルリードの @hisaichi5518 です。

minneのAndroidアプリは、2012年から開発されていてそれなりにコードベースも大きくなってきており、MacBook Pro (13-inch, 2016) プロセッサ 2.9 GHz Intel Core i5, メモリ 16 GB 2133 MHz LPDDR3 でビルドするとクリーンビルドで10分、キャッシュが効いていても3分ほどかかっていました。コードに変更を加えてビルドをするたび数分待ち、その間Twitterを眺めているのは開発効率が高いとは言えないので、リモートビルドを導入しました。

リモートビルドとは?

ここでいうリモートビルドとは、開発に利用しているマシン(以下、ローカルマシン)よりもハイスペックなインスタンスをGoogle Cloud Platform(以下、GCP)などに立ち上げて、そのインスタンスを利用してビルドを行い、ビルド結果をローカルマシンと同期することを指します。

リモートビルドを行ううえで必要となるのが、以下の2点です。

  • GCPなどにインスタンスを立ち上げて、ビルドが出来る環境に整備する
  • ファイルの同期やビルドの実行

どう実現するのか、1つずつ説明していきます。

GCPなどにインスタンスを立ち上げて、ビルドが出来る環境に整備する

minneには、リリース準備の自動化に利用しているtenmaというコマンドがあります。前まではOSSではありませんでしたが、今はOSSになっています。

https://github.com/hisaichi5518/tenma

このtenmaコマンドに、インスタンスの立ち上げとビルドが出来る環境に整備するまで行ってくれるtenma ichibaがあるのでそれを使っていきます。1

tenma ichiba は、gcloudのインストールとセットアップが必要です。以下のリンクからダウンロードし、gcloud initを実行しアカウントを紐付けてください。

https://cloud.google.com/sdk/gcloud

またどのAndroid SDKをインストールするか等の情報が必要になるので、Androidアプリのディレクトリ配下に tenma/ichiba.yml を作る必要があります。

android_sdk:
    license: "your license key"
    update_list:
        - platform-tools
        - build-tools-27.0.3
        - android-27
        - extra-android-m2repository
        - extra-google-m2repository
        - extra-google-google_play_services

準備がおわり、Androidアプリのディレクトリで以下のコマンドを実行すればインスタンス作成とビルドが出来る環境が整います。2

bundle exec tenma ichiba --create-instance --provision-instance --instance-project <your-gcp-project>

tenma 0.9.0時点で作成されるインスタンスは以下の通りです。オプションで変更も可能です。

オプション デフォルト値
--instance-name remote-build
--instance-zone asia-northeast1-c
--instance-machine-type n1-highcpu-16
--instance-disk-size 20(GB)

以下は、インスタンス作成時にgcloudに渡すオプションですが、tenma ichiba経由では変更出来ません。

gcloud オプション
--preemptible true
--image-family ubuntu-1710
--image-project ubuntu-os-cloud

ファイルの同期やビルドの実行

インスタンスの準備が出来たはずなので、次はファイルの同期やビルドを行います。これらは、tenmaではなく gojuno/mainframer を利用します。

Androidアプリのディレクトリに移動し、mainframerをダウンロードし、.mainframer/configを作成します。

remote_machine=remote-build.asia-northeast1-c.<your-gcp-project>

ローカルからインスタンスに同期する必要がないファイルなどを指定したい場合は、ignore, localignore, remoteignoreを作成すれば、よしなにやってくれます。

設定が終われば、以下のコマンドでファイルの同期やビルドが行われます。

./mainframer.sh ./gradlew :app:assembleDebug

またAndroid StudioのRunボタンから実行したい場合はAndroid Studio Configuration to Run APKに書いてある通りにやれば、実現できます。

結果

tenma ichiba + mainframer を利用することで、ビルド時間はクリーンビルドで3分, キャッシュが効いているビルドで30秒から1分ほどとなりました。

Androidエンジニアは4人いて、1人1日20回ビルドしてるとして、1ビルド3分かけてるとすると

  • 20*3=1日で1人あたり60分(1時間)ビルドを待っている
  • 20*3*31=1ヶ月(31日間)で1人あたり1860分(約31時間)ビルドを待っている
  • 20*3*31*4=1ヶ月(31日間)で4人あたり7440分(約124時間)ビルドを待っている

これをリモートビルドを実装したときの数字である1ビルド1分ほどにすると

  • 20*1=1日で1人あたり20分ビルドを待っている
  • 20*1*31=1ヶ月(31日間)で1人あたり620分(約10時間)ビルドを待っている
  • 20*1*31*4=1ヶ月(31日間)で4人あたり2480分(約41時間)ビルドを待っている

となり、ざっくりとした計算ではありますが 124時間-41時間で4人あたり1ヶ月約83時間待たなくて良くなりました。

節約術

Androidエンジニアが働く時にだけリモートビルドインスタンスが立ち上がっていればいいので、平日朝に自動でインスタンスを立ち上げて、夜には削除するようにしています。これはBitriseの定期実行機能を使って実現しています。

以下のようなワークフローになります。

---
format_version: '4'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: android
workflows:
  delete-remote-build-instance:
    steps:
    - activate-ssh-key:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone: {}
    - script:
        title: Delete remote-build instance
        inputs:
        - content: |-
            #!/usr/bin/env bash
            set -e
            set -x

            curl -o /tmp/sacc_key.json $BITRISEIO_SERVICE_ACCOUNT_KEY_URL
            gcloud auth activate-service-account -q --key-file /tmp/sacc_key.json
            gcloud config set project $GCP_PROJECT

            bundle install
            bundle exec tenma ichiba --delete-instance
    - deploy-to-bitrise-io: {}
    - slack:
        inputs:
        - channel: "#minne_dev"
        - text: ''
        - pretext_on_error: Androidリモートビルドインスタンスの削除に失敗!!!!!!!!!1
        - pretext: Androidリモートビルドインスタンスの削除を行いました
        - channel_on_error: "#minne_dev"
        - webhook_url: <webhook_url>
  create-remote-build-instance:
    steps:
    - activate-ssh-key:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone: {}
    - script:
        title: Set up remote machine
        inputs:
        - content: |-
            #!/usr/bin/env bash
            set -e
            set -x

            curl -o /tmp/sacc_key.json $BITRISEIO_SERVICE_ACCOUNT_KEY_URL
            gcloud auth activate-service-account -q --key-file /tmp/sacc_key.json
            gcloud config set project $GCP_PROJECT

            bundle install
            bundle exec tenma ichiba --create-instance
    - deploy-to-bitrise-io: {}
    - slack@2.7.2:
        inputs:
        - channel: "#minne_dev"
        - pretext: Androidリモートビルドインスタンス作成に成功しました。
        - pretext_on_error: Androidリモートビルドインスタンス作成に失敗!!!!!!1
        - channel_on_error: "#minne_dev"
        - webhook_url: <webhook_url>
app:
  envs:
  - opts:
      is_expand: false
    GRADLE_BUILD_FILE_PATH: build.gradle
  - opts:
      is_expand: false
    GRADLEW_PATH: "./gradlew"
  - opts:
      is_expand: false
    GCP_PROJECT: <your-gcp-project>

まとめ

  • minneのAndroidアプリ開発では、リモートビルドを利用しています
  • リモートビルドによって、4人あたり約83時間待つ時間を減らすことが出来ました
  • 節約のために、インスタンスの立ち上げと削除は自動化しています
  1. tenma ichibaは、みなさんご存知活気あふれる浪花の台所 天満市場から取ってます。 

  2. 都度プロジェクトを設定するのがめんどい人は、環境変数TENMA_ICHIBA_INSTANCE_PROJECTを設定すると適用されます