Capistrano 3系でRails 5.1のデプロイ[rbenv][ruby2.4]


デプロイツールのデファクトスタンダードとなった『Capistrano3』。stagingやproductionといった複数環境へのデプロイを標準で対応していたり、bundleやmigration、pumaとの連携なども抜群です。
かなり乗り遅れてしまいましたが、最近ようやくRails 5.1をcapistrano3系でデプロイしました。ずっとHerokuやElastic Beanstalkにばっかり頼っていたのでちょこちょことハマって苦労しました。今後忘れないための忘備録メモっす!

rbenv, rvmの両方に対応しました。オリジナルのcapコマンドの作り方も書きました!


🚜 前提条件

今回の環境では次のような環境を想定しています。

* DB: PostgreSQL
* Webサーバ: puma
* Rubyのバージョン管理: rbenv
* Cron DSL: whenever

🐹 PostgreSQL DBの準備

デプロイ先のDBの接続用ユーザーとデータベースを作成するために、次のコマンドをstaging環境とproduction環境で実行します。

PostgreSQLの場合

こちらは、PostgreSQLを使う場合の手順です。

# postgreユーザーになる
sudo su -
su - postgres

# パスワード付きのユーザーを作成する
createuser -s [ユーザー名] -P

😀 Gemfileの追加

Gemfileに次のGemを追加して、bundle installを実行。

group :deployment do
# cron management
gem 'whenever', require: false

# Deploy
gem 'capistrano', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-rbenv', require: false
gem 'capistrano3-puma', require: false
end

🐰 Capistranoの設定ファイルを生成

次のコマンドでCapistranoの設定ファイルdeploy.rbらを生成。

bundle exec cap install

🚌 Capfileの設定

Capfileを次のように変更します。

# Load DSL and set up stages
require 'capistrano/setup'

# Include default deployment tasks
require 'capistrano/deploy'

# Load the SCM plugin appropriate to your project:
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git

# Include tasks from other gems included in your Gemfile
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
require 'whenever/capistrano'

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
install_plugin Capistrano::Puma

🗽 Capistrano共通のデプロイ設定

まず、共通のデプロイ情報をdeploy.rbに記入していきます。

lock '3.9.1'

set :repo_url, 'git@xxx.git'
set :application, 'APP_NAME'
set :user, 'APP_USER'
set :puma_threads, [4, 16]
set :puma_workers, 0
set :pty, true
set :use_sudo, false
set :stage, :production
set :deploy_via, :remote_cache
set :deploy_to, 'SERVER_PATH'
set :puma_bind, "unix://#{shared_path}/tmp/sockets/puma.sock"
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.access.log"
set :puma_error_log, "#{release_path}/log/puma.error.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :rbenv_type, :system
set :rbenv_path, '/usr/local/rbenv'
set :rbenv_ruby, File.read('.ruby-version').strip
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w[rake gem bundle ruby rails]
set :linked_dirs, fetch(:linked_dirs, []).push(
'log',
'tmp/pids',
'tmp/cache',
'tmp/sockets',
'vendor/bundle',
'public/system',
'public/uploads'
)
set :linked_files, fetch(:linked_files, []).push(
'config/database.yml',
'config/secrets.yml',
'.env'
)
set :whenever_identifier, -> { "#{fetch(:application)}_#{fetch(:stage)}" }

namespace :puma do
desc 'Create Directories for Puma Pids and Socket'
task :make_dirs do
on roles(:app) do
execute "mkdir #{shared_path}/tmp/sockets -p"
execute "mkdir #{shared_path}/tmp/pids -p"
end
end
before :start, :make_dirs
end

namespace :deploy do
desc 'Make sure local git is in sync with remote.'
task :check_revision do
on roles(:app) do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
end
end
end

desc 'Initial Deploy'
task :initial do
on roles(:app) do
before 'deploy:restart', 'puma:start'
invoke 'deploy'
end
end

desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
invoke 'puma:restart'
end
end

desc 'reload the database with seed data'
task :seed do
on roles(:db) do
with rails_env: fetch(:rails_env) do
within release_path do
execute :bundle, :exec, :rake, 'db:seed'
end
end
end
end

after :migrate, :seed
before :starting, :check_revision
after :finishing, :compile_assets
after :finishing, :cleanup
end

🎂 環境別のデプロイ設定

次に環境ごとに異なる設定をconfig/deploy/staging.rb(production.rb)に記述していきます。

set :branch, 'master'

server 'IP_ADDRESS', user: 'USER_NAME', roles: %w[web app db]

set :ssh_options, {
keys: [File.expand_path('/PATH/TO/SSH_KEY/')],
forward_agent: true,
fetch(:user)
}

🗻 wheneverの設定

サーバ側のcronの設定をコードで簡単に管理できるwheneverは、『Wheneverは導入が超簡単なcrontab管理ライブラリGemです![Rails4.1]』を参考に設定!

🍮 初回インストール

本番環境へのデプロイは次のように実施します。

bundle exec cap production deploy:initial

🎳 デプロイ手順

デプロイに必要なディレクトリを生成して、デプロイを実行。

# productionへのデプロイ
bundle exec cap production deploy

🐠 補足:capコマンドの一覧を表示したい場合

capコマンドの一覧を表示したい場合は次のコマンドを実行します。

bundle exec cap -T

🐯 補足:deploy時にブランチを選択したい場合

set :branck, 'マスタ'の部分を次のように書き直し。

set :branch, `feature/xxx`

🐮 補足:サーバへのファイルのアップロードをしたい場合

namespace :devops do
desc 'Copy files'
task :copy do
on roles(:all) do |host|
%w[file.one file.two].each do |f|
upload! "/path/fo/file/#{f}", "/remote/path/#{f}"
end
end
end
end

🐡 nginxとの連携

🐞 CentOSのセキュリティ改善

😼 参考リンク

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