自分のDockerイメージを作成したので、CIサービスでも利用して、環境を持ち運べるようにしたいなと思いました。
BITRISEがモバイルアプリのCIサービスとして良いという話を見かけたので、BITRISE上で自分のDockerイメージを利用してビルドするのを試しました。
タイトルにも書いてあるように、一部制約があります。
BITRISE
CIサービスの一つです。Werckerと比較してですが、workflowが扱いやすいです。GUIのエディターがあるのと、やりたいことがstepという単位でコンポーネント化されてるので、追加・削除が容易です。
ローカル環境で試すためのBITRISE CLIというものも用意されていて、非常に便利でした。
今回は共有のためにbitrise.ymlをコミットしてますが、BITRISE上でいじるものなのでコード管理する必要はないです。BITRISE CLIを利用してローカルで試す際はbitrise.ymlは必要です。しかし、BITRISE上だと容易に変更出来すぎてしまうので、コード管理しておいてもいいかなとも思いました。bitrise.ymlを管理していても、BITRISE上では無視されます。
利用しているbitrise.ymlはこちらです
Dockerイメージを取得して任意のコマンドを実行する
scriptで記述します。
- registryにログイン
- イメージをpull
- コマンドを実行
これらを行います。
registryにログイン
docker login
をします。
今回はDockerHubだったので指定はしてないですが、任意のregistryを指定して、ログインします。
$ docker login -u $USERNAME -p $PASSWORD
privateな変数はBITRISEのSecret Environment Variables
で指定します。BITRISE CLIで試すときは、 .bitrise.secrets.yml
を作成して、そちらに定義します。
- .bitrise.secrets.yml
envs: - USERNAME: xxxxxxxx - PASSWORD: xxxxxxxx - DANGER_GITHUB_API_TOKEN: xxxxxxxxxxxxx
イメージをpull
無事ログインできたらpullします。
イメージのcacheは、現在対応していないようです(一部イメージのみ対応。後述)。
docker pull $DOCKERHUB_IMG_ID
$DOCKERHUB_IMG_ID
は自分で App Environment Variables
に定義しています。
envs: - DOCKERHUB_IMG_ID: sakebook/docker-android-alpine:25.0.3_3
コマンドを実行
コンテナを残す必要はないのでrmオプションを付けてrunします。
BITRISE上でDockerを使うと、 /root
はマウントできません。マウント可能なのは /bitrise
以下だけです。
なので、Gradle本体や依存関係をcacheさせるには、Gradleを実行する位置を調整する必要があります。
マウント位置を、Dockerのユーザホームに指定します。
そして、ワーキングディレクトリは指定がなければ /
になってしまうので、マウントした位置に移動してソースを見つけられるようにします。
rm -rf local.properties
は、ローカルで実行したときにDocker上のANDROID_HOMEが上書きされてしまうのを防ぐために削除します。Android Studioを立ち上げ直すと再び作成されるので心配ありません。
もろもろを合わせると、実行部分は次のようになります。
- script@1.1.4: title: run test use docker inputs: - content: |- rm -rf local.properties docker run --rm \ -v $BITRISE_SOURCE_DIR:$DOCKER_HOME \ -w $DOCKER_HOME \ -e BITRISE_IO=true \ -e GIT_REPOSITORY_URL=$GIT_REPOSITORY_URL \ -e BITRISE_PULL_REQUEST=$BITRISE_PULL_REQUEST \ -e DANGER_GITHUB_API_TOKEN=$DANGER_GITHUB_API_TOKEN \ $DOCKERHUB_IMG_ID bash -c \ " bundle install bundle exec danger ./gradlew :multilinedivider:testDebug -PdisablePreDex "
$DOCKER_HOMEは利用するDockerイメージに応じて変えてください。自分のDockerイメージではユーザを作成していないので/root
にしています。
envs: - GRADLEW_PATH: "./gradlew" - GRADLE_BUILD_FILE_PATH: build.gradle - DOCKER_HOME: "/root"
注意点として、Docker上でビルドすると、環境変数を引き継いであげる必要があります。
CIだと、Pull Requrst(PR)なのかPUSHなのかまたはどのブランチからなのか、などトリガーに応じて処理を分けることがあります。
自前のDocker上で実行してしまうと、それらの環境変数が適応されないので、eオプションで適宜必要なものを渡してあげます。
先程の例だと、BITRISE上で、PRをトリガーに実行されたということを渡しています。
Dangerを使わなかったり、PRと認識させたくない場合、一行しか実行しなくて良い場合などはもっとシンプルになります。
制約
BITRISEだと、PRがトリガーのビルドの場合 Secret Environment Variables
が利用できません。
なので、PrivateなDockerイメージを利用しようとした場合、USERNAMEとPASSWORDをApp Environment Variables
に定義する必要があります。
これについては議論があるようです。自分は見当違いな質問を投げたときに教えてもらいました。
PRで利用できてしまうと、悪意のあるユーザにSecret Environment Variables
が漏洩するからです。
Gradle本体と依存関係のcache
cacheするにはBITRISEのCache:Push
stepを使用します。
docker run実行時に変更したpathを指定することでcacheすることができます。
$HOME
も追加しているのは、dockerを利用しない場合のためです。
合わせて不要なものも除外しておきます。
- cache-push@1.1.3: inputs: - cache_paths: |- $BITRISE_SOURCE_DIR/.gradle $BITRISE_SOURCE_DIR/.m2 $HOME/.gradle $HOME/.m2 - ignore_check_on_paths: |- $HOME/.gradle/caches/*.lock $HOME/.gradle/*.bin $BITRISE_SOURCE_DIR/.gradle/*.lock $BITRISE_SOURCE_DIR./.gradle/*.bin
Dockerイメージのcache
Dockerイメージを取得する部分を速くできないか検証しました。
docker save
でpullしたDockerイメージを保持し、次回以降はdocker load
で前回利用したDockerイメージを再利用というのを試しました。
どちらもGradle周りはcache出来ている状態です。
docker load
を使うと、一応、USERNAMEとPASSWORDなしでPrivateなDockerイメージをPRで利用可能にすることもできます。
- Dockerイメージのcacheなし
- Dockerイメージのcacheあり
思ったような結果にはなりませんでした。 Dockerイメージを取得する部分も大差なく、むしろDockerイメージをcacheする部分で時間がかかってしまい、結果的に遅くなってしましました。
これはBITRISEのcacheの仕組みが、特定のディレクトリを使いまわすような仕組みではなく、ファイルをアップロード & ダウンロードする仕組みになっているからです。
また、Pull Requestのときはcacheをアップロードしないです。cacheのダウンロードは行われます。
一部イメージだと可能
BITRISEが提供しているDockerイメージだと、CIの実行環境に既にcacheされているので高速化できます。
そして自前のDockerイメージを利用するときは、BaseのDockerイメージにBITRISEが提供しているDockerイメージを使うことが推奨されています。
- Android関連のツールが既にインストールされている
- ディレクトリ構成がBITRISEで利用するように出来上がっている
- 必要な環境変数がで定義されている
- BaseにするDockerイメージがcacheされている
という利点があるためです。
まとめ
今回自前のDockerイメージを利用しましたが、Ubuntuのイメージが用意されていてビルドもできるので、BITRISEで自前のイメージを使う旨味はないと思いました。どうしてもカスタマイズしないとできないことがある場合に使うほうが良いです。
あと、Secret Environment Variables
が利用できないのが辛いです。Dangerも利用しようとしてたのですが、この制限から一旦利用をやめました。
と言いつつ、自分はイメージを育てていきたいので、Pushのときは利用して、PRのときは利用しない(できない)ようにします。
参考
Bitrise - Mobile Continuous Integration and Delivery - iOS & Android Build Automation / Bitrise
Docker support on bitrise.io / Bitrise DevCenter
How to use your own Docker image for your builds / Bitrise Discussions
Docker login failed, only PR trigger / Bitrise Discussions
How to cache Gradle dependencies / Bitrise Discussions
About caching / Bitrise DevCenter
Using bitrise.io custom docker image option / Bitrise DevCenter