timakin
7 min readMay 30, 2017

今在籍している企業では、最近CircleCI2.0へのアップグレードが盛んに行われている。 すでにアップグレードを体験された方からは、「は?」「速すぎてドン引き」「爆速すぎてずっと泣いてる」などとの感想が寄せられている。元気そう。

具体的に自分もAPIでバージョンを上げたら、6分半が45秒になった。

で、特定のpackageのtestだったり、インフラ周りの設定のアタッチ等なら小規模なのでなんとなくかけば導入できる。 しかしAPIとなると、意外と設定を載せているところがまだ少なかったので、API用途だとざっくりとこんな感じだよというのを書きたいと思う。

もしそこまでじゃなくていいので、簡単な設定がみたければこちらへどうぞ

全体感

APIやある程度の規模のソフトウェアのCIとなると、

  • 依存パッケージのインストール
  • linterでの文法チェック
  • テスト
  • デプロイ

の諸々をやらなくてはならない。 色々名前を変えているが、上記を大体行うとこんな感じになる。

version: 2
jobs:
build:
working_directory: /go/src/github.com/timakin/sample_project
docker:
- image: timakin/golang-api-npm-glide
environment:
- APP_NAME: sample_project
steps:
- checkout
- restore_cache:
key: sample_project-{{ .Branch }}-{{ checksum "glide.lock" }}
paths:
- "/go/src/github.com/timakin/sample_project/vendor"
- run:
name: Vendoring
command: |
vendoring_cache_path=/go/src/github.com/timakin/sample_project/vendor
if [ ! -e $vendoring_cache_path ]; then
glide install
fi
- save_cache:
name: Saving Cache - glide
key: sample-project-{{ .Branch }}-{{ checksum "glide.lock" }}
paths:
- "/go/src/github.com/timakin/sample_project/vendor"
- restore_cache:
key: sample-project-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- "/go/src/github.com/timakin/sample_project/node_modules"
- run:
name: Npm install
command: |
npm_cache_path=/go/src/github.com/timakin/sample_project/node_modules
if [ ! -e $npm_cache_path ]; then
npm install
fi
- save_cache:
name: Saving Cache - node_modules
key: sample-project-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- "/go/src/github.com/timakin/sample_project/node_modules"
- run: go vet $(go list ./... | grep -v /vendor/)
- run: go test -race $(go list ./... | grep -v /vendor/)
- run: sh scripts/make_artifact.rb
- deploy:
name: Deploy staging
command: |
if [ "${CIRCLE_BRANCH}" == "develop" ]; then
sh scripts/deploy_staging.sh
fi
- deploy:
name: Deploy production
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
sh scripts/deploy_production.sh
fi

Docker Image

CircleCI2.0はDockerベースのビルドを行うが、フロントエンドとバックエンド両方に影響があるCIだった場合、 下記のようにカスタムのDocker Imageを作る必要がある。

golangは運良くCircleCIが1.8.3 (2017年5月30日現在)までの新鮮な公式イメージを提供してくれてるので、それをベースに作るとよさそう。

また、AWSにdeployするならaws-cli、GCPへのデプロイならまた別途の手段でツール群をインストールする処理をDockerfileに書いておくべき。 ビルドごとに変化しないコマンドは全部Dockerfileに書きたいところ。

Cache

依存するパッケージが多ければ多いほど、キャッシュが有効な手段になる。 npm install bundle install assets:precompile glide get dep ensure などはlockファイルのchecksumをキーにして、もし更新がなければcacheをrestoreすることでインストールをskipできる。 pathsパラメーターにちゃんとvendor対象になるものを置くディレクトリを指定すれば万事OK。 具体的には2分半~4分くらいかかってるものが1秒や2秒になる。

また、もしキャッシュが存在するときにskipするときの書き方は、こんな感じでディレクトリの存在チェックを挟む感じでやれば良い。

- run: 
name: Vendoring
command: |
vendoring_cache_path=/go/src/github.com/timakin/sample_project/vendor
if [ ! -e $vendoring_cache_path ]; then
glide install
fi

デプロイ

deployコマンドを実行するとき、ブランチによって処理を切り替えて、デプロイ用の処理を書いたスクリプトを叩きたくなる。 コマンド実行のrootとなるのは、 working_directory なので特に特別なことを意識せずに処理できる。

また、ブランチによって処理を切り替える場合、一つのコマンドで処理を変えることもできるが、deployオプションは複数書くことができるので、ブランチが違えば別のdeployオプションとして書いて置くとわかりやすい。

まとめ

上記のようにキャッシュを生かしつつビルドを行えば、格段にビルド時間が短縮されます。 カスタマイズされたDocker Imageもそこまで労力ではないので、自分一人が利用するとしても、繰り返し同じコマンドを実行する場合はImageを作成しましょう。