#はじめに
どうも、Pirikaraです。
久しぶりの投稿となりました。
今回は、Docker環境でRailsアプリケーションを開発し、
CircleCIで自動テスト、Herokuにデプロイするところまでやっていきたいと思います。
個人開発でやってみましたが、このエラーの山々......
一つ一つエラーを解決して設定ファイルを修正してを繰り返し繰り返し......
やっとまともに動くようになりました。
初めてやるよって方は僕の屍を踏み越えていってください。
ちなみに間違ってるよーとか改善点とかご指摘いただけると幸いです。
#環境
・Mac OS
・Ruby 2.5.3
・Rails 5.2.2
・MySQL 5.7
また、Dockerがインストールされていることを前提としています(Dockerコマンドが使用できる状態)。
インストールしていない場合は、公式サイトからアカウントを作ってログインし、DockerHubからダウンロード・インストールします。
Dockerhub
Herokuについても登録していない場合は登録の必要があります。
公式サイトから登録が可能です。
Heroku
開発環境からDockerを導入し、RailsとMySQLのimageでコンテナを作りました。苦労していた環境構築がすぐに出来て感動。
GithubにpushするとCircleCIによる自動テストが行われて、テストをパスするとHerokuに自動でデプロイされる仕様です。
また、Herokuでは有料であればMySQLが使用できるみたいですが、僕はお金がないので本番環境のみPostgreSQLを使用します。
では、
- Docker開発環境の構築
- CircleCIの導入と自動テスト
- Herokuに自動デプロイ
の順番でやっていきましょう。
1.Docker開発環境の構築
まず、アプリケーションのディレクトリを作成します。
ここではsample_appとします。
sample_appディレクトリを作成
$ mkdir sample_app
sample_appディレクトリに移動
$ cd sample_app
ディレクトリ内に「Dockerfile」「docker-compose.yml」「Gemfile」「Gemfile.lock」の4ファイルを作成します。
$ touch Dockerfile
$ touch docker-compose.yml
$ touch Gemfile
$ touch Gemfile.lock
では、それぞれ中身を書いていきましょう。(Gemfile.lockは空のままです)
FROM ruby:2.5.3
#必要なパッケージのインストール
RUN apt-get update -qq && \
apt-get install -y build-essential \
libpq-dev \
nodejs
#作業ディレクトリの作成
RUN mkdir /sample_app #自身のアプリディレクトリ名を設定
#作業ディレクトリをAPP_ROOTに割り当てる
ENV APP_ROOT /sample_app #自身のアプリディレクトリ名を設定
WORKDIR $APP_ROOT
#ローカルのGemfileを追加
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock
#Gemfileのbundle installを実行
RUN bundle install
ADD . $APP_ROOT
version: '3'
services:
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: root
ports:
- "4306:3306"
web:
build: .
command: rails s -p 3000 -b '0.0.0.0'
environment:
RAILS_ENV: development
volumes:
- .:/sample_app #自身のアプリディレクトリ名を設定
ports:
- "3000:3000"
links:
- db
source 'https://rubygems.org'
gem 'rails', '5.2.2'
ところどころ違いますが、
設定の内容についてはこちらの記事が詳しくて参考になったので、任せます。
DockerでRuby on Railsの環境構築を行うためのステップ【Rails 6対応】
ここまで書けたら、dockerコマンドでrails newします。
$ docker-compose run web rails new . --force --database=mysql --skip-bundle
あとでimageを構築する際にDockerfileにしたがってbundle installが実行されるので、ここではスキップします。
作成されたconfig/database.ymlを編集します。
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password # docker-compose.ymlのMYSQL_ROOT_PASSWORD
host: db # docker-compose.ymlのservice名
dockerを起動します。
imageを構築(コンテナは作成しない)
$ docker-compose build
コンテナを構築・起動
$ docker-compose up
データベースを作成します。
$ docker-compose run --rm web rails db:create
--rmオプションをつけることで、コンテナが実行されたあと削除されます。
「rails g controller ~」や「rails console」など、docker-composeから始まるコマンドで実行していくことになりますが、
--rmをつけていないと実行するたびにコンテナが増えていってしまうので、都度削除しています。僕は。
ここまでできると、localhost:3000へアクセスしてサーバーの起動を確認することができます。おめでとう。
2.CircleCIの導入と自動テスト
こちらのブログをパク・・・参考にしました。
導入まで大変わかりやすく、参考になりました。こちらを参考に導入してみてください。
また、今回テストはRspecで実装しますのでRspecを導入しておいてください。
「.circleci」というディレクトリを作成し、これ以下に「config.yml」というファイルを作成します。
また、configディレクトリ以下に「database.yml.ci」というファイルを作成します。
では、作成したファイルの中身を書いていきましょう。
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5.3-node-browsers
environment:
- BUNDLER_VERSION: 2.0.2
- RAILS_ENV: 'test'
- image: circleci/mysql:5.7
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.0.2
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: Databasesetup
command: |
bundle exec rake db:create
bundle exec rake db:schema:load
# run tests!
- run:
name: Run rspec
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
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
test:
adapter: mysql2
encoding: utf8
pool: 5
username: 'root'
port: 3306
host: '127.0.0.1'
database: sample_app_test #database.ymlのテスト環境のデータベース名を参照
これでGithubへpushすると、自動でテストが実行されるようになります。
プルリクエストを作ってmasterブランチへmergeしていくと思うのですが、テストに失敗するとmergeすることができません。
ほらね。
テストをパスすると、Mergeボタンがアクティブになります。
※ seeds.rbを読み込みたい場合
.circleci/config.ymlでseeds.rbの読み込み設定をしたところ、うまくいかなかったのでspec_helperにseeds.rbを読み込む設定をしました。
「database_cleaner」というgemを導入し、spec_helperへ処理を書いていきます。
これで、Rspec実行の際にデータベースがリフレッシュされ、seeds.fileが読み込まれます。
Githubへpushした際に実行された場合は大丈夫なのですが、ローカルでdocker-composeコマンドからrspecを実行した場合にデータベースがリセットされてしまうので注意してください。
group :development, :test do
# rspec実行時にDBをリセットする
gem 'database_cleaner'
end
# テスト実行時にDatabaseをリセットし、seeds.rbを読み込む
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
Rails.application.load_seed
end
end
3.Herokuに自動デプロイ
Herokuにデプロイするため、ファイルを修正・追加します。
まずはGemfile。
開発環境ではMySQL、本番環境ではPostgeSQLを使用するので、「pg」のgemをインストールします。
# 変更前
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
# 変更後
gem 'mysql2', '>= 0.4.4', '< 0.6.0', groups: %w(test development), require: false
gem 'pg', '~> 0.19.0', group: :production, require: false
次に、database.ymlを編集していきます。
本番環境でPostgreSQLを使う設定をします。
production:
<<: *default
adapter: postgresql
encoding: unicode
pool: 5
そして、「heroku.yml」というファイルを新たに作成します。
作成場所はアプリ直下です。(GemfileとかDockerfileと一緒)
今回はDockerコンテナを使用して開発をしているので、コンテナごとHerokuのサーバーに持っていきます。
そのための設定ファイルです。知らんけど。
DockerコンテナをHerokuにアップする方法は公式サイトを参考にしました。
build:
docker:
web: Dockerfile
run:
web: bundle exec puma -C config/puma.rb
アプリケーション側の設定はこれで完了です。
ではHeroku側の設定をしてデプロイしていきましょう。
ターミナルから、Herokuにログインします。
$ heroku login
# ログインするかどうか確認されます。
# クリックするとブラウザが立ち上がり、Herokuのログイン画面になります。ログインしてください。
heroku: Press any key to open up the browser to login or q to exit:
# ログインが完了すると、ターミナルに「ログインしたよー」って表示されます。
Opening browser to https://cli-auth.heroku.com/auth/browser/**************
Logging in... done
Logged in as *******@email.com
次に、Herokuにアプリケーションを作成します。
# アプリケーション名のところには好きな名前を入れてください。それでURLが作成されます。
$ heroku create アプリケーション名
Creating app... done, ⬢ *******
https://******.herokuapp.com/ | https://git.heroku.com/*******.git
ちなみにこの時、Herokuのリポジトリが作成されています。
もしアプリケーションの名前を変更したいときは、Herokuの管理画面から名前を変更することに加えて、リポジトリを削除する必要があります。
$ git remote rm heroku
このコマンドで消せます。
消さないとアプリ名とリポジトリ名が異なるためにデプロイできなくなります。
そして、Heroku側でPostgreSQLを使用する設定をします。
$ heroku addons:create heroku-postgresql:hobby-dev
# PostgreSQLのデータベース作ったよーって通知がきます。
Creating heroku-postgresql:hobby-dev on ⬢ ******... free
Database has been created and is available
! This database is empty. If upgrading, you can transfer
! data from another database with pg:copy
Created postgresql-transparent-70433 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation
今回DockerコンテナをHerokuにのせるので、Heroku側でコンテナにアプリのStackをセットします。
$ heroku stack:set container
この辺の話は「heroku.yml」の設定の時に出てきた公式サイトに全部載ってます。
Dockerを使用したデプロイ
これでデプロイするための設定は完了しました。
masterブランチにアプリをpushしたのち、
$ git push heroku master
このコマンドでHerokuのリポジトリにpushすることでデプロイが完了します。エラーが起こらなければ。
さて、ついに自動デプロイです。
circleciを通してHerokuにデプロイしていくので、config.ymlファイルにdeployの処理を追加します。
# 省略
- deploy:
name: Deploy Master to Heroku
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master
fi
「HEROKU_API_KEY」と「HEROKU_APP_NAME」については、CircleCIに設定した環境変数が読まれるため、
こちらを設定していきます。
どちらもHerokuの管理画面から取得できます。
アカウント設定からHEROKU_API_KEYに当たるAPI KEYを......
アプリケーションの設定からHEROKU_APP_NAMEを......
それぞれ取得します。
CircleCIのアプリケーション管理画面から、右上の「Project Setting」をクリックします。
Environment Variablesから「Add Variables」をクリックして、先ほど取得した値を環境変数として設定します。
これがconfig.ymlで使用されます。
設定は以上です。
これで、
Githubへpush → CircleCiが自動でテスト → パスすればHerokuに自動でデプロイ
という風に動きます。良かったね。
##終わりに
DockerとCircleCIを利用したのは初めてでしたが、
環境構築もテスト・デプロイも簡潔になったので、非常に便利でした。
特にテストコマンドやデプロイコマンドについては「コマンド打つだけやしなぁ......」と自動化の恩恵を甘く見ていましたが、
チリも積もればなんとやらです。
いちいちコマンドを打つ煩わしさから解放され、アプリの開発スピードも段違いだったように感じます。
参考にしていただけたら幸いです。
またね。