BitbucketトリガでEC2上のJenkinsからDockerを立ち上げてAndroidのビルドをしてみた

# はじめに
今更Dockerデビューしたので、Androidアプリをビルドするために使用してみました。
今回作成するビルド環境は以下のような構成です。

<img width="450" alt="スクリーンショット 2018-04-30 12.48.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/99570/ecaad518-1696-1271-9efe-a161e78d01d0.png">


* Bitbucketのリポジトリへのpushを契機にEC2上のJenkinsジョブを実行する
* JenkinsからAndroidビルド環境を持つDockerコンテナを立ち上げビルド実行
* JenkinsがcloneしてきたディレクトリをDockerコンテナからも参照する
* ビルドが終わったらコンテナを削除する

この記事では以下の範囲はカバーしないので予め設定しておいてください。
EC2インスタンスはAmazon Linuxで作成しています。

* EC2インスタンスを立てる作業
* EC2にssh接続する環境構築


## EC2上にDockerを導入
まずはEC2上にDockerを導入します。Dockerイメージの作成などは後ほど。

```
# Dockerのインストール
$sudo yum update
$sudo yum install -y docker

# Dockerサービスの起動
$sudo service docker start
```

インストール後、ec2-userでdockerコマンド実行時にsudoをつけずに済むように以下を実行。
その後ログアウト/ログインしなおしてください。

```
$ sudo usermod -a -G docker ec2-user
```

【参考資料】

