メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

BITRISEでPrivateなDockerイメージを利用してビルドする(Push時のみ)

自分のDockerイメージを作成したので、CIサービスでも利用して、環境を持ち運べるようにしたいなと思いました。

sakebook.hatenablog.com

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はこちらです

github.com

Dockerイメージを取得して任意のコマンドを実行する

scriptで記述します。

  1. registryにログイン
  2. イメージをpull
  3. コマンドを実行

これらを行います。

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なし

f:id:sakebook:20170808230402p:plain

  • Dockerイメージのcacheあり

f:id:sakebook:20170808230349p:plain

思ったような結果にはなりませんでした。 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

Bitrise CLI / 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

Dockerコマンドメモ / Qiita

About caching / Bitrise DevCenter

Using bitrise.io custom docker image option / Bitrise DevCenter