pockestrap

Programmer's memo

CircleCIの実行を速くした

先週仕事でやったのをメモします。 CI力が低いので、記事を公開することでツッコミをもらうのが目的です。

モチベーション

弊社ではCIの実行が1回あたり20分ほどかかっています。 これはわりとストレスなので速くしたいです。幸いにも削れそうなところが2つ見つかったので削ってみました。

Shallow Clone

CircleCIではデフォルトのcheckoutを使用するとリポジトリの全体をcloneしてきます。 これを回避するためにshallow cloneを導入しました。

まず、次のようなコマンドを定義します。

commands:
  shallow-clone:
    description: 'Git clone shallowly'
    steps:
      - run:
          name: 'shallow clone'
          command: |
            set -x
            echo "machine github.com login $GITHUB_ACCESS_TOKEN" > ~/.netrc
            cd /home/ubuntu/kibela
            repository_https_url=$(echo $CIRCLE_REPOSITORY_URL | sed -e "s|git@github.com:|https://github.com/|")
            git clone --depth=1 $repository_https_url .
            if [[ "$CIRCLE_BRANCH" =~ ^pull\/* ]]; then
              FETCH_SRC="${CIRCLE_BRANCH}/head"
            else
              FETCH_SRC=$CIRCLE_BRANCH
            fi
            git fetch --depth=10 origin $FETCH_SRC
            git reset --hard $CIRCLE_SHA1 || (echo "The branch was updated after the build was executed. Please rebuild." 1>&2; false)

そして、定義したコマンドをjob定義のstepsの中で使います。

jobs:
  build:
    steps:
      # - checkout を以下に置き換え
      - shallow-clone

この設定は次の記事を参考にしています。

GameWithさんの設定をベースにし、うまく動かないところをguitarrapcさんのOrbを参考に修正しています。

今まで1~2分かかっていたcloneが、これによって3秒程度で終わるようになりました。

なお今回は試しませんでしたが、shallow cloneをする他にも、gitリポジトリをCircleCIのキャッシュに突っ込むという方法もあるようです。

bundle install結果のclean

以前からbundle installの結果をキャッシュしていました。 ところがキャッシュをfetchするのに2分ほどかかっていました。

時間がかかっている原因は、bundle installした結果をcleanしていないためでした。 つまり、Gemの更新があると古いバージョンのgemと新しいバージョンのgemの両方がキャッシュされてしまいます。 これを繰り返すと、どんどんキャッシュサイズが膨れていってしまいます。

これを防ぐためにbundle install--cleanオプションを渡すようにしました。

- restore_cache:
    name: "Restore Cache"
    keys:
      - rubygems-{{ .Environment.COMMON_CACHE_KEY }}-{{ checksum "Gemfile.lock" }}
      - rubygems-{{ .Environment.COMMON_CACHE_KEY }}-
- run:
    name: "Install"
    command: |
      bundle install --clean --path=vendor/bundle/ --jobs=4 --retry=3 --without=production --frozen
- save_cache:
    name: "Save Cache"
    key: rubygems-{{ .Environment.COMMON_CACHE_KEY }}-{{ checksum "Gemfile.lock" }}
    paths:
      - vendor/bundle/

これでbundle install後に必要なくなったgemを消してからキャッシュに保存されるようになります。

この修正は以下の記事を参考にしました。

この修正によって、2分ほどかかっていたキャッシュのfetchが20秒ほどまで減りました。

(ところで、--cleanオプションがdeprecatedじゃないか確認してませんでした。手元で確認しづらいし気になる)


本当に基本的な修正しかしていませんが、これでCIの実行が多少速くなりました。 本当はもっともっと速くなってほしいですが、ここから先は金で殴るしかないですかねえ。

ツッコミお待ちしています。