Help us understand the problem. What is going on with this article?

Cloud BuildでAndroidプロジェクトをビルドする

More than 1 year has passed since last update.

LIFULL Advent Calendar 2018 2日目

GCPでのCI/CD実現

2018年に開催されたCloud Next '18では、様々なGCPの新プロダクトが発表された。その中で盛り上がっていたのはML・AI関連プロダクトと、CI/CDに関するものだった。
WebならCloud BuildでCIを実施、Spinnakerでカナリアリリース含めた自動デプロイがすべてGCP上で実現できる。
それらの中で今回は第一歩として、Cloud Buildについていろいろと触って試してみた。

やること

Cloud Build + Cloud Source Repository + Container Registryを利用し、試しにAndroidのプロジェクトをビルドしてみる。

手順

手順はシンプルで以下の通り。1番の準備が少し厄介だが、そこさえクリアすれば特段ハマることなく出来た。
1. Androidビルド用のDockerイメージを作成し、Container Registryにpushする
2. 任意のAndroidプロジェクトを作成する(今回はHello Worldレベルの簡単なもので準備)
3. Cloud Source Repositoryにリポジトリを作成し、Cloud Buildと連携させる
4. ソースコードをpushするとビルドが走るようにする

下準備 Cloud Storageにバケットを作成

いくつかのStorageバケットを利用するため、以下のバケットを任意の名前で作成する(後述)。

  • Android SDK License 用バケット
  • Build Cache用バケット
  • Debug Buildバケット

Source RepositoryやContainer RegistryでもStorageを利用するが、勝手に作成されるため割愛。

順を追って自動ビルドを実現する

1. Androidビルド用のDockerコンテナイメージを作成し、Container Registryにpushする

作成方法はgithubにサンプルがあるため、それを参考にする。
https://github.com/GoogleCloudPlatform/cloud-builders-community/tree/master/android

Android SDKパッケージの記述リストを作る

githubサンプルにあるスクリプトからpackages.txtというファイルを生成する。
今回生成したのは以下の通り。

packages.txt
extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2
extras;google;google_play_services
platforms;android-28
extras;android;m2repository
platform-tools
extras;android;gapid;3
patcher;v4
build-tools;28.0.2

各種Storageバケットの作成

cacheやビルドファイル、ライセンス用の保存領域としてCloud Storageを使用するため、必要なバケットを作成する。今回作成したのは以下の通り。

Bucket name 用途
android_build_caches ビルドキャッシュ用
debug_build_etet デバッグビルド(apk)保存用
licenses_bucket Android SDKライセンスディレクトリ用

Android SDKライセンスをGCP Storageに上げる

以下のコマンドで、SDKライセンスをGCP Storageにアップロードする。

$ gsutil rsync -d [ANDROID_SDK_HOME]/licenses/ gs://[_LICENSES_BUCKET]

※ もしCloud Build上でビルドしたときに、「You have not accepted the license agreements of the following SDK components」と出てしまった場合は以下のコマンドでライセンス同意をして再度licensesディレクトリをrsyncさせる

$ [path_to_sdk]/tools/bin/sdkmanager --licenses

コンテナビルドをsubmitする

$ gcloud builds submit --config cloudbuild.yaml . --substitutions=_ANDROID_SDK_LICENSE=$ANDROID_SDK_LICENSE

CloudBuildを見てみると、ビルドプロセスが走っていることがわかる。
Screen Shot 2018-11-18 at 16.55.13.png

buildがパスすると、Container Registryにイメージがpushされていることが確認できる。
"android-builder"という名前でイメージが作成出来ている。
Screen Shot 2018-11-18 at 16.58.28.png

これでSource Repositoryにpushしたコードを作成したDockerイメージでビルドすることができるようになる。

Dockerfileは以下の通り。

Dockerfile
FROM openjdk:8-slim
ENV DEBIAN_FRONTEND noninteractive
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'

ARG ANDROID_SDK_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip"
ARG ANDROID_SDK_LICENSE
ARG ANDROID_BUILD_CACHE
ARG GRADLE_DISTRIBUTION="https://services.gradle.org/distributions/gradle-4.6-bin.zip"

ENV GRADLE_BUILD_CACHE=${ANDROID_BUILD_CACHE}
ENV DOCKER_ANDROID_DISPLAY_NAME mobileci-docker
ENV GRADLE_TARGETS="assembleDebug"
ENV GRADLE_HOME=/usr/local/gradle-4.6

ENV ANDROID_HOME /android-sdk
ENV PATH ${ANDROID_HOME}/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools/bin:/bin:$PATH
#Install underlying development packages and languages
ADD packages.txt .
COPY gradle-build /bin
COPY licenses licenses

    # Prepare to install packages
