技術ブログ

CircleCI で使用する Docker イメージをキャッシュする

author: yterajimadate: 2016-02-17tags: circleci, docker

CircleCI でテストを実行する際に Docker を導入しています。Docker イメージをキャッシュしテスト実行時間を短縮する方法について書いてみます。

CircleCI に Docker を導入する

CircleCI はテスト実行時に Docker を使って必要な環境を用意することができます。現在開発中の mitsume では Docker コンテナで MariaDB + Mroonga をインストールしてテストを実行しています。

Docker コンテナの使用方法についてはドキュメントに丁寧に書かれています。

Deployment to a Docker registry

cicle.yml に次の記述を追加するだけです。

  • services に docker を追加
  • docker build で Docker イメージを作成

mitsume ではテスト実行時に yterajima/mitsume-mroonga を clone, build しテストを行っています。

当初の circle.yml は次のような内容でした。(一部修正して掲載)

machine:
  services:
    - docker

dependencies:
  pre:
    - git clone git@github.com:yterajima/mitsume-mroonga.git ./mroonga
    - docker build -t mitsume/mroonga ./mroonga
    - bash ./circleci/install-go-1.5.1.sh

  override:
    - go get bitbucket.org/liamstask/goose/cmd/goose
    - go get github.com/golang/lint/golint
    - go get github.com/mattn/gom
    - gom -test install

test:
  pre:
    - go version
  override:
    - docker run -it -d -p 13306:3306 --name mroonga mitsume/mroonga
    - sleep 10
    - go vet ./...
    - test -z "$(golint ./... | tee /dev/stderr)"
    - gom test -v .:
        timeout: 600

この方法を採用するまでしばらく MariaDB + Mroonga が CircleCI の Ubuntu にインストールされずに(依存関係らしい)困っていましたが, これで解決しました。

Docker を導入した場合の時間問題

当たり前の話で Docker を採用したことでテストの実行時間が増えました。Docker イメージをテストに使用する場合次の手順が増えます。

  1. github からの clone
  2. Docker イメージのビルド

github においてある Dockerfile は頻繁に変更されるものではないので, できればこの手順を飛ばしてテストを実行したいですね。cache 機能 を使って都度ビルドされないように変更することにします。

github からの clone を減らす

対象リポジトリの HEAD とローカルに clone してあるリポジトリの HEAD が異なる場合にのみ clone を実行するように変更しました。

リモートの HEAD を確認するコマンド:

$ git ls-remote git@github.com:yterajima/mitsume-mroonga.git | grep HEAD | awk '{print $1}'`

ローカルの HEAD を確認するコマンド:

$ git log -n 1 | grep 'commit ' | awk '{print $2}'

それぞれの HEAD の値を比較して異なっていれば再度 clone するようにしています。

Docker イメージのビルドを減らす

ビルドした Docker イメージは次のコマンドで保存できるらしく。

$ docker save mitsume/mroonga > ~/docker_cache/mitsume-mroonga.tar

保存したデータからイメージをロードできるらしく。

$ docker load < ~/docker_cache/mitsume-mroonga.tar

これらの機能を利用して $ git clone または $ git pull --rebase が実行された場合だけビルドしてキャッシュするように変更しました。

改善後

circle.yml (一部修正の上掲載)

general:
  branches:
    only:
      - master
      - develop
      - /feature\/.*/

machine:
  services:
    - docker

dependencies:
  cache_directories:
    - "~/mroonga"
    - "~/docker_cache"
  pre:
    - bash ./circleci/install-mitsume-mroonga.sh:
        timeout: 300

  override:
    - go get bitbucket.org/liamstask/goose/cmd/goose
    - go get github.com/golang/lint/golint
    - go get github.com/mattn/gom
    - gom -test install

test:
  pre:
    - go version
  override:
    - docker run -it -d -p 13306:3306 --name mroonga mitsume/mroonga
    - sleep 10
    - go vet ./...
    - test -z "$(golint ./... | tee /dev/stderr)"
    - gom test -v .:
        timeout: 600

install-mitsume-mroonga.sh の中身:

#!/bin/bash

set -ex

remote_head=`git ls-remote git@github.com:yterajima/mitsume-mroonga.git | grep HEAD | awk '{print $1}'`

if [ -e ~/mroonga ]; then
  cd ~/mroonga
  local_head=`git log -n 1 | grep 'commit ' | awk '{print $2}'`
else
  local_head="not-exist"
fi

if [ ! -e ~/docker_cache ]; then
  mkdir ~/docker_cache
fi

if [ $remote_head != $local_head -o ! -e ~/docker_cache/mitsume-mroonga.tar ]; then
  if [ -d ~/mroonga ]; then
    cd ~/mroonga
    git pull --rebase
  else
    git clone git@github.com:yterajima/mitsume-mroonga.git ~/mroonga
  fi
  docker build -t mitsume/mroonga ~/mroonga
  docker save mitsume/mroonga > ~/docker_cache/mitsume-mroonga.tar
else
  docker load < ~/docker_cache/mitsume-mroonga.tar
fi

これで 80 秒短縮しました。やったね!

変更前

変更後