CircleCIでDockerイメージをキャッシュするのに、実はちょっとした工夫が必要な件

2月ですね。

さて、CircleCIにはcache_directoriesという機能があって、前回のビルドでダウンロードしたり生成したりするもので時間的コストのかかるものをキャッシュしておいて、次回以降のビルドでコンテナにリストアできます。

例えば、MavenでJarを大量にダウンロードしてきて出来上がったローカルリポジトリ等ですね。ちなみに.m2や.ivy等はデフォルトでキャッシュされます。

Dockerイメージのキャッシュ

ライブラリの他に、時間的コストとなるものの代表というとやはりDockerイメージなんですけど、実はこれをcache_directoriesの機能を使ってイメージの場所を指定してもビルドの時間は短縮できません。

実はCircleCIの公式ドキュメントには、ひっそりと以下のように記述されています。

Docker images aren't cached automatically. At the moment, we have no good method to cache them although we are trying to find a technical solution.
(Dockerイメージは自動ではキャッシュされない。技術的解決策を探しているが、現時点ではイメージをキャッシュするための良い方法がない。)

オゥフ。これを知らずにキャッシュがうまく使えないと言ってる人がTL上に結構いました。結構罠なので注意されたし。

さて、この課題に対する現状一番ベターな方法とされてるのが以下の手法です。

dependencies:
  # cache_directoriesとして~/dockerを利用
  cache_directories:
    - "~/docker"

  pre:
    # ~/docker/mysql.tarが存在すれば、docker loadでアーカイブをキャッシュとして復元
    - if [[ -e ~/docker/mysql.tar ]]; then docker load --input ~/docker/mysql.tar; fi

    # dockerイメージにビルド
    - docker build -t mysql .

    # ~/dockerを作成
    - mkdir -p ~/docker

    # docker saveでイメージをtarとして保存(次回以降はこれが利用される)
    - docker save mysql > ~/docker/mysql.tar

簡単に言うと、cache_directoriesdocker load + docker saveの合わせ技ですね。

cache_directoriesのコストも無視できない

この手法でビルドしたイメージを次回以降キャッシュとして利用できるようになりますが、cache_directoriesを利用している以上、キャッシュのアーカイブをリストアする時間的コストも当然発生します。こんなふうに。

それを考えると、いかにしてイメージのサイズを小さくするというのがDockerを利用しつつビルド時間短縮を短縮するためのカギになります。

重量級のDockerイメージを利用してないか今一度チェックすると良いですねー。