【Rails6】初心者が見様見真似でDockerとCircleCIを既存Railsアプリに導入した結果…

## アプリの環境

- rails6.0.3.6
- ruby2.6.5
- bundler2.1.4
- mysql5.6

## 最初に…僕なりのイメージ

実装してみた感じこういうイメージで捉えておくと良さそう

### Docker関係

- Dockerfile…仮想コンテナの環境構築をするファイル。 `docker componse build` するとここに書いてあるコマンドが実行される。
- docker-compose.yml…仮想コンテナで使用するDBなど詳細設定を行うファイル。 `docker compose up` をするとここに書いてある内容を元にアプリが立ち上がる
- entrypoint.sh…仮想環境を立ち上げる際に最初に読み込まれる内容を決めるファイル

### CircleCI関係

- config.yml…Githubへのpushが行われた際にここに書いてある内容が順に実行される。手順書的な感じ。雰囲気だけはDockerfileに近い

## 【Docker編】やったこと

https://qiita.com/Tomohisa_koyama/items/5f2d32fd0677aa244b72
こちらにruby, rails, mysqlともに同じバージョンのものを発見しましたのでこれを参考にしました。

```dockerfile
FROM ruby:2.6.5

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn 

RUN mkdir /myapp

WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN gem install bundler # これがないとbundlerがないって言われる
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

参考サイトとの違いは `gem install bundler` を実行しているか否か。これは以下サイトを参考にしました。
https://qiita.com/kenz-dev/items/e3d970b59bf106cab19e

#### docker-compose.yml
参考サイト通りです。

```yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    env_file: database.env
    ports:
        - "3306:3306"
    volumes:
        - ./db/mysql/volumes:/var/lib/mysql
  web:
    env_file: web.env
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    stdin_open: true
    tty: true
    volumes:
      - .:/myapp
      - gem_data:/usr/local/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  mysql_data:
  gem_data:
```

【追記】
環境変数が読み込めなくてエラーが起こったので以下サイトを参考に外部ファイルから読み込めるよう設定しました。
https://qiita.com/KEINOS/items/518610bc2fdf5999acf2

#### mysql周りで必要な設定
以下を参考にやりました。
https://qiita.com/nanakenashi/items/180941699dc7ba9d0922
気をつけるべきはMYSQL_ROOT_PASSWORDが必須であることでしょう。
ローカルではパスワードなしでもrootユーザー動かせますがdockerの場合はpasswordなしだと
docker-compose upのときにエラーが出ます。

#### entrypoint.sh
参考サイト通り

```sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

この時点で

```
docker compose build
docker compose up
```

をすると仮想環境が立ち上がるはずです。

## 【CircleCI編】やったこと

CircleCIはちょっと複雑でした。
どうやら事前にcommandオプションで実行する内容を定義してからstepsを書く、というのもあるみたいですね？？

https://qiita.com/AK4747471/items/b2161784065f21cd1645
こちらのサイトがconfig.ymlの記述自体は理解しやすかったのでこれを基準にカスタマイズしました。

#### config.yml

```yml
version: 2.1
orbs:
    slack: circleci/slack@4.3.0

jobs:
  build:
    docker:
    - image: circleci/ruby:2.6.5-node-browsers
      environment:
        - BUNDLER_VERSION: 2.1.4
        - RAILS_ENV: 'test'

    - image: circleci/mysql:5.6
      environment:
        - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
        - MYSQL_ROOT_HOST: '127.0.0.1'

    working_directory: ~/sample_app

    steps:
    - checkout

    - restore_cache:
        keys:
        - v1-dependencies-{{ checksum "Gemfile.lock" }}
        - v1-dependencies-


    - run:
        name: install dependencies
        command: |
          gem install bundler -v 2.1.4
          bundle install --jobs=4 --retry=3 --path vendor/bundle
    - save_cache:
        paths:
        - ./vendor/bundle
        key: v1-dependencies-{{ checksum "Gemfile.lock" }}


    # database setup
    - run: mv ./config/database.yml.ci ./config/database.yml

    # database setup
    - run:
        name: setup database
        command: |
           bundle exec rake db:create
           bundle exec rake db:schema:load

    # install yarn
    - run:
        name: install yarn
        command: yarn install


    #run rubocop
    - run:
        name: Rubocop
        command: |
            bundle exec rubocop -a
            bundle exec rubocop -A


    # run tests
    - run:
        name: run rspec
        command: bundle exec rspec
 

    # collect reports
    - store_test_results:
        path: /tmp/test-results


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

    - slack/notify:
        event: fail
        mentions: '@ユーザー名'
        template: basic_fail_1
    - slack/notify:
        event: pass
        mentions: '@ユーザー名'
        template: basic_success_1
```

このymlファイルを使うと以下を実行できます。

- 自動コード整形(rubocop)

- 自動テスト(rspec)

- Slackへの通知

  

## 苦労した点

特段難しい箇所はなかったですが、CircleCIからSlackへの通知がなかなか飛ばなくて苦戦しました。
Slackへの通知は以下URLから公式の情報を元にやりましょう。というのも昨年あたりにSlackとCircleCIの連携方法が変更になったこともあり、
古い情報を載せているブログなどもあるためです。簡単に言うと、Webhookを使った連携が昔のやり方で、今はOAuthを使って連携しているようです。
SlackAppでcircleciと検索すると公式のアプリが出てきますが、それはもう使われていない？ようです。

https://github.com/CircleCI-Public/slack-orb/wiki/Setup

細かいカスタマイズとかは以下URLが詳しいです。最初はとりあえずtemplateでいいと思いますが、jobsが失敗したときだけ通知したいとかそういう場合は別途設定が必要なので徐々にカスタマイズしていきましょう。

https://circleci.com/developer/orbs/orb/circleci/slack

あとは、環境変数の設定が必要になるのですがどこからやるのか記載がなくちょっと探しました。
Project Setting→Environment Variablesと進むと環境変数をまとめて設定できます。Rspec実行時に必要な環境変数もここで設定しましょう。

## 最後に

初めてのCI導入でしたがいざ導入してみると非常に便利で感動しました。もっと早くしっておけばよかったです。

特に感動したポイントは

- 毎回rubocopとテストの実行を自動でやってくれるのでコードの管理の手間が省ける
- rubocopとテストの実行を抜けもれなくやってくれるのであとになってやり直しをしなくて済む
- Circle CIが頑張ってくれている間に他の作業ができる

といったあたりです。チームで開発しているとコードレビューの手間も省けそうですよね！

以上、参考になれば幸いです。