Rails+docker-composeでCircleCi2.0の高速化tips

## 概要

Rails+docker-composeでCircleCi2.0の高速化のtipsをメモ

## キャッシュを活用する

### docker image のキャッシュ

`docker-compose.yml`, `Dockerfile`  等の `checksum` を使って `save_cache` することで、次回以降の処理をキャッシュされたdocker image からコンテナを立ち上げることができます

```sh
md5sum $(find ./-name "Dockerfile*") > /tmp/dockerfile-md5sum`
```

とすることで、ワイルドカードを指定してプロジェクトディレクトリ内の `Dockerfile*` の `checksum` の値を用いてキャッシュすることも可能です

下記例では、`docker-compose.yml` と `Gemfile.lock` と `Dockerfile` の `checksum` を組み合わせた値 で `key` を作成し、
`key` に該当するデータがあれば `rails` のimageを復元し、なければビルド&キャッシュするようにしています

```yml
      - run:
          name: generate Dockerfile checksum
          command: md5sum $(find ./-name "Dockerfile*") > /tmp/dockerfile-md5sum
     - restore_cache:
          name: restore rails image cache
          key: docker-{{ checksum "docker-compose.yml" }}-{{ checksum "Gemfile.lock" }}-{{ checksum "/tmp/dockerfile-md5sum" }}
          paths: ~/caches
      - run:
          name: build rails image
          command: |
            if [ ! -f ~/caches/rails.tar ]; then
              docker-compose build rails
              docker-compose run rails bundle install
              docker-compose up -d rails
              docker save rails > ~/caches/rails.tar
            else
              docker load -i ~/caches/rails.tar
            fi
      - save_cache:
          name: save rails image cache
          key: docker-{{ checksum "docker-compose.yml" }}-{{ checksum "Gemfile.lock" }}-{{ checksum "/tmp/dockerfile-md5sum" }}
          paths: ~/caches
```

### テストデータのキャッシュ

テストの実行に初期データ等が依存していて、かつ初期データ作成にかかる時間的コストが大きい場合は
初期データをdumpしてキャッシュすることで時間短縮ができます

下記例では、`db/seeds.rb` と `db/schema.rb` と `factory-girl` に記述したコードの `checksum` を組み合わせた値 で `key` を作成し、`key` に該当するデータがあれば `dump` されたデータを復元し、なければデータ作成& `dump` するようにしています

```yml
      - run:
          name: generate factory-girl checksum
          command: md5sum $(find ./spec/factories -name "*.rb") > /tmp/factory-girl-md5sum
      - restore_cache:
          name: restore db dump
          key: db-{{ checksum "db/seeds.rb" }}-{{ checksum "db/schema.rb" }}-{{ checksum "/tmp/factory-girl-md5sum" }}
          paths: ~/caches/dump_data.sql
      - run:
          name: setup db data
          command: |
            if [ -f ~/caches/dump.sql ]; then
              cp -f ~/caches/dump.sql ~/tmp/dump.sql
              docker-compose exec db bash -c "mysql -u root < /sql/dump.sql"
            else
              mkdir -p ~/caches
              docker-compose run rails rails db:create RAILS_ENV=test
              docker-compose run rails rails db:migrate RAILS_ENV=test
              docker-compose run rails rails db:seed RAILS_ENV=test
              docker-compose exec db bash -c "mysqldump -u root -A > /tmp/dump.sql"
              cp -f ~/tmp/dump.sql ~/caches/dump.sql
            fi
      - save_cache:
          name: save db dump
          key: db-{{ checksum "db/seeds.rb" }}-{{ checksum "db/schema.rb" }}-{{ checksum "/tmp/factory-girl-md5sum" }}
          paths: ~/caches/dump.sql    
```

Artifactsにdumpしたデータを保存しておけばローカルのテストでもテストデータの作成時間を削減できて開発効率が上がりそうですね！

## テストを並列化する

テストの並列化をしてくれるGemがいくつかあるので、導入することでテスト時間を短縮化できます
多くのGemは

- 並列化する数だけテスト用のdatabaseを作成
- スレッドを立ててRspecを並列実行

をしてくれます
代表的なGemとして、

- [parallel_tests](https://github.com/grosser/parallel_tests)
- [test-queue](https://github.com/tmm1/test-queue)

などがあります


## 参考

- [CircleCI 2.0 で複数ファイルからキャッシュキーを生成する便利な方法](http://vividcode.hatenablog.com/entry/circleci/cache-key-from-multiple-files)