Androidアプリ開発向けの横断CI環境

こんにちは、スマートデバイス基盤チームの段です。

スマートデバイス基盤チームでは、リクルートライフスタイルのスマホアプリ開発における開発標準・品質指標の策定、新技術の検証及び横断推進、また、可視化及びCIシステムの運用改善など横断ミッションを担っています。

継続的インテグレーション(CI)はアプリ開発プロセスにおける重要な一環で、そのシステム運用改善を横断チームで実施するか各サービス側で実施するかの選択肢があると思いますが、弊社ではスマートデバイス基盤チームが担当しています。日頃の安定運用も勿論ですが、サービス側要件の柔軟対応、CIノウハウの属人化回避、運用工数削減などを意識した改善活動を積極的に行っています。

本記事では、現在運用しているAndroid向けのCI環境をご紹介します。

全体構成

Ci

  1. ビルド処理〜配布はジョブ毎にDockerコンテナで実施
  2. Jenkins Pipelineを導入し、コードベースでジョブ管理する
  3. Github、Slack、Deploygate、SonarQubeなどとの外部連携

機能拡張する為に多くのPluginを導入

機能拡張する為のPluginが多数存在することはJenkinsの大きな特徴で、現在導入されている一部のPluginも紹介させていただきます。各Pluginの機能をより詳細に知りたい方はJenkins Pluginsをご参照ください。

1. 静的解析

Plugin 機能概要
Static Analysis Collector 静的解析の結果を表示
Dashboard View ダッシュボードビュー、Static Analysis Collectorの結果もジョブを跨いだ表示が可能
JaCoCo コードカバレッジレポート
Checkstyle コーディング規約チェック
FindBugs 不具合が発生しそうなコードの検出

2. 外部連携

Plugin 機能概要
Slack Notification ビルド関連の通知をSlackに送信する
Google Play Android Publisher Google PlayにビルドできたAPKをアップロードする
Google OAuth Credentials Google APIsを利用する為のOAuth認証機能を提供
Nexus Artifact Uploader Nexus連携

3. Jenkins画面のカスタマイズ

Plugin 機能概要
Build Trigger Badge ビルド履歴にトリガーをアイコンで分別できるようにする
categorized-view 大量ジョブがある場合、ジョブのグルーピングすることが可能
View Job Filters ジョブのフィルタ機能を提供、条件指定して特定ステータスのジョブビューを作成可能
Simple Theme トップバーの色設定など見た目の変更
Job Configuration History ジョブごとのJenkinsの設定変更履歴を確認可能にする
Sidebar Link Jenkinsのサイドバーにリンク追加可能にする

ビルド処理に利用するDockerイメージを用意

アプリビルドするための基本的な環境をDockerイメージとして用意し、サービス側で利用及び拡張できるようにしています。また、イメージの作成/最新化もJenkinsジョブとして登録していて、ジョブ実行するだけでできるようになっています。

Dockerfileの内部処理(一部抜粋)

1. Java実行環境のインストール

