Jenkins による Android アプリの CI 環境を Amazon EC2 を使ってマスター・スレーブ構成で構築する
はじめに
AWS 上に Jenkins による Android アプリの CI 環境を構築する機会がありましたので、記録として残しておきます。
Jenkins 単体であれば下記の記事のままで大体OKなのですが、
Amazon Linux に Android エミュレータを入れるには色々と難があります。そこで、Android アプリのビルドやテストを行うマシンを Ubuntu を使った、マスター・スレーブ構成の CI 環境を構築してみました。
下図のような構成となる CI 環境を構築します。
マスターマシンの構築
EC2 の起動
次の記事の通り、普通に Amazon Linux のインスタンスを起動します。
キーペアはダウンロードしておきます。EC2 インスタンス起動後、SSH 接続で作業を進めます。
$ ssh -i <keypair name>.pem ec2-user@<ip address>
JDK のインストール
JDK のインストールは scp でやる方法もありますが、インスタンス内で完結させたかったので、下記を参考にリンク貼る方法でインストールします。
$ sudo wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u25-b17/jdk-8u25-linux-x64.rpm" $ sudo rpm -ivh jdk-8u25-linux-x64.rpm $ sudo ln -s /usr/java/jdk1.8.0_25/bin/java /usr/bin/java8 $ sudo rm /usr/bin/java $ sudo ln -s /usr/bin/java8 /usr/bin/java
これで、JDK のバージョンは 1.8 になっているはずです。
$ java -version java version "1.8.0_25"
Git のインストール
SCM に Git を使うので、インストールしておきます。
$ sudo yum -y install git
Jenkins のインストール
Jenkins は yum リポジトリにインポートしてからインストールします。現時点での最新バージョンは 2.9 です(2016/06/20 現在)。
$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo $ sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key $ sudo yum -y install jenkins
インストールが終わったら、自動起動登録しつつ起動しておきましょう。
$ sudo chkconfig jenkins on $ sudo service jenkins start Starting Jenkins [ OK ]
Jenkins は最近 Version 2 になりました。セットアップ方法がちょっと変わっています。本記事は構築が主の目的なので、セットアップ方法については割愛します。
スレーブマシンの構築
EC2 の起動
スレーブマシンは、Ubuntu サーバーとして構築します。
詳細な設定については割愛します。概ね次のような設定でインスタンスを起動します。
- Ubuntu サーバーにする (エミュレータを起動するため)
- t2.medium くらいにする (エミュレータを実行するのでそこそこ必要なため)
- EBS は 100 GB とかにする (Android SDK が結構ディスク容量を食うため)
マスターマシンと同様に、キーペアはダウンロードしておきます。EC2 インスタンス起動後、SSH 接続で作業を進めます。Amazon Linux サーバーに SSH 接続する場合とユーザーが異なるので注意してください。Ubuntu サーバーの場合は ubuntu
ユーザーで SSH 接続します。
$ ssh -i <keypair name>.pem ubuntu@<ip address>
JDK のインストール
Open JDK をインストールする方法が簡単です。
$ sudo add-apt-repository ppa:openjdk-r/ppa $ sudo apt-get update $ sudo apt-get install openjdk-8-jdk
これで、JDK がインストールされているはずです。
$ java -version openjdk version "1.8.0_91" OpenJDK Runtime Environment (build 1.8.0_91-8u91-b14-0ubuntu4~14.04-b14) OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)
Git のインストール
SCM に Git を使うので、インストールしておきます。
$ sudo apt-get install git
Android SDK のインストール
最新の SDK のダウンロード URL は、下記のページで公開されています。
EC2 内で最新の SDK をダウンロードし、展開します。現時点での最新バージョンは 24.4.1 です(2016/06/20 現在)。
$ wget https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz $ sudo tar -xvzof android-sdk_r24.4.1-linux.tgz $ sudo mv android-sdk-linux/ /usr/local/
Android SDK のセットアップを行うため、環境変数を設定します。必須ではありませんが、コマンド入力を楽にするために行います。以下を .bash_profile
に書いておきましょう。
$ export PATH=$PATH:/usr/local/android-sdk-linux/tools/ $ export ANDROID_HOME=/usr/local/android-sdk-linux/
Android アプリのビルドに必要な Build-tools などを入れていきます。まずはパッケージをリストアップしましょう。
$ android list sdk --all --extended
上記コマンドでダウンロードに失敗する場合は、SSL/TLS 通信が実行できていない可能性があります。下記コマンドを実行してから再度行ってみてください。
$ /var/lib/dpkg/info/ca-certificates-java.postinst configure
以下のパッケージを入れます。
- Android SDK Build-tools
- SDK Platform
- Android Support Repository
- Android Support Library
これらのパッケージを入れるには update
コマンドを使います。主要なオプションは次の通りです。--all
を付けないと古い SDK バージョンが対象にならないので、注意してください。
オプション | オプション(フル) | 意味 |
---|---|---|
-a |
--all |
全てのパッケージを対象にする |
-u |
--no-ui |
GUI を使用しない |
-t |
--filter |
Filter してインストールする |
例えば、次のようにフィルタリングしてインストールします。
$ android update sdk -a -u -t platform,platform-tools,build-tools-24.0.0,extra-android-support,extra-android-m2repository
以上で、Android アプリのビルド、テストが行える環境が整いました。念のため、適当な Android アプリプロジェクトを clone し、ビルドできるか試しておきましょう。
$ git clone https://github.com/suwa-yuki/AndroidDemoApp.git && AndroidDemoApp $ ./gradlew build ...省略... :app:assembleReleaseUnitTest :app:testReleaseUnitTest :app:test :app:check :app:build BUILD SUCCESSFUL Total time: 26.777 secs This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.10/userguide/gradle_daemon.html
Android エミュレータのセットアップ
Android エミュレータ を起動するためには AVD (Android Virtual Device) を用意する必要があります。
AVD の作成及びエミュレータの起動は「Android Emulator Plugin」を使うことで Jenkins のジョブに集約することができますが、スレーブが生まれ変わったら SDK のダウンロードからやり直し、AVD も作り直しになってしまうのでスピード感が出ません。
そのような場合は、スレーブマシンの AMI を作る前に、AVD の作成までを行っておき、ジョブで即座に起動できるようにします。
次のコマンドは armeabi-v7a
のイメージをダウンロードした後に、適当な AVD (testing
という名称) を作成しています。
$ android update sdk -u -t sys-img-armeabi-v7a-android-23 $ android create avd -f -a -c 32M -s WVGA800 -n testing -t android-23 --abi armeabi-v7a
Jenkins Slave としての準備
ジョブのワークスペースとなるディレクトリを予め用意しておきます。
$ sudo mkdir /var/lib/jenkins && sudo chmod 755 /var/lib/jenkins
AMI にする
仕上げとして、構築した EC2 インスタンスを AMI にします。
AMI の ID を控えておきましょう。
マスターマシンとスレーブマシンを連係させる
次に、マスターマシンの設定で、スレーブマシンを管理する設定を行います。今回は「Amazon EC2 Plugin」を使います。
このプラグインを使うと、ジョブにキューが登録されたタイミングでスレーブとなる EC2 インスタンスを起動し、その中でジョブの処理を実行してくれます。並行ジョブ数が多いと、その分インスタンスもスケールアウトしてくれます。反対に、ジョブが無くなったらスレーブを Terminate してくれます。
プラグインをインストールした後、「Jenkins の管理」から「システムの設定」を開き、一番下にある「クラウド」で「Amazon EC2」を追加します。
設定は次のとおりです。まずは EC2 の設定です。
①は新しく追加する設定の名前です。ここは EC2 インスタンスに付く名前ではなく、Jenkins 上の呼び名です。
②は EC2 インスタンスに付与する AWS の認証情報を EC2 インスタンスプロフィールから取得する設定です。EC2 の起動及び終了できるポリシーがマスターマシン自体に付与されていればOKです。
③は EC2 インスタンスを起動するリージョンです。テストですので、なるべく安価に運用できるリージョンの方が良いと思います。
④は EC2 インスタンスに使用するキーペアを入力します。
⑤では疎通確認のテストを実施することができます。ここまで入力できたら試しに押してみましょう。
続けて、AMI の設定です。
⑥は AMI 設定の説明文です。こちらは AMI の設定に反映されるものなどではなく、Jenkins 上で識別しやすくするための説明文です。
⑦は AMI の ID です。先ほど作成した Android の環境構築を行った EC2 インスタンスを元にした AMI の ID を設定します。
⑧は⑦で設定した AMI が Jenkins 上で参照可能かどうか確認するためのテストです。一度行っておきましょう。
⑨は EC2 インスタンスのインスタンスタイプです。デフォルトでは t1.micro
が設定されているので注意してください *1。ここでは t2.medium
としています。Android エミュレータを起動する必要があるため、そこそこ必要です。
⑩は AZ の設定です。ここでは、マスターマシンと同じ AZ に配置しています。
⑪はセキュリティグループの設定です。ここも、マスターマシンと同じセキュリティグループにしていますが、マスターマシンのみ許可する方が適切だと思います。
⑫はスレーブマシン上で Jenkins ジョブの各ファイルを展開するディレクトリの指定です。前述している /var/lib/jenkins
を設定しています。
⑬は SSH 接続するユーザの指定です。Ubuntu の場合は ubuntu
になります。
⑭は SSH 接続の詳細設定です。ルートコマンド実行のためのプレフィックスは sudo
、ポートは 22
です。
⑮はジョブに設定する、ジョブを実行するラベル式の設定です。あとでジョブ設定で使います。
⑯は EC2 インスタンスのスレーブマシンとしての扱いの設定です。ここでは「このマシーンを特定ジョブ専用にする」を設定しています。つまり、ジョブ実行によって起動した EC2 インスタンスは、対象のジョブでしか使われません。別のジョブを実行するときには新しく EC2 インスタンスが起動するようになります。
あとは、高度な設定です。ここで行う設定は少しだけです。
⑰は VPC のサブネットの設定です。ここではマスターマシンと同じサブネットにしていますが。VPC の中に閉じた EC2 インスタンスにすることもできます。その方がよりセキュアですね。
⑱は EC2 インスタンスに付与する Label の設定です。最低限 Name
だけ付けておいた方が良いでしょう。
CI する
環境構築が整ったところで、実際に CI してみましょう。CI を行う対象として、シンプルな Android アプリプロジェクトを作成しました。GitHub で公開しています。
ジョブの設定は、要点だけ解説します。
「実行するノードの制限」は、スレーブマシンを使う時に行う設定です。Jenkins の AMI 設定の Labels (前述の⑮) で設定したラベル式を設定します。
続いてビルド処理に「シェルの実行」を追加します。
次のようなコマンドを入力します。
$ export ANDROID_HOME=/usr/local/android-sdk-linux $ export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools $ emulator -avd testing -no-snapshot-load -no-snapshot-save -no-window -noaudio & $ ./wait-for-emulator.sh $ ./gradlew connectedCheck
なお、Android エミュレータが起動するまでの待ち合わせを行うスクリプトは次のようになっています。
#!/bin/bash export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH" while true; do BOOTUP=$(adb shell getprop init.svc.bootanim | grep -oe '[a-z]\+') if [[ "$BOOTUP" = "running" ]]; then break fi echo "Got: '$BOOTUP', waiting for 'running'" sleep 5 done
こちらのシェルスクリプトは こちら で公開されている Circle CI のダミープロジェクトの内容を参考にしています *2。複数の Android エミュレータを対象とする場合は こちら を参照してください。
それでは、CI を回してみましょう。スレーブが立ち上がり、ジョブが実行できていることが確認できます。
まとめ
AWS から CI as a Service 出てくれないかな。。
参考
Jenkins について
- EC2にJenkinsによるCI環境を作成する | Developers.IO
- Yukiの枝折: Android+GradleなCI環境構築
- Jenkinsを利用したAndroidコード品質の見える化 - Qiita
- JenkinsのSlaveをEC2で動作させる - Devlog
Android SDK について
- Android SDKのコマンドラインオプション (android update sdk) - Qiita
- Control the Emulator from the Command Line | Android Studio
- CI用にヘッドレスなAndroidエミュレータを複数台立ち上げるシェルスクリプト - Qiita
Ubuntu について
- How to Install OpenJDK 8 in Ubuntu 14.04 & 12.04 LTS | UbuntuHandbook
- Connection problems with SSL/TLS peers in Java on Ubuntu Server 14.12 (with fix!) | Justus Beyer
- Ubuntu のAndroid Studio にエミュレータを設定する - Qiita
- Test Android Applications - CircleCI