最近でCircleCIおじさんになっているりんご(@mstssk)です。
今やっているプロジェクトのgitリポジトリが大きくなりすぎて、CircleCIでの checkout
の実行に何分もかかってしまっています。
以前は2〜3分だったのが、先日は8分もかかってました。
思い切って.gitディレクトリをキャッシュしてしまうようにして、10秒前後で済むようにしてみました。
追記2019/02/19
本記事の内容を試す前に、あなたがCircleCIで使っているDockerイメージにgitとsshがインストールされているか確認しましょう。
gitとsshが無い場合、CircleCIがFallbackしてチェックアウトを行ってくれるのですが、これはイメージ上のgitとsshを使うより遅いようです。
FallbackしているかどうかはCircleCIの各ジョブのページでCheckout codeステップのログを見ればわかります。
参考記事
やった内容は、下記の公式ドキュメントとクラウドワークスさんのエンジニアブログ記事を参考にしたものです。
- Caching Dependencies - CircleCI
https://circleci.com/docs/2.0/caching/#source-caching - CircleCI 2.0に移行して新機能を活用したらCIの実行時間が半分になった話 - クラウドワークス エンジニアブログ
http://engineer.crowdworks.jp/entry/2017/04/04/202719
クラウドワークスさんの記事には、.gitのキャッシュではなく、shallow cloneすることでチェックアウトを高速化する手段が書かれていますが、私は.gitをキャッシュする方法を選択しました。
キャッシュの方法は公式ドキュメントに記載されているからというのもありますが、CircleCI上でshallow cloneするには別途GitHubのアクセストークンを用意する必要があり管理上の手間が増えてCIの管理が属人化しすぎるのを嫌ったためです。
CircleCIで .git ディレクトリをキャッシュする
実際のCircleCIの設定ファイルは以下のように記述しています。
version: 2
jobs:
foobar-job:
steps:
# 公式ドキュメントに書いてある.gitをキャッシュするやり口
# https://circleci.com/docs/2.0/caching/#source-caching
- restore_cache:
keys:
- dot-git-cache-{{ .Environment.CACHE_KEY }}-{{ .Branch }}-{{ .Revision }}
- dot-git-cache-{{ .Environment.CACHE_KEY }}-{{ .Branch }}-
- dot-git-cache-{{ .Environment.CACHE_KEY }}-
# レストア時にワーキングディレクトリがcleanじゃないことがあったのでhard reset。echoはexit code食わせるため
- run: git reset HEAD --hard || echo ng
- checkout
- save_cache:
key: dot-git-cache-{{ .Environment.CACHE_KEY }}-{{ .Branch }}-{{ .Revision }}
paths:
- ".git"
-
restore_cache
のkeys
を複数指定しているのは、合致するキャッシュがなければ別コミット・別ブランチのCIのキャッシュを拾うためです。 -
{{ .Environment.CACHE_KEY }}
は任意にキャッシュをクリアするため。CircleCIでCACHE_KEY
という環境変数に適当な文字列を入れておいて、キャッシュをクリアしたいときに値を変えることでキャッシュのキーを変えてしまっています。 -
git reset HEAD --hard || echo ng
は、レストア時にワーキングディレクトリがcleanじゃないことがあったためです。キャッシュをレストアしない場合は git reset が失敗するので、echoでexit code食わせてごまかしています。 - .gitディレクトリをキャッシュからレストアした状態で、CircleCIの
checkout
を使うと、ちゃんとレストアした.gitを使ってくれるので、差分だけの取得になり高速になります。
このやり方の注意点として、変なブランチ名の付け方をしてしまったときに、キャッシュ済みのリポジトリ情報と新しいブランチ名が衝突してうまくチェックアウトできない場合があります。
具体的には feature/foo/bar
というブランチを作ったあと、それを消して feature/foo
というブランチを作ると、キャッシュからレストアした.gitディレクトリに feature/foo/
が既にいるので feature/foo
のチェックアウトに失敗します。
めったにないことですし CACHE_KEY
を変更すれば済むので、これについては特別な対応はしていません。