1
2
3
4
5
6
7
8
9
10
11
RUN \
# Oracle JDK8
wget -O jdk.tar.gz --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" \
http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz && \
tar zxf jdk.tar.gz && rm jdk.tar.gz && \
ln -s /opt/jdk1.8.0_161 /opt/oraclejdk8 && \
# Maven 3.3
wget -O maven.tar.gz http://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz && \
tar zxf maven.tar.gz && rm maven.tar.gz && \
ln -s /opt/apache-maven-3.3.9 /opt/maven-3.3 && \
chmod -R 755 /opt/*

2. emulator起動に必要なライブラリ群のインストール

1
2
3
4
5
6
7
8
9
10
11
RUN \
dpkg --add-architecture i386 && \
apt-get update && \
apt-get install -y --force-yes libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1 libqt5widgets5 && \
libpulse0 && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* /var/cache/debconf/*-old
RUN \
apt-get update && \
apt-get -y --no-install-recommends install imagemagick && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* /var/cache/debconf/*-old

3. Android SDKのインストール

(※android-sdk-download.bash、android-sdk-download.bash、android-sdk-licenses.bashは内製スクリプト)

1
2
3
4
5
6
7
8
ENV ANDROID_HOME /opt/android-sdk-linux
ENV ANDROID_SDK_ROOT ${ANDROID_HOME}
ENV ANDROID_NDK_HOME ${ANDROID_HOME}/ndk-bundle
RUN \
/bin/bash android-sdk-download.bash && \
/bin/bash android-sdk-update.bash /opt/oraclejdk8 && \
/bin/bash android-sdk-licenses.bash ${ANDROID_HOME}/licenses && \
chmod -R 777 /opt/android-sdk-linux

Jenkins Pipelineの導入

Pipeline導入前のビルドスクリプトがスマートデバイス基盤チーム内に閉じていて、サービス側に対してはブラックボックス化されていました。その為、下記課題がありました。

 ・エラー発生した場合、サービス側で原因究明できず、且つ問題発生箇所が可視化できていない為、問題解決に時間が掛かる

 ・CI関連ノウハウの属人化

 ・サービス側で提供された機能しか利用できず、独自設定・処理を追加できる柔軟性に欠ける

 Pipeline適用とともに、スマートデバイス基盤チーム側はビルド環境(インフラ、Jenkins、導入Pluginなど)の構築・進化及び技術サポートを担い、サービス側は開発フローに沿ってスクリプトを自由に組み込めるような形にしました。また、Pipeline化により、ビルドスクリプトの履歴管理ができ、ビルド処理がステージごとに分割されることにより可視化ができ、問題が短時間で解決できるようになりました。

Jenkinsfileのサンプルコード:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
node("docker") {
    try {
        stage("Init") {
            //初期化処理、Shared Library読み込み
        }
        stage("Checkout") {
            //CheckOut
        }
        //ビルド処理〜配布
        stage("Container") {
            withDockerContainer(args: CONTAINER_NAME + environments.getENV(JAVA), image: IMAGE) {
                environments.setENV(JAVA)
                stage("Build") {
                    //ビルド処理
                }
                stage("Tests") {
                    //テスト処理
                }
                stage("StaticAnalysis") {
                    //静的解析
                }
                stage("Deploy") {
                    //配布
                }
            }
        }
        stage("Archive") {
            //成果物保存
        }
    } catch (Exception e) {
        logger.printStackTrace(e)
        currentBuild.result = "FAILURE"
    } finally {
        if (!currentBuild.resultIsBetterOrEqualTo("SUCCESS")) {
            slack.color = "danger"
        }
        notification.sendSlack(slack)
    }
}

共通処理をShared Libraryとして提供

 ビルドスクリプトの作成・管理はサービス側で持つことになりますが、共通的な処理の実装は横断で提供すべきところで、基盤チームからShared Libraryを実装・ソース公開しました。下記は提供しているものの一覧となります。

スクリプト 機能
jobProperties ジョブの成果物の保持数及び保持日数を設定
environments ビルドコンテナの環境変数設定
gradlew gradlewタスク実行
buildVariant ビルドモード、配布モード取得
emulator エミュレーターの作成・起動
analysis 静的解析、解析結果収集、SonarQube解析などを実施
sonarQube SonarQube解析、SonarQubeの解析モード設定取得
deploy DeployGateにアップロードする
notification Slack通知
logger Exception発生時のログをコンソールに出力
gitComments 実行中のブランチの最新のコミットコメントを取得

終わりに

今回は基盤チームの日頃の運用改善事例としてAndroidアプリ開発のCI環境を紹介させていただきました。Pipelineの詳細、Dockerの一般的な知識は割愛させていただきましたが、少しでもご参考になればと思います。

次回はiOSアプリ開発のCI環境も紹介させていただきたいと思います。