はじめてのCircleCI(Docker+Rails+MySQL)でハマったお話

Railsアプリがある程度できてCircleCIを導入しようとしたらうまくいかなかったのでメモ。
CircleCIをざっくり理解して使うようになるために、簡単な手順もメモします。

## 1. 前置き
### 1-1. 動作環境
|  | version      |
|:----------:|:------------:|
| MacOS      | Mojave        |
| Ruby       | 2.5.7        |
| Rails      | 5.1.7        |
| MySQL      | 5.7          |
| CircleCI   | 2            |

### 1-2. この記事を読んで欲しい人
下記の人の助けになれたらと思って書いた記事です。

- CircleCIは知ってる。けど導入するのがちょっと億劫(怖い)
- いろんな記事があるけどどれが良い?

### 1-3. 初めてCircleCIを使うときに知っておきたかったこと(まとめ)
一番知っておきたかったことは3つです。

- CircleCI使うのが初めてだからって怖がらなくていい
- 設定ファイルを追記してpushしても全然CircleCIが実行されないがあるけど時間が経てば反映される(はず)
- 大抵は`config.yml`の設定が違うのが原因

問題なく動作するようになったら自分のテンプレート残しておくのが一番良いですよねww
ちなみにもうちょっとだけ詳しく知りたい方は[私なりの感想付きの解説](https://qiita.com/t0sh1/items/e7965e1efbb20ffeb996#4-%E8%A3%9C%E8%B6%B3%E5%88%9D%E3%82%81%E3%81%A6circleci%E3%82%92%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%8D%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8%E3%81%9D%E3%81%AE%E7%90%86%E7%94%B1)を読んでもらえると嬉しいです。



### 1-4. 書こうと思った経緯

Docker + Rails + CircleCIという構成で丁寧に解説してくださっている記事は多々あります。
下記の情報はどれも分かりやすく、理解する上でとても参考になりました。
私的には、1をベースに1~3を比較して`config/database.yml`を作成しました。

1. [【circleCI】Railsアプリでgithubと連携してrubocopとrspecテストを走らせる - Qiita](https://qiita.com/AK4747471/items/b2161784065f21cd1645)
2. [言語ガイド: Ruby - CircleCI](https://circleci.com/docs/ja/2.0/language-ruby/#ruby-on-rails-%E3%81%AE%E3%83%87%E3%83%A2-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89)
3. [【サーバーサイド一式】Docker + Rails + Circle CI + Terraformでインフラをコードで環境構築 & ECSへ自動コンテナデプロイ【前半】 - Qiita](https://qiita.com/kazukimatsumoto/items/fa448e002b0afd441b7c#circleci%E3%81%A7ci%E3%81%AE%E8%A8%AD%E5%AE%9A)
4. [いまさらだけどCircleCIに入門したので分かりやすくまとめてみた - Qiita](https://qiita.com/gold-kou/items/4c7e62434af455e977c2)

とりあえず問題なく動作はしたのですが、どうしても1の記事中のコードに書いてある`むりやり~~`という言葉が気になって、「じゃあきれいなのはどんなだよ。。。」ということで調べてみました。
ベストプラクティスではありませんのでご容赦ください。


## 2. 本題(CircleCIの関連ファイルを`.config/circleci.yml`だけで動くようにする)
### 2-1. 手順などを参考にするリンク
下記の記事がとてもわかりやすいと思います。
私も基本的にはこの手順で進めました。
順番としては下記の手順でざっくりと理解できると思います。
最終的にはGitHubにpushするとCircleCIでbuildが実行され始めればOKです。

1. [いまさらだけどCircleCIに入門したので分かりやすくまとめてみた - Qiita](https://qiita.com/gold-kou/items/4c7e62434af455e977c2)を読む。理解できると2以降の内容がスラスラ理解できます。
2. [【circleCI】Railsアプリでgithubと連携してrubocopとrspecテストを走らせる - Qiita](https://qiita.com/AK4747471/items/b2161784065f21cd1645)を読む。1が少し理解できたけど、飽きてきてしまったら2を読んでみるのもありだと思います。
3. ２を参考に進めてみる。１から3を繰り返してコードを修正していけばある程度作れます。
  1. CircleCIアカウントの登録コードを実際に書いて動かしてみる
  1. GitHubとの連携
  1. 設定ファイルを作成、コードを書く



### 2-2. 作成・修正するファイル

下記が今回話に出てくるファイルです。

- .circleci/config.yml
- .circleci/databse.yml.ci
- config/database.yml
- Gemfile

`.circleci/databse.yml.ci`は最後には削除します。



### 2-3. 今回のゴール
<strong>最終的に`.circleci/config.yml`だけでCircleCIの設定ができるようにする</strong>
ということのが今回のゴールです。
具体的には下記の項目が挙げられrます。

- `.circleci/database.yml.ci`を削除しても動作すること
- ローカルでもテストが問題なく動作すること
- `.circleci/config.yml`内の下記のコードを削除してもCircleCI上でテストが動作すること

```yml
- run: mv config/database.yml.ci config/database.yml
```



### 2-4.先に結論(私の完成形のコード)
先に私の最終的なコードを載せます。
ちなみに下記の状態になってますので悪しからず...

- コメントアウトや今回の内容に関係ないところなどは省略、削除しています。
- コードは折りたたんでます。
- `.circleci/database.yml.ci`は必要なくなったので削除しています。

<details><summary>**.circleci/config.yml**</summary><div>

```yml:.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.5.7-node-browsers
        environment:
          BUNDLE_JOBS: 3
          BUNDLE_RETRY: 3
          BUNDLE_PATH: vendor/bundle
          BUNDLER_VERSION: 1.17.3
          RAILS_ENV: 'test'
          MYSQL_HOST: 'db'
          MYSQL_PASSWORD: 'password'
          MYSQL_PORT: 3306
      - image: circleci/mysql:5.7
        environment:
          MYSQL_USER: 'test'
          MYSQL_ROOT_PASSWORD: 'password'
          MYSQL_PASSWORD: 'password'
          MYSQL_DATABASE: 'webapp_test'
          MYSQL_HOST: 'db'
        name: db
    parallelism: 1
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 1.17.3
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      - restore_cache:
          keys:
            - rails-demo-yarn-{{ checksum "yarn.lock" }}
            - rails-demo-yarn-

      - run:
          name: install yarn
          command: yarn install --cache-folder ~/.cache/yarn

      - save_cache:
          key: rails-demo-yarn-{{ checksum "yarn.lock" }}
          paths:
            - ~/.cache/yarn

      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      - run:
          name: run tests
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES

      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

```
</div></details>

<details><summary>**config/database.yml**</summary><div>

```yml:config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: webapp_development

test:
  <<: *default
  database: webapp_test
```

</div></details>

### 2-5. 手順詳細
### 2-5-1. gemを追加する
Ruby + RSpec のプロジェクトでテスト結果をCircle CIで取り扱いたい場合は`rspec_junit_formatter`というgemが必要になります。
Gemfileのtest環境に追加したら`bundle install`を実行します。
> [Circle CI のテストで RSpec のテストレポートを生成する - Tbpgr Blog](https://tbpgr.hatenablog.com/entry/2016/09/01/230523)

```ruby:Gemfile
group :test do
  gem "rspec_junit_formatter"
end
```




### 2-5-2. リンクを参考にとりあえず設定をする
[【circleCI】Railsアプリでgithubと連携してrubocopとrspecテストを走らせる - Qiita](https://qiita.com/AK4747471/items/b2161784065f21cd1645)を参考にしながら作成すると下記ファイルが作成されることになります。

- .circleci/config.yml
- .circleci/database.yml.ci

`.circleci/database.yml.ci`はデータベースで処理を始める前に、`confing/database.yml`の中のtest用の設定を変更するためです。

```yml:.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.5.7-node-browsers
        environment:
          - BUNDLE_JOBS: 3
          - BUNDLE_RETRY: 3
          - BUNDLE_PATH: vendor/bundle
          - BUNDLER_VERSION: 1.17.3
          - RAILS_ENV: 'test'
      - image: circleci/mysql:5.7
        environment:
          - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          - MYSQL_ROOT_HOST: '%'
    parallelism: 1
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 1.17.3
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      # ここがとても気になった
      - run: mv config/database.yml.ci config/database.yml
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      - run:
        name: run tests
        command: |
          mkdir /tmp/test-results
          TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
            circleci tests glob "spec/requests/managers/*_spec.rb" | \
            circleci tests split --split-by=timings)"
          bundle exec rspec \
            --format progress \
            --format RspecJunitFormatter \
            --out /tmp/test-results/rspec.xml \
            --format progress \
            $TEST_FILES

      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results
```
```yml:.circleci/database.yml.ci
test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: webapp_test

```

### 2-5-3. 【解決】`.circleci/database.yml.ci`が不要になるように`.circleci/config.ymlの設定を追加する`
今回記事にした理由の部分です。
[【circleCI】Railsアプリでgithubと連携してrubocopとrspecテストを走らせる - Qiita](https://qiita.com/AK4747471/items/b2161784065f21cd1645)の中に`ちょっと無理やりですが、database.ymlとdatabase.yml.ciを入れ替える記述です。`というコメントが`.circleci/config.yml`のコードの中にありました(下記がそのコード部分です)。

```docker
run: mv config/database.yml.ci config/database.yml
```

違和感を感じたので、`.circleci/config.yml`だけで設定ができるようにします。
結論としては
`.circleci/config.yml`のMySQLイメージに環境変数を`.circleci/config.yml`に追記します。

```yml:.circleci/config.yml
      - image: circleci/mysql:5.7
        environment:
          MYSQL_USER: 'test'
          MYSQL_ROOT_PASSWORD: 'password'
          MYSQL_PASSWORD: 'password'
          MYSQL_DATABASE: 'webapp_test'
          MYSQL_HOST: 'db'
        name: db
```

> [Operation CREATE USER failed for 'root'@'%' エラーが出て MySQL コンテナが起動に失敗する - emahiro/b.log](https://ema-hiro.hatenablog.com/entry/2020/06/16/023116)

これだけです。


## 3. 完成するまでに発生したエラー
今回の場合は基本的にMySQL関連のエラーでした。

- `has incorrect indentation,it should be formatted like:` or `in job 'build': step was not in the right format (unable to find step's name/details)`
- `ERROR 1396 (HY000) at line 1: Operation CREATE USER failed for 'root'@'%'`
- `Access denied for user 'root'@'172.28.0.3' (using password: YES)Please provide the root password for your MySQL installation`

### 3-1. has incorrect indentation,it should be formatted like:
インデントが間違っているというエラーです。
CircleCIの`Spin up environment`というSTEPSで発生しました。
単純なエラーでちゃんと原因も教えてくれているのですが、初めてのCircleCIでエラー原因が別の場所にあると思い、気づきませんでした。
しっかりとエラー文を読みましょうねというCircleCIからの訓示ですねw

### 3-2. `ERROR 1396 (HY000) at line 1: Operation CREATE USER failed for 'root'@'%'`
`Container circleci/mysql:5.7`のステップで発生しました。
MySQLにパスワードを設定していないことがが原因で発生したエラーです。
上記の参考にしたサイトに下記のコードが多く見られましたが、公式を見ると記述がありません([データベースの構成例 - CircleCI](https://circleci.com/docs/ja/2.0/postgres-config/#mysql-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BE%8B))。
みんなどこからこのコードを引っ張ってきてるのでしょうか?とても気になります。

`config.yml`の下記のコードはkeyの文字の意味そのままですが、
このコードを付け加えることでパスワードを設定しなくてもMySQLに接続できるようにしてくれます。

```yml:.circleci/config.yml
# 省略
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '%'
# 省略
```

そのため、上記のコードをコメントアウトすると`ERROR 1396 (HY000) at line 1: Operation CREATE USER failed for 'root'@'%'`というエラーが発生しました。

上記のコードを
`docker mysql ERROR 1396 (HY000) at line 1: Operation CREATE USER failed for 'root'@'%'`
でググると下記の記事が出てきます。

[Operation CREATE USER failed for 'root'@'%' エラーが出て MySQL コンテナが起動に失敗する - emahiro/b.log](https://ema-hiro.hatenablog.com/entry/2020/06/16/023116)

ググって出てきた記事のように`.circleci/config.yml`のmysqlイメージの`envrionment`にパスワードを設定することで解決されます。

```yml:.circleci/config.yml
      - image: circleci/mysql:5.7
        environment:
          MYSQL_USER: 'test'
          MYSQL_ROOT_PASSWORD: 'password'
          MYSQL_PASSWORD: 'password'
          MYSQL_DATABASE: 'webapp_test'
          MYSQL_HOST: 'db'
        name: db
```
`MYSQL_USER`が`root`だとダメなので`test`に変更しました(これも記事に書いてありました)。
一番下の`name`とは他のコンテナで使用するときなどに引用するコンテナの別名みたいな感じです。


### 3-3.  `Access denied for user 'root'@'172.28.0.3' (using password: YES)Please provide the root password for your MySQL installation`


`docker mysql Access denied for user 'root'@'' (using password: YES)Please provide the root password for your MySQL installation`でググると下記の記事が出てきました。


[railsのapp作成でmysqlを使用する時の導入とハマるところ　rake db:createができるまでの流れ　　　 - Qiita](https://qiita.com/Kaisyou/items/17142ff2d080d565fedc#%E3%81%AF%E3%81%BE%E3%82%8B%E3%81%A8%E3%81%93%E3%82%8D2--access-denied-for-user-rootlocalhost-using-password-no)

モロこれです。
パスワードが指定してなかったので、`config/database.yml`の`password`欄に下記のコードを設定することで解決しました。

```yml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # <- ここ

test:
  <<: *default
  username: test
  password: password
  database: webapp_test
```

ただ後から気づいたのですが、上記の設定をしてしまうと**ローカルでのテストに影響が出てしまう場合があります**。
試していて私が最終的問題なく動作するようになった設定は

- `config/database.yml`にCircleCIの設定を書かない(あくまでローカル用の設定とする)
- MySQLのユーザー、パスワード、データベース名前は`config.yml`に書く(上記の部分で設定済み)

というものでした。


### 3-4. `Mysql2::Error::ConnectionError: Can't connect to MySQL server on '127.0.0.1' (115)`
`bundle exec rake db:schema:load`というSTEPで発生しました。
MySQLに接続できませんというエラーです(おなじみですねw)。
このエラーは上記で設定した
`name: db`
というコードが関係しています。
もともと`MYSQL_HOST`には`127.0.0.1`を設定していましたが、何度も修正していくうちにこのコードを削除していました。
MySQLのイメージに設定した`name: db`を使用してrubyのイメージの環境変数(environment)の`MYSQL_HOST`に`'db'`という形で指定することでエラーは解消されました。

```yml:config.yml
    docker:
      - image: circleci/ruby:2.5.7-node-browsers
        environment:
          - BUNDLE_JOBS: 3
          - BUNDLE_RETRY: 3
          - BUNDLE_PATH: vendor/bundle
          - BUNDLER_VERSION: 1.17.3
          - RAILS_ENV: 'test'
          - MYSQL_HOST: 'db' # <- 127.0.0.1から変更
          - MYSQL_PASSWORD: 'password'
          - MYSQL_PORT: 3306
      - image: circleci/mysql:5.7
        environment:
          MYSQL_USER: 'test'
          MYSQL_ROOT_PASSWORD: 'password'
          MYSQL_PASSWORD: 'password'
          MYSQL_DATABASE: 'webapp_test'
          MYSQL_HOST: 'db'
        name: db
```

## 4. 【補足】初めてCircleCIを使うときに知っておきたかったこと(その理由)
この内容はの内容をちょっとだけ細かくしている内容(私見あり)なので読み飛ばしてもらって大丈夫です。



#### 4-1. CircleCI使うのが初めてだからって怖がらなくていい
今まで苦労して作成したアプリをCircleCIを導入して失敗すると今までの苦労が、、、
そんな恐れは必要ありません(知らなかった私はビクビクしながらやってました)。

CircleCIの設定は`config.yml`の1ファイルにまとめて書くことができるので他の環境に影響することは(たぶん)ありません。
無料枠もあるのでpush1回で課金されることは滅多にないと思っていいです。
CircleCIに接続できて、buildが実行されれば、最低限の設定はできていると思います。

#### 4-2. 設定ファイルを追記してpushしても全然CircleCIが実行されない
私の場合は初めてCircleCIの設定ファイルを追記してpush、20分前後かかって初めてCircleCI上でテストが実行された記憶があります。
設定とか問題なさそうなのに、、、と色々調べているうちに、いつのまにかCircleCIがテストを実行してました。笑
すぐ実行してくれる場合がほとんどかもしれませんが、私みたいなこともあるので、気を長く持ってください。


#### 4-3. 大抵は`config.yml`の設定が違うのが原因
だってローカルで動いてるんだもん。
エラーが表示されていたらそのエラー文を元に原因を探ることがとても大事です。
ローカルのテストもCircleCIのテストもコンテナ上で動いているのでエラー内容は基本的に同じであるはずです。

ちなみに私がCircleCIで初buildできたときに発生したエラーの原因は`config.ymlのインデントがずれている`というものでした。


以上。

[1-2. 初めてCircleCIを使うときに知っておきたかったこと(まとめ)](https://qiita.com/t0sh1/items/e7965e1efbb20ffeb996#1-3-%E5%88%9D%E3%82%81%E3%81%A6circleci%E3%82%92%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%8D%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8%E3%81%BE%E3%81%A8%E3%82%81)に戻る


### 5. 最後に
初めての技術に触るときはやはり

- 最小の壊れてもよいアプリを使用して試す
- 新規アプリを作成して試す

のが一番だと実感しました。
ビクビクしながらやるのは辛いです(m_ _m)