[Mac OS XでAWS EC2のLinuxインスタンスにssh接続する](https://qiitacom/tomoktan_cansell/items/5c83f090f958ca35b1d2)

[AWSのEC2でDockerを試してみる](https://qiita.com/yumatsud/items/33bc22f7d8f640a286f4)


## EC2上にJenkinsを導入
次にJenkinsを導入します。
以下にまとまっている手順を実行すればセットアップできるので一通り実施してください。
(ポートは8080のままいきます)

[AWSにJenkins環境を構築する](https://qiita.com/hitomatagi/items/4bf578b46c525fc01514)

上記を行なった後、ブラウザから`http://[EC2インスタンスのIPアドレス]:8080/`にアクセス
するとJenkinsの初期設定ページが出るので指示に従って設定完了してください。

## EC2上にGitインストール
JenkinsでBitbucketからcloneする際にエラーが出るので、
もし入っていなければ以下でインストールしてください。


```
$sudo yum install git
```

## JenkinsとBitbucketの連携設定
以下にまとまっている手順の1〜5の「ビルド・トリガ」でSCMをポーリングを選択」までを実施してください。
Jenkins用の鍵の生成とBitbucket との連携設定を一通り行うことができます。

[BitbucketにpushされたらJenkinsで自動デプロイする](http://miraicraft.co.jp/blog/2014/08/20/bitbucket_jenkins/)


## DockerfileとDockerイメージ作成
### Dockerfile作成

EC2上の任意の場所にDockerfileを作成してイメージをビルドします。
今回はec2-userのホームディレクトリで作業しています。

```
$mkdir docker
$cd docker
$vim Dockerfile
```

```Dockerfile
ROM ubuntu:16.04

# Android 用のパッケージ
RUN apt-get update -y && \
  apt-get upgrade -y && \
  apt-get install git-core gnupg flex bison gperf build-essential zip curl \
    zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev \
    x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev \
    libxml2-utils xsltproc unzip -y

# Java 用のパッケージ
RUN apt-get install software-properties-common python-software-properties -y

# JDKインストール
RUN apt-get -y update
RUN apt-get -y install openjdk-8-jdk || true
RUN apt-get -y install openjdk-8-jdk

# Android SDKをインストール 
RUN cd /usr/local/ && \
  curl -L -O \
  https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \
  unzip sdk-tools-linux-4333796.zip && \
  /usr/local/tools/bin/sdkmanager --update && \
  /usr/local/tools/bin/sdkmanager "platform-tools" && \
  /usr/local/tools/bin/sdkmanager "build-tools;26.0.2" && \
  /usr/local/tools/bin/sdkmanager "platforms;android-27" && \
  /usr/local/tools/bin/sdkmanager "extras;android;m2repository" && \
  /usr/local/tools/bin/sdkmanager "extras;google;m2repository" && \
  /usr/local/tools/bin/sdkmanager "extras;google;google_play_services" && \
  yes | /usr/local/tools/bin/sdkmanager --licenses && \
  rm -rf /usr/local/sdk-tools-linux-4333796.zip

# 環境変数設定
ENV ANDROID_HOME /usr/local
ENV PATH $PATH:$ANDROID_HOME/tools
ENV PATH $PATH:$ANDROID_HOME/platform-tools
```

ubuntu16.04ベースでイメージを作成します。
build-toolsとplatformsのバージョンは自分のビルド環境に合わせて修正してください。

特に気をつける箇所は`yes | /usr/local/tools/bin/sdkmanager --licenses`の所です。
この記述で`docker run`時にSDKの各種パッケージのライセンスに同意した状態になります。
パッケージのライセンスに同意すると/usr/local以下にlicensesというディレクトリが作成され、
その中にライセンス同意情報ファイル一式が作成されます。

もう一つの気をつける箇所は`ENV ANDROID_HOME /usr/local`の所です。
ビルド時にはANDROID_HOME以下にlicensesディレクトリがある前提でチェックされるため、
この設定が違うとライセンスに同意していない旨のビルドエラーが出てしまいます。

### Dockerイメージ作成
Dockerfileができたら、以下コマンドを実行してandroid-ciという名前でDockerイメージを作成します。

```
$cd ..
$sudo docker build -t android-ci docker/
```

Dockerイメージ作成に成功した場合、`docker images`コマンドで以下のように表示されます。

```
$sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
android-ci          latest              db6359ec1c3f        19 seconds ago      1.06GB
ubuntu              16.04               0b1edfbffd27        42 hours ago        113MB
```

## ビルドスクリプト記述
`docker run`の時にDockerコンテナ内から実行するビルドスクリプトbuild.shを記述します。
JenkinsでCloneすると`/var/lib/jenkins/workspace`下にプロジェクトができるので、
Clone後にプロジェクトディレクトリにコピーしやすいように`/var/lib/jenkins/workspace`下にbuild.shを作成します。

```build.sh
#!/bin/bash

./gradlew clean && \
./gradlew dependencies && \
./gradlew assembleDebug
```

build.shを保存したら実行権限を付けておきます。

```
$sudo chmod 777 build.sh 
```

## Jenkinsのビルドシェル記述
Jenkinsでdockerコマンドを実行するときsudoしなくて良いように以下を実行します。

```
$sudo usermod -a -G docker jenkins
```

Jenkinsジョブ設定で[ビルド手順の追加]->[シェルの実行]を選択しシェルスクリプトに以下を記述する。

```
cd ${WORKSPACE}
cp ../build.sh ./
chmod 777 gradlew
docker run -i --rm -v ${WORKSPACE}:/workspace -w /workspace android-ci sh build.sh
```

上のスクリプトでは`dockr run`を以下の設定で実行しています。

* `docker run`時に--rmを指定することでbuild.sh終了後にコンテナが削除されるようにする
* ホストマシン(EC2)のWORKSPACE(`/var/lib/jenkins/workspace/プロジェクトディレクトリ`)をDockerコンテナ上の/workspaceにマッピングして共有ディレクトリとして使用する
* android-ciイメージからコンテナを作成し、その上でbuild.shを実行する

ここまで設定したら、Jenkinsジョブで設定したリポジトリ・ブランチに変更をpushしてみましょう。
pushを契機にJenkinsでジョブが実行され、ビルド成功することが確認できるはずです。

# おわりに
Docker、環境使い捨てできるのがとても便利だなと思いました。
ずぼらで環境汚しがちな人間なので積極的に活用していきたいところです。
プルリク契機でビルドする方法も調べてみようと思います。