[Rails]自動テスト〜自動デプロイまでをDockerコンテナで実行する方法


こちらの記事の続きです

RSpec on DockerでChromeブラウザテストを行う方法
DockerでのRuby on Rails環境構築を一つずつ詳解する
CircleCI→herokuのCI/CDパイプライン構築手順

手順としては上記記事の続きであり
・Dockerでの開発環境を構築している
・単体テスト・統合テストをDockerコンテナ内で実行できる
・Github→CircleCI→herokuのCI/CDパイプラインを構築している
を前提としています。
今回はこれらを組み合わせて
CicleCIでの自動テスト環境にdocker-composeを利用していきたいと思います。
これにより開発環境、テスト環境、本番環境の環境差分が無くなります。

CircleCIでDocker使用時はMachine Executor

CircleCIでは通常はdocker環境でテストを実行させるように
config.ymlを設定しますがdocker-composeを自分で使いたい場合は
MachineExecutor(VM環境)を選択します。
(CircleCIのDocker環境ではdocker-composeのvolumesが実行できません)

Machine Executorを使ったconfig.yml

config.yml
version: 2.0
jobs:
  build:
    machine: true
    steps:
      - run:
          name: Start container
          command: docker-compose up -d

基本的には
CircleCI→herokuのCI/CDパイプライン構築手順
こちらで書いたconfig.ymlと同じです。
相違点はdockerとimageを書いていた部分を「machine: true」に変更します。
これでCircleCIのLinuxVM上でコマンドが実行されます。

HerokuのContainerRegistryでデプロイ

次にheroku本番環境でもDockerコンテナが実行可能です。
サービス名はContainer Resigtryという名前で下記リンクが公式です。
Container Registry公式

$ heroku container:login --app APP_NAME
$ heroku container:push web --app APP_NAME
$ heroku container:release web --app APP_NAME

上記のコマンドのみでherokuでDockerコンテナを実行することができます。
container:pushで、Dockerfileを元にimage build、とheroku registryへのpushが実行されます。
そしてcontainer:relaseでpushしたイメージがリリースされます。
remoteが複数になるとapp名が必要になるので念のため
--appで明示的に指定しています。

また、初回はAPP,DB作成のため以下のコマンドが必要になります。

$ heroku create app_name
$ heroku addons:create heroku-postgresql:hobby-dev

つまりheroku containerコマンドをcircleci/config.ymlに
記述すればデプロイが可能ですね。

.circleci/config.yml の設定

heroku cointainer:loginが必要なので
公式にあるように以下のコマンドが必要です。

#heroku container:login
docker login --username=_ --password=${HEROKU_API_KEY} registry.heroku.com

.circleci/config.yml 完成版

そして完成版がこちらになります。

.circleci/config.yml
version: 2.0
jobs:
  build:
    machine: true
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          name: Start container
          command: docker-compose up -d

      - run: docker-compose exec web rails db:create
      - run: docker-compose exec web rails 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)"

             docker-compose exec web 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

  deploy_stage:
    machine: true
    steps:
      - checkout
      - run:
          name: install Heroku CLI
          command: curl https://cli-assets.heroku.com/install.sh | sh
      - run:
          name: heroku maintenance on
          command: heroku maintenance:on --app ${HEROKU_APP_NAME_STAGE}
      - run:
          name: deploy to heroku_staging
          command: |
            docker login --username=_ --password=${HEROKU_API_KEY} registry.heroku.com
            heroku container:push web --app ${HEROKU_APP_NAME_STAGE}
      - run:
          name: rails db migrate
          command: heroku run rails db:migrate --app ${HEROKU_APP_NAME_STAGE}
      - run:
          name: release app
          command: heroku container:release web --app ${HEROKU_APP_NAME_STAGE}
      - run:
          name: heroku maintenance off
          command: heroku maintenance:off --app ${HEROKU_APP_NAME_STAGE}

  deploy_prod:
    machine: true
    steps:
      - checkout
      - run:
          name: install Heroku CLI, if necessary
          command: curl https://cli-assets.heroku.com/install.sh | sh
      - run:
          name: heroku maintenance on
          command: heroku maintenance:on --app ${HEROKU_APP_NAME_PROD}
      - run:
          name: deploy to heroku_staging
          command: |
            docker login --username=_ --password=${HEROKU_API_KEY} registry.heroku.com
            heroku container:push web --app ${HEROKU_APP_NAME_PROD}
      - run:
          name: rails db migrate
          command: heroku run rails db:migrate --app ${HEROKU_APP_NAME_PROD}
      - run:
          name: release app
          command: heroku container:release web --app ${HEROKU_APP_NAME_PROD}
      - run:
          name: heroku maintenance off
          command: heroku maintenance:off --app ${HEROKU_APP_NAME_PROD}

workflows:
  version: 2
  build-deploy:
    jobs:
      - build
      - deploy_stage:
          requires:
            - build
          filters:
            branches:
              only:
                - staging
      - deploy_prod:
          requires:
            - build
          filters:
            branches:
              only:
                - master

必要な環境変数は
HEROKU_API_KEY、HEROKU_APP_NAME_STAGE、HEROKU_APP_NAME_PRODです。
それぞれCircleCIのUIから設定してください。

これで自動テストと、検証環境、本番環境への自動デプロイ。
そしてそれらがDockerコンテナで実行されるCI/CDが構築できました。

注意点

Dockerfileに下記の記述が無いとherokuコンテナで
railsが起動しない場合があるので注意が必要です。

Dockerfile

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

ここの設定については
DockerでのRuby on Rails環境構築を一つずつ詳解する
こちらで書いています。