しふみんのブログ

しふみんのブログです。

DockerとCircleCIを使ってRailsアプリの自動テスト&自動Herokuへのデプロイ環境を作った

はじめに

はい、標題の通りですが、DockerとCircleCIを使ってRailsアプリの自動テスト&自動Herokuへのデプロイ環境のテンプレートを作成しました。

どういうものか

Heroku用のDockerizedされたRailsアプリのテンプレートです。
docker-compse up して rails new してアプリを書いた後に、GitHubにpushすると自動テスト(RSpec)が実行されて、テストがすべて通るとHerokuへと自動デプロイされます。

  • ローカル環境
  • テスト実行
    • GitHubにpushするとCircleCIを通じてローカルと同じ構成のテスト環境でRSpecが実行される
  • デプロイ実行
    • GitHubにpushして上記のテストが全て通るとCircleCIを通じてHerokuへとデプロイされる
  • Heroku
    • ローカルと同じ構成の環境がデプロイされる

なぜ作ったか

ギョームだと、ローカルはVagrantでProductionやStagingはAWSであることが多いのですが、個人開発でいちいちVagrantに環境を整えるは面倒だし本番も雑にHerokuでいいやと思ったので、Docker+HerokuのRailsアプリのテンプレートを作成しました。加えて、楽したいからGitHubにpushしたら自動テストと自動デプロイが走るようにCircleCIの設定を整えました。

作成物

作成物は下記になります。

github.com

続けて、DockerとCirclrCIの設定を見ていきます。

Dockerfile

FROM ruby:2.5.1
ENV LANG C.UTF-8

RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       apt-transport-https \
                       libpq-dev \
                       postgresql-client \
                       --no-install-recommends

# node
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
    && apt-get install -y nodejs

# yarn
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 \
    && apt-get update && apt-get install -y yarn

RUN mkdir /rails_app
WORKDIR /rails_app

COPY Gemfile /rails_app/Gemfile
COPY Gemfile.lock /rails_app/Gemfile.lock
COPY yarn.lock /rails_app/yarn.lock

RUN bundle install --jobs=4 --no-cache
COPY . /rails_app

EXPOSE 3000
RUN RAILS_ENV=production rails assets:precompile
CMD ["rails", "s", "-b", "0.0.0.0"]

Rails用のよく見る形のDockerfileだと思います。
必要なパッケージをインストールして、bundle installして、そして最後にアプリ全体をコピーしています。
rails s する直前で assets:precompile を実行しています。

docker-compose.yml

version: '3'
services:
  web:
    build:
      context: .
    tty: true
    stdin_open: true
    ports:
      - '3000:3000'
    volumes:
      - .:/rails_app
    depends_on:
      - db
  db:
    image: postgres:10.3
    ports:
      - '5432:5432'
    volumes:
      - postgresql-data:/var/lib/postgresql/data
volumes:
  postgresql-data:
    driver: local

こちらもよく見るRails+PostgreSQLの構成のdocker-compose.ymlだと思います。
特記すべき点は、2点あって、
1点目が、Dockerコンテナ内でも binding.pryデバッグ可能にするために下記の設定を追加している点です。

  web:
    tty: true
    stdin_open: true

binding.pry を埋め込んで、 docker-compose run --rm --service-ports web で起動すると他環境と同様にデバッグすることができます。

2点目が、DBのデータを永続化するために下記の設定を追加している点です。

  db:
    volumes:
      - postgresql-data:/var/lib/postgresql/data
volumes:
  postgresql-data:
    driver: local

この設定を追加することによって、DBの中身をローカルに永続化でき docker-compose up を止めてもDBの中身が消えません。

CircleCI2.0

.circleci/config.yml

version: 2
jobs:
  test:
    machine:
      image: circleci/classic:edge
    steps:
      - checkout
      - run:
          name: docker-compose build
          command: docker-compose build
      - run:
          name: docker-compose up
          command: docker-compose up -d
      - run:
          name: sleep for waiting launch db
          command: sleep 1
      - run:
          name: rails db:create and db:migrate
          command: docker-compose run web rails db:create db:migrate
      - run:
          name: rspec
          command: docker-compose run web rspec
      - run:
          name: docker-compose down
          command: docker-compose down

  deploy:
    working_directory: ~/deploy
    machine:
      image: circleci/classic:edge
    steps:
      - checkout
      - run:
          name: Build docker image
          command: docker build --rm=false -t registry.heroku.com/${HEROKU_APP_NAME}/web .
      - run:
          name: Setup Heroku command
          command: bash .circleci/setup_heroku.sh
      - run:
          name: Execute Heroku maintenance on
          command: heroku maintenance:on --app ${HEROKU_APP_NAME}
      - run:
          name: Push container to registry.heroku.com
          command: |
            docker login --username=_ --password=$HEROKU_AUTH_TOKEN registry.heroku.com
            docker push registry.heroku.com/${HEROKU_APP_NAME}/web
      - run:
          name: Execute Heroku db migrate
          command: heroku run rails db:migrate --app ${HEROKU_APP_NAME}
      - run:
          name: Execut Heroku maintenance off
          command: heroku maintenance:off --app ${HEROKU_APP_NAME}

workflows:
  version: 2
  test-deploy:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master

特記すべき点は、 test job, deploy job共に下記のように machine imageに ciecleci/classic:edge を指定していること。

  test:
    machine:
      image: circleci/classic:edge

このように設定することにより、最新のdocker-compseがインストール済みの仮想マシンを使うことができます(楽)

参考

circleci.com

.circleci/setup_heroku.sh

#!/bin/bash
wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz
sudo mkdir -p /usr/local/lib /usr/local/bin
sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib
sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku

cat > ~/.netrc << EOF
machine api.heroku.com
  login $HEROKU_LOGIN
  password $HEROKU_API_KEY
EOF

# Add heroku.com to the list of known hosts
ssh-keyscan -H heroku.com >> ~/.ssh/known_hosts

CiecleCIのworkflowの deploy jobで実行している setup_heroku.sh は下記のリポジトリスクリプトを参考(ほぼそのまま)にさせていただきましたm( )m
内容としては、herokuコマンドを使えるようにして、herokuのログイン設定を設定ファイルに追記して、SSHのknown_hostsを更新しています。

参考

github.com

まとめ

HerokuのRailsアプリ開発環境をDockerとCircleCIを使って作成しました。 Dockerを使うことによって、Rubyのインストールすらせずに素早くローカル開発環境を用意できるだけでなく、開発環境、テスト環境、本番環境で同じDockerイメージを使いまわせて環境の違いによる謎のバグに悩ませることなくRailsアプリの開発を行うことができます。便利ですね。
気が向いたら、このテンプレートを利用して具体的にHerokuでRailsアプリを動かすところを解説する記事を書きたいと思います。

参考

library/postgres - Docker Hub
https://hub.docker.com/_/postgres/

Heroku Container Registryで作るDocker時代のRails 5 入門 - Qiita
https://qiita.com/koduki/items/3aafc38c6c518bef2af3

docker-composeでRailsを開発しCircleCI 2.0でテストしてHerokuにdeployする - Qiita
https://qiita.com/Kesin11/items/47079bc7f659e71b694c

Choosing an Executor Type - CircleCI
https://circleci.com/docs/2.0/executor-types/

Kesin11/docker_rails_sample: Dockernized rails with CircleCI and Heroku
https://github.com/Kesin11/docker_rails_sample