AndroidアプリをDockerでビルドしてみた

Posted by rhoboro on 2016-07-30

タイトルの通り、いま作っているAndroidアプリをビルドするためにDockerを使ってみました。

きっかけ

ビルドサーバーの環境を汚したくなかったからです。

複数のアプリを並行して開発していると、ひとつのマシンで複数のアプリをビルドするというのが普通だと思います。 CI用のビルドサーバー、そこでビルドされるすべてのアプリを自分一人で管理しているのであれば大きな問題はないと思いますが、 複数チームで1台のビルドサーバーを共有する場合、ビルドマシンの環境をできるだけ変更したくない状況がでてきます。

そういう状況では、ビルドにDockerを使うと、ホストの環境への影響を最小限に食い止めることができます。

Dockerを使うメリット

Dockerを使うとホストの環境を汚さないという点以外にもいくつかメリットがあります。

  1. ビルドに必要なSDKやbuild-toolsのバージョンがわかりやすくなる
    • Gradle管理されていないライブラリを複数含んでいる場合、build.gradleを一つ一つ見るのも大変
  2. みんな同じ環境でバイナリを作れる
    • CI環境を整えていてもビルドされるのはリモートリポジトリへのcommitや定期的なビルドのみだと思います
    • Dockerでビルドできるようにしておけば、ローカルでもCI環境と同じ環境でビルドできます

DockerでAndroidアプリのビルドをする

今回はCI環境で行っていた内容を踏まえ、下記をローカルでもできるようにしてみました。

  1. Dockerイメージの作成
  2. Dockerでコンテナを起動(以下コンテナ内での処理)
  3. リモートリポジトリからソースコードを取得
  4. 特定のブランチにチェックアウト
  5. ビルド
  6. できたapkを成果物としてホストマシンに保存

これらを実現するために最終的に下記の様な構成としました。

  • Dockerfile
    • Infrastructure as a code ですね
  • app-build.sh
    • 手順3-5を行うシェルスクリプト
  • netrc
    • ソースコードを取得するアカウント情報を記載します
    • 今回は諸事情によりID/PASSをファイルに書き出していますが、SSHが使える環境ならソースコード取得用ユーザーを作成して鍵を登録したほうがいいと思います(もともとGitHub管理ではない別プロジェクトで作ったので)
  • Makefile
    • 上記手順すべてを自動化するためにMakefileを使いました

ちなみに今回作った上記のファイルはこちらにあります。

作り方、ポイント、はまった点

Dockerfileの作成手順ですが、私は docker exec -it ubuntu /bin/bash でコンテナに入って実際に環境を構築していきながら、 叩いたコマンドを一つ一つDockerfileにメモして、最後にできあがったDockerfileを整理して作成しました。
Dockerfileの中身はすごく単純なので見ていただければわかると思います。
ビルド時にAAPTが失敗したのですが、これは lib32stdc++6lib32z1 をインストールすると解消しました。

Dockerfile作成時のポイントととして、できるだけ && でつないで中間生成物を減らすのをオススメします。 Dockerfileでは1つのRUNやCOPYなどコマンドごとにDockerイメージのコミットが行われるため、 ファイル保存と削除を別コマンドで行うと、保存された履歴が残るため、最終的なイメージサイズは大きくなるようです。

Dockerfileの中身はこちら。 必要なSDKやbuild-toolsのバージョンを変えるだけで大抵のAndroidプロジェクトで使えると思います。

FROM ubuntu:16.04

RUN apt-get update -y && apt-get upgrade -y && \
    apt-get install -y openjdk-8-jre openjdk-8-jdk lib32stdc++6 lib32z1 wget git
RUN cd && wget https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz && tar -zxvf android-sdk_r24.4.1-linux.tgz && \
    /root/android-sdk-linux/tools/android list sdk -a -e && \
    echo y | /root/android-sdk-linux/tools/android update sdk -a -u -t build-tools-24.0.0,android-24,extra-android-support,extra-android-m2repository,extra-google-m2repository,extra-google-google_play_services
ENV ANDROID_HOME /root/android-sdk-linux
COPY app-build.sh /root/
COPY netrc /root/.netrc

また、Dockerfileからコンテナを起動し、生成されたコンテナ内apkをホストにコピーするという手間を省くために作ったMakefileはこちら。
(いまのテーマだとコードハイライトでタブ使えないみたいですね。Makefileなので行頭はスペースではなくタブを使って下さい)

date=$(shell date +%s)
apk: Dockerfile app-build.sh
    docker build -t app-build:0.1 .
    docker run --name="app-$(date)" app-build:0.1
    docker cp app-$(date):/root/pyconjp-android/app/build/outputs/apk/app-production-debug.apk .
clean:
    docker ps -a | grep "app" | awk '{print $$14}' | xargs docker rm

すごく実験的なものなので、ブランチやビルドフレーバーは固定になっています。
この辺はすごく改善の余地がありますね。

まとめ

このDockerfileは一度作ってしまえば今後Android開発ではずっと使い続けていけそうな気がします。
久しぶりにDockerを触りましたけど、いつでも同じ環境を構築できて、ホストを汚さない点はやっぱり便利ですね。
先ほど挙げた点や環境変数周りなど、まだまだ実用していくには足りていない機能も多いので、更新したらその時はまた記事書きます。
それでは。