CircleCI 2.0 を使うようになって 1 年くらい経ちました。ノウハウがたまってきたのでまとめておきます。
目次
Docker Executorを使う
CircleCI には実行環境として Docker Executor と Machine Executor がありますが、可能な限り Docker Executor を利用するようにしています。
Docker Executor はローカル開発用の CLI ツールを利用できる
Docker Executor は ローカル開発用の CLI ツール を利用できます。ワークフローやキャッシュに対応していなかったりしますが、大抵のことをローカル環境で試すことができます。
.circleci/config.yml があるディレクトリで、こんな感じで使います。オプションでブランチや環境変数を指定できます。
$ circleci build \
--branch develop \
-e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
-e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
-e AWS_REGION=${AWS_DEFAULT_REGION}
構文エラーのチェックもできます。
$ circleci config validate
Docker Executor はビルドのリソースを変更できる
Machine Executor のリソースは固定ですが Docker Executor は resource_class を使用してビルドのリソースをある程度コントロールできます。
以下のように resource_class
を指定します。
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5
resource_class: medium+
Machine Executor はビルドの開始に時間がかかることがある
Docker Executor と Machine Executor の比較にも書かれていますが、Machine Executor はビルドの開始までに 1 分くらい待たされることがあります。多くの場合はすぐにビルドが開始されるので、CircleCI の混み具合によるようです。
ダウンロードしたファイルをキャッシュする
ビルドに必要なファイルを毎回ダウンロードしているとビルドが遅くなるので、一度ダウンロードしたファイルはキャッシュして再利用するようにします。
パッケージマネージャー編
以下は Ruby の Gem パッケージをキャッシュする例です。
version: 2
jobs:
build:
steps:
- checkout
# Restore cache
- restore_cache:
key: bundle-{{ checksum "Gemfile.lock" }}
# Install package
- run: bundle install --path vendor/bundle
# Save cache
- save_cache:
key: bundle-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
checksum "Gemfile.lock"
のようにしてキャッシュのキーを作成することで、Gemfile.lock ファイルに変更があった場合はキャッシュを利用せず、新しくファイルを取得するようになっているのがポイントです。checksum
以外にも利用できる変数は Using Keys and Templates から確認できます。言語ごとのサンプルは Caching Dependencies Bundler(Ruby) から確認できます。
wget curl 編
jq のようにパッケージマネージャーを使用せず wget で取得したファイルをキャッシュする例です。
まず、.jq-version
ファイルにバージョン番号を書いて Git にコミットしておきます。
$ cat .jq-version
1.5
.jq-version ファイルの checksum をキーに、ダウンロードしたファイルをキャッシュします。
version: 2
jobs:
build:
steps:
- checkout
# Restore cache
- restore_cache:
name: Restore jq
keys:
- jq-{{ checksum ".jq-version" }}
# Download package
- run:
name: Download jq
command: |
if [ ! -e /usr/local/bin/jq ]; then
version=$(cat .jq-version)
wget https://github.com/stedolan/jq/releases/download/jq-${version}/jq-linux64 -O /usr/local/bin/jq
chmod +x /usr/local/bin/jq
fi
# Save cache
- save_cache:
name: Save jq
key: jq-{{ checksum ".jq-version" }}
paths:
- "/usr/local/bin/jq"
ビルドを並列化する
Rails で RSpec のテストを 3 並列で実行する例です。詳しくは Running Tests in Parallel - CircleCI を参照。
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5
parallelism: 3
steps:
- checkout
- run: circleci tests glob "spec/**/*.rb" | circleci tests split | xargs bundle exec rspec
Workflows はできるだけ使わない
CircleCI2.0 から使えるようになった Workflows ですが、可能な限り、ギリギリまで使うことを避けます。経験上、Workflows を使うと保守性が落ちることが多く、デバッグに労力を割くことが多いです。ローカルの CLI ツールが Workflow に対応していないのもデメリット。条件分岐をしたくなったときは、Workflows で条件分岐 せず、まずは if 文でやりたいことが実現できないか考えるようにしています。
ブランチ名でデプロイ先を分岐させるケース。
version: 2
jobs:
build:
steps:
- deploy:
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
# deploy to production environment
elif [ "${CIRCLE_BRANCH}" == "develop" ]; then
# deploy to development environment
fi
0.0.1 の形式でタグが打たれているときにデプロイするケース。
version: 2
jobs:
build:
steps:
- deploy:
command: |
if [[ "${CIRCLE_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
# deploy to production environment
fi