RUN apt-get update && \
    apt-get install -y locales && \
    sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && \
    locale-gen && \
    dpkg-reconfigure locales && \
    apt-get install -y apt-utils && \
    dpkg --add-architecture i386 && \
    # install packages
    apt-get install -qq -y \
    software-properties-common \
    curl \
    wget \
    build-essential \
    zip \
    unzip \
    --no-install-recommends && \
    # Install gradle
    wget --quiet $GRADLE_DISTRIBUTION && \
    unzip -qq  gradle-4.6-bin.zip -d /usr/local/ && \
    ln -s /usr/local/gradle-4.6/bin/gradle /usr/local/bin/gradle && \
    # Install android sdk
    mkdir -p /root/.android && touch /root/.android/repositories.cfg && \
    wget --quiet --output-document=android-sdk.zip ${ANDROID_SDK_TOOLS_URL} && \
    unzip -qq android-sdk.zip -d $ANDROID_HOME && \
    cp -r licenses /$ANDROID_HOME/licenses && \
    echo ${ANDROID_SDK_LICENSE} >> $ANDROID_HOME/licenses/android-sdk-license && \
    echo "sdk.dir=${ANDROID_HOME}" > android_local.properties && \
    sdkmanager --package_file=packages.txt && \
    chmod -R +x bin && \
    rm $ANDROID_HOME/licenses/* && \
    keytool -genkey -v -keystore /root/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" && \
    keytool -list -v -keystore /root/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
CMD ["sh", "-c", "build-debug", "${GRADLE_TARGETS}"]

2. 任意のAndroidプロジェクトを作成する

今回は、簡単なHello Worldを表示するだけのプロジェクトを作成するだけ。

3. Cloud Source Repositoryにリポジトリを作成し、Cloud Buildと連携させる

cloudbuild.yamlファイルの作成

Androidプロジェクト直下にcloudbuild.yamlという名前でビルド用のファイルを作成する。
build cacheなどは、前述で作成したStorageバケット名を指定する。
(注) githubのサンプルでは、gs://が指定されていないため、ビルド成功してもapkやキャッシュがStorgaeに保存されなかった。

cloudbuild.yaml
steps:
#Copy the build cache locally, if it exists.
- name: 'gcr.io/cloud-builders/gsutil'
  args: ['rsync', 'gs://${_ANDROID_BUILD_CACHE}/', '/build_cache']
  id: copy_build_cache_local
  volumes:
    - name: 'build_cache'
      path: '/build_cache'
#Android build and distribute with gradle
- name: 'gcr.io/$PROJECT_ID/android-builder'
  args: ['gradle-build','${_ANDROID_SDK_LICENSE}','assembleDebug']
  id: gradle_build
  waitFor:
    - copy_build_cache_local
  volumes:
    - name: 'build_cache'
      path: '/build_cache'
#Copy debug apk to cloud storage bucket
- name: 'gcr.io/cloud-builders/gsutil'
  args: ['-q','cp', 'app/build/outputs/apk/debug/app-debug.apk', 'gs://${_DEBUG_BUILD_BUCKET}/$PROJECT_ID-$BRANCH_NAME-$SHORT_SHA-debug.apk']
  waitFor:
    - gradle_build
#Repopulate the build-cache
- name: 'gcr.io/cloud-builders/gsutil'
  args: ['-q','cp', '/build_cache/dot_gradle.zip', 'gs://${_ANDROID_BUILD_CACHE}']
  waitFor:
    - gradle_build
  volumes:
    - name: 'build_cache'
      path: '/build_cache'
timeout: 1200s

Cloud BuildでTriggerの設定する

GCPコンソール → トリガー → トリガーを追加 でBuildのトリガーを追加する。
今回のサンプルでは、masterブランチへのpushでBuildされるように設定。
以下が、設定した内容。
Screen Shot 2018-11-24 at 19.57.49.png

4. ソースコードをpush

ソースコードがmasterにpushされると、Cloud BuildのTriggerにより自動的にビルドが実行される。
数分待つと、ビルドが完了しビルドステップが全て成功している状態となっている。
Screen Shot 2018-11-18 at 18.39.24.png

ビルドで何をやってるか確認してみる

cloudbuild.ymlのstepに従ってビルドが実行されているのがわかる。

1.キャッシュ読み込み

Building synchronization state...
Starting synchronization...
Copying gs://android_build_caches/dot_gradle.zip...
 [0 files][ 0.0 B/108.2 MiB] 
 [0 files][ 64.2 MiB/108.2 MiB] 
 [1 files][108.2 MiB/108.2 MiB] 
Operation completed over 1 objects/108.2 MiB.

2.gradleのビルド

~ 略 ~
:app:transformDexArchiveWithExternalLibsDexMergerForDebug
:app:transformDexArchiveWithDexMergerForDebug
:app:mergeDebugJniLibFolders
:app:transformNativeLibsWithMergeJniLibsForDebug
:app:checkDebugLibraries
:app:processDebugJavaRes NO-SOURCE
:app:transformResourcesWithMergeJavaResForDebug
:app:validateSigningDebug
:app:packageDebug
:app:assembleDebug

BUILD SUCCESSFUL in 1m 25s
28 actionable tasks: 28 executed
Exit status is: 0

3.出来上がったapkをStorageに保存

.gradle/ディレクトリがzipファイルとして上がっていることが確認できる。
Screen Shot 2018-11-24 at 21.34.02.png

4.ビルドキャッシュをStorageに保存

apkファイルが上がっていることが確認できる。
Screen Shot 2018-11-24 at 21.34.45.png

gradleキャッシュがない場合とある場合のビルド速度の違い

dot_gradle.zipファイルがある場合は、余計な依存関係のダウンロードが必要なくなるため、ビルド時間が短くなる。
実際に試してみると、1分ほどビルド時間が短くなっているのがわかる。

キャッシュなし キャッシュあり
Screen Shot 2018-11-24 at 21.40.40.png Screen Shot 2018-11-24 at 21.40.50.png

次にやること

ビルドしてapkのアップロードまで出来たので、次はテストコードを書いてUT実行 → Fabricベータ公開までできそうなので試してみたい。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away