capistrano3を利用してRails4をdeployしunicornを使う

December 2, 2014

Rails v4.1.2
capistrano v3.3.3

TL;DR

cap3でrails4のデプロイとbundle install
unicornの操作をできるようにするまでのメモ

手元のマシンでcap3をインストール

$ gem install capistrano

Railsアプリケーションのあるパスに移動

$ cd app_name

cap install

$ cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
$ 

app_name/config配下にdeploy用の各種設定テンプレートが格納され、
独自rake taskを格納するapp_name/lib/capistrano/tasks/ディレクトリが作成され、
capistrano用の設定ファイル?であるCapfileのひな形が作成される.

deployの設定

config/deploy.rb

汎用的な設定はここに書くのかな

lock '3.3.3'

# 自分のアプリケーション名
set :application, 'app_name'

# 自分のリポジトリ名.capistranoはrsyncではなくgit pullする
set :repo_url, 'git@github.com:kenjiskywalker/app_name.git'

# デプロイ先
set :deploy_to, '/home/foo/app_name'

# cap stage unicorn:start などの実行対象の role . デフォルトは app
set :unicorn_roles, :all

set :ssh_options, {

# ここのオプションは ssh_config に記載されていれば不要
# port: 2222,
# keys: [File.expand_path('~/.ssh/hoge.key')],

}

# ここは cap install するとコメントアウトで入るのでそのコメントアウトを解除
set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

config/deploy/stage.rb

汎用的な設定はdeploy.rbに書き、stage毎の設定はstage.rbに書くのかな

  • config/deploy/development.rb
# RAILS_ENV の指定
set :rails_env, 'development'

# unicorn で利用する RACK_ENV の指定
set :unicorn_rack_env, 'development'

# ここの user も ssh_config に記載されていれば不要
server 'dev-web1', user: 'foo', roles: %w{web}
  • config/deploy/staging.rb
# RAILS_ENV の指定
set :rails_env, 'staging'

# unicorn で利用する RACK_ENV の指定
set :unicorn_rack_env, 'staging'

# ここの user も ssh_config に記載されていれば不要
server 'stg-web1', user: 'foo', roles: %w{web}
  • config/deploy/production.rb
# RAILS_ENV の指定
set :rails_env, 'production'

# unicorn で利用する RACK_ENV の指定
set :unicorn_rack_env, 'production'

# ここの user も ssh_config に記載されていれば不要
server 'web1', user: 'foo', roles: %w{web}

Gemfile

capistranoで利用するgemを追加

group :development, :staging, :production do
  gem 'capistrano'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano3-unicorn'
  gem 'unicorn'
end

Capfile

consoleとbundlerとunicornを有効化

# Load DSL and Setup Up Stages
require 'capistrano/setup'

# Includes default deployment tasks
require 'capistrano/deploy'
require 'capistrano/console' # cap stage console とかやると便利

# Includes tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
#   https://github.com/capistrano/rvm
#   https://github.com/capistrano/rbenv
#   https://github.com/capistrano/chruby
#   https://github.com/capistrano/bundler
#   https://github.com/capistrano/rails
#
# require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler' # deploy:updated の前に bundle install してくれる
require 'capistrano3/unicorn' # cap3で unicorn を操作できるようにしてくれる
# require 'capistrano/rails/assets'
# require 'capistrano/rails/migrations'

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

config/unicorn/stage.rb

:unicorn_config_path を参考にすると
config/unicorn.rbではなくconfig/unicorn/satge.rbを記載する

unicorn 設定例 : example.rb

上記設定例を参考に設定.取り敢えず変更箇所はapp_pathぐらい

  • config/unicorn/stage.rb
# paths
app_path = "/home/foo/app_name"
working_directory "#{app_path}/current"
pid               "#{app_path}/current/tmp/pids/unicorn.pid"

# listen
listen "/tmp/hoge-api.socket", :backlog => 64

# logging
stderr_path "log/unicorn.stderr.log"
stdout_path "log/unicorn.stdout.log"

# workers
worker_processes 3

# use correct Gemfile on restarts
before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{app_path}/current/Gemfile"
end

# preload
preload_app true

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end
end

動作チェック

deploy

$ bundle exec cap development deploy:check
$ bundle exec cap development deploy
$ bundle exec cap staging deploy
$ bundle exec cap production deploy

unicorn

$ bundle exec cap development unicorn:start
$ bundle exec cap development unicorn:legacy_restart
$ bundle exec cap development unicorn:stop

学び

rsyncではなくgit pullなので混乱することがある.
currentやrelease, sharedなど見たらわかる面白構造.
当たり前だけど上手くいかない時は手を動かすよりドキュメントとコードを読んだ方が早い.

以上簡単なメモです.