第5回　AWSに自動でテスト/デプロイしてくれるインフラの設定・構築（CircleCI編）

# 本シリーズ集

|  | タイトル |
|:-:|:-:|
| 0 | [目標・やりたいこと](https://qiita.com/drafts/c59c0df9eaf5e6b10111/edit) |
| 1 | [AWS編](https://qiita.com/Dadadada-TT/items/86e0a578f934e6578c04)|
| 2 | [rails開発環境構築編](https://qiita.com/Dadadada-TT/items/f4ce8d997cc0bb3e0aca)|
| 3 | [Nginx・MySQL編](https://qiita.com/Dadadada-TT/items/4a7edd61a5d7c75f772d) |
| 4 | [Capistrano編](https://qiita.com/Dadadada-TT/items/79e33659eacda1cd0510) |
| 5 | ___CircleCI編___ |
| 6 | [総集編](https://qiita.com/Dadadada-TT/items/3d1fa7854d562ae1db1b) |

#CircleCIとは
　CircleCI(version: 2.1)は、自動でテスト＆デプロイを実行してくれる、所謂、『__CI/CDパイプライン（継続的インテグレーション/デリバリー）__』 を構築する際に使用するSaaS。

　なお、利用する際には、YAML（__.yml__）ファイルで記述する。

また、本記事を書く際に特に参考にしたのは以下の2つ。
　1. 公式サイト　
　 　　https://circleci.com/docs/ja/2.0/configuration-reference/#section=configuration

　2.　[いまさらだけどCircleCIに入門したので分かりやすくまとめてみた](https://qiita.com/gold-kou/items/4c7e62434af455e977c2)

　他にもたくさんCircleCIの設定内容の記事が存在しているので、この記事では __登録方法・SSHの設定・設定の結果__ の3段階の説明にしておきます。


# CircleCIの登録
1. [circleciの公式サイト](https://circleci.com/signup/)で『__Sign up with Github__』をクリック
2. 『__Authorize circleci__』をクリック
3. Githubのログイン画面が出てくるので、パスワードを入力
4. ユーザ選択画面が出てくるので、自身のアカウントと結びつける。
5. 自身のリポジトリ一覧が表示されるので、利用したいリポジトリの右側の『__Set Up Project__』をクリック
6. 遷移した画面先で、『Add Config』をクリック。
7. 下図のようなモーダルが開かれるので、『Proeed to New UX』をクリック![スクリーンショット 2020-11-21 15.33.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557656/8e375eda-60d1-3029-7d1e-2511dd27977c.png)
8. 新しくブランチが作成されるので、master（デフォルト）ブランチにmergeする。

　__※ 初めはエラーが出てくるかもしれませんが無視で構わないです。__

-> 登録終了


# SSHの設定
　まずは、circleci から AWSのEC2インスタンスへデプロイ可能にするためのSSHキー設定を行う。

　設定方法は、二通り。
###① __Circleci__のプロジェクト画面から行う。

　1. 下図のように、対象のProjectで 『Project Settings』をクリック
![スクリーンショット 2020-11-25 0.27.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557656/318ec06f-dcf0-a255-74b9-f4053bc73872.png)
　2. 『SSH Keys』をクリック
　3. 下のほうにスクロールして、『Additional SSH Keys』の欄の『Add SSH Key』をクリックして、EC2インスタンスのアクセス用の公開鍵をコピペする。

###② __.circleci/config.yml__ ファイルに記述する
　こちらは、__CircleCI__ の設定ファイルである、 __.circleci/config.yml__ ファイルにsshのfingerprintを直接記述するパターンである。

```.yml:　
jobs:
#... (something)
  deploy:
    steps:
      #... (something)
      - add_ssh_keys:
          fingerprints: "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
      - run: #{デプロイ用のコマンド}
```

　今回は、前者で設定を行ったが、セキュリティ的にはどちらでも良いと思われる。（なお、EC2のSSH鍵ファイルは必要ないので、ブランチに鍵ファイルを含めなくても良い）




#.circleci/config ファイルの編集

　以下は、gitで __push or PR__が発生した際に、自動で __rspec__ テストを行ってくれ、なおかつ対象ブランチが __master__ であった時に、__capistrano__ を使用したデプロイ処理を行ってくれる設定のファイルである。

```.yml:.circleci/config.yml
version: 2.1
jobs:
  build:
    docker:
      - image: circleci/ruby:2.7.1-node-browsers
    steps:
      - checkout
      # 2回目以降高速に行うため、キャッシュに保存したものを取り出す。ただ、Gemfileを変更した際に前回使用したものと同じではエラーが生じるかもなので、
      # ファイル名・ファイルの中身に応じてハッシュ値を算出する、 checksum 構文を利用している。
      - restore_cache:
          key: Sample_Cache_{{ checksum "Gemfile.lock" }}
      # 初回 or Gemfile更新 時にはこのコマンドを使用して環境を整える。
      - run:
          name: install Gemfile
          command: bundle install --path vendor/bundle
      # 上記のコマンドにより生成されたディレクトリをキャッシュに保存する。ちなみに、キャッシュ名が既に有る場合は、実行されない。
      - save_cache:
          key: Sample_Cache_{{ checksum "Gemfile.lock" }}
          paths:
            - ./vendor/bundle
  test:
    docker:
      - image: circleci/ruby:2.7.1-node-browsers
        environment:
          DB_HOST: 127.0.0.1
          DB_PASSWORD: root
      - image: circleci/mysql:5.7
        environment:
          MYSQL_USER: root
          MYSQL_ROOT_PASSWORD: root
    steps:
      - checkout
      - restore_cache:
          key: Sample_Cache_{{ checksum "Gemfile.lock" }}
      # このコマンドを実行しないと、"Your Yarn packages are out of date! Please ... " というエラーが出るので実行
      - run:
          name: if do not execute this, an error occurs.
          command: yarn install --check-files
      # DBの立ち上げを待つ。120秒経っても起動しない場合は、test ジョブを強制終了させる。
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
      # bundlerの path設定を vendor/bundle にする
      - run:
          name: setup bundler target path
          command: bundle config --local path vendor/bundle
      - run:
          name: Set up DB
          command: |
            bundle exec rake db:create
            bundle exec rails db:migrate
      - run:
          name: implement test
          command: bundle exec rspec ./spec/
  deploy:
    docker:
      - image: circleci/ruby:2.7.1-node-browsers
    steps:
      - checkout
      - restore_cache:
          key: Sample_Cache_{{ checksum "Gemfile.lock" }}
      - run:
          name: setup bundler target path
          command: bundle config --local path vendor/bundle
      - run:
          name: deploy app by capistrano
          command: bundle exec cap production deploy
workflows:
  version: 2.1
  test-deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master
```

　なお、他者が公開している __executor / command 等__ を使える __orbs__ という機能がversion2から実装されたが、例えば（調べた限りでは）、`rspec` が `rspec_junit_formatter`というgemでなければならなかったりしたので、簡単に実装できるが、柔軟性があまりないため、今回は一から自分で実装した。
　 （例：　https://circleci.com/developer/orbs/orb/circleci/ruby ）

　これで、全てのブランチの__push (commit) / PR__に対して、テストを行ってくれ、__master__ ブランチが対象であった場合は、__capistrano__ を使用したデプロイ作業を自動で行ってくれるインフラの構築が完了。

<details><summary>__この設定に合わせた`config/database.yml`ファイルの設定__</summary>
<div>

```.rb:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch("DB_USER") { "root" } %>
  password: <%= ENV.fetch("DB_PASSWORD") { "password" } %>
  host: <%= ENV.fetch("DB_HOST") { "db" } %>
```

</div>
</details>


　他にも__rubocop__ など、『コードが（インデントなどの）コーディング規約に則っているか』をテストしてくれるgemがあるので、実際の開発では、jobs: test: の欄に、

```.yml:.circleci/config.yml
jobs:
  test:
    #(...something)
    steps:
      #(...something)
      - run:
          name: 'rubocop'
          command: bundle exec rubocop #（合ってるかわからない）
```

と書くなどすると思われる。


# デプロイ時にエラーが発生
<details><summary> ※ Capistrano の  `unless ...` 文をつける前だったので、つけたら解決。前回『必須』と説明したのはこのため。
　いずれにせよ初回はローカルからデプロイすることは必須。</summary>
　masterブランチにMergeされた時に、Capistranoのコマンドにより、デプロイされる設定を書いたが、エラーが発生していた。（database.ymlはgitに含まれてました笑）
<div>

```
SSHKit::Runner::ExecuteError: Exception while executing as ec2-user@xx.xx.xx.xx: No such file or directory @ rb_file_s_stat - config/master.key
```

__↓(再掲)原因箇所__

```.rb:config/production.rb
# (...something)
on roles(:app) do
    upload! "config/database.yml", "#{shared_path}/config/database-pro.yml" unless test "[ -f #{shared_path}/config/database-pro.yml"
    upload! "config/master.key", "#{shared_path}/config/master-pro.key" unless test "[ -f #{shared_path}/config/master-pro.key ]"
  end
end
# (...something)
```

</div>
　デプロイ元には『__master.key__』ファイルがないというエラー....
　つまり、__circleci__ が__git__を使用し、デプロイ時にも__Capistrano__が __git__ を使用しているため、gitにない『__master.key__』 を使おうとしたことが原因のエラーだった。
</details>


# Webhook設定方法
　どのタイミング（__commit,push__ 等）で、circleciが走るか（は、github側で設定できる。
　対象リポジトリの『Settings』へ行き、サイドバーの『Webhooks』の蘭で、
　`https://circleci.com/hooks/github...`
という項目ができているので、それをクリックすれば設定できる。

# 戸惑った点
① yaml の構文によるSyntax エラーの発生。（rubyでいう）ハッシュに対して、インデントが足りてなかった。

___ダメな例___

```.yml
- run:
  name: some name
  command: some command
```

___良い例___

```.yml
- run:
    name: some name
    command: some command
```

　
②  [executors](https://circleci.com/docs/ja/2.0/executor-types/) 周り。 DRYに書こうとして、共通部分である

```.yml:.circleci/config.yml
docker:
  - image: circleci/ruby:2.7.1-node-browsers
    environment:
      DB_HOST: 127.0.0.1
      DB_PASSWORD: root
```

を executors を使用してまとめようとしたが、結果、`test` Job の mysqlのコンテナがプライマリとなり、

```
#!/bin/bash -eo pipefail
yarn install --check-files

/bin/bash: yarn: command not found

Exited with code exit status 127
CircleCI received exit code 127
```

というエラーが発生したため、実現は叶わなかった。（しかもexecutorに指定したrubyコンテナが反映されているかもわからなかった）


# まとめ
　今まで、会社の方では、用意されていたCircleCIを使用してテストしてもらっていたけど、今回実際に自分で構築してみて、（Capistrano と比較して）思ったほど難しくないと思った。