Edited at

Capistrano3でUnicorn+Nginxな環境にRailsをデプロイする:初心者向け

More than 3 years have passed since last update.

この手の記事は、もう散々出尽くしている感が満載ですが、そんな記事を見ても1発で完了できず、微妙に考え方の違う複数ある記事をつまみ食いして死亡という、スキル不足な初心者にありがちなパターンを王道でいってしまったので、初心者が初心者に送る一つの道標になればと。


前提


  • Capistrano v3.2.1

  • Rails v4.1

  • rbenv v0.4.0

  • Ruby v2.1.2

  • Unicorn v4.8.3

  • Nginx v1.4.7

  • Git

  • GitHub

こんな感じで動作確認済み。

Webサーバ(nginx)とAppサーバ(unicorn)は同一のステージングサーバにデプロイするのを想定した構築メモです。GitでRailsアプリをバージョン管理し、ローカルからステージングサーバへCapistranoでGitリポジトリ経由でデプロイします。


設定


GitHub

GitHubからデプロイするためにDeploy keysを設定する必要があります。対象リポジトリページの「Settings」→「Deploy keys」に移動し、デプロイ対象サーバの公開鍵を登録します。


Gemライブラリ

デプロイに必要な部分のみ記載しておきます。適宜、アプリケーションに必要なものを追記してください。


Gemfile

gem 'dotenv-rails' #環境変数を簡単に扱うのに使用

group :development, :test do
gem 'capistrano', '~> 3.2.1'
gem 'capistrano-rails'
gem 'capistrano-rbenv'
gem 'capistrano-bundler'
gem 'capistrano3-unicorn' #Unicornの起動を簡単に行える
end

group :production, :staging do
gem 'unicorn'
end


$ bundle install

人によっては、dotenv-railscapistrano3-unicornは使わず、自前で行うかもしれないですが、これを使ったほうが楽だったので。


デプロイ設定

Capistranoは初期設定コマンドがあるので、それを使ってもいいんだけど、使わなくてもいいです。単に初期テンプレートファイルを作ってくれるだけなので。

$ bundle exec cap install


/Capfile

require 'capistrano/setup'

require 'capistrano/deploy'

# rbenvを使用している場合
require 'capistrano/rbenv'

# デプロイ先のサーバで、ユーザディレクトリでrbenvをインストールしている場合
set :rbenv_type, :user
set :rbenv_ruby, '2.1.2'

require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

require 'capistrano3/unicorn'

# Rails4から分離したsecrets.ymlの環境変数を .envファイルで管理する
set :linked_files, %w{config/secrets.yml .env}

# タスクを読み込むけど、今回は特に使わない
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }



/config/deploy.rb

lock '3.2.1'

set :application, 'myapp'
set :repo_url, 'GitのリポジトリURL'
set :branch, 'master'
set :deploy_to, '/var/www/myapp'
set :scm, :git
set :log_level, :debug
set :pty, true
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle public/system public/assets}
set :default_env, { path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH" }
set :keep_releases, 5

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do

desc 'Restart application'
task :restart do
invoke 'unicorn:restart'
end
end


前述したsecret_key_baseを環境変数で設定するため、stagingの項目を追加するのみでOK。


/config/secrets.yml

development:

secret_key_base:
bbbb # 適当

test:
secret_key_base:
aaaa # 適当

staging:
secret_key_base:
<%= ENV["SECRET_KEY_BASE"] %>

production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>


Railsアプリの稼働ディレクトリを仮に/var/www/myappと設定。


/config/unicorn/staging.rb

@app_path = '/var/www/myapp'

working_directory @app_path + "/current"

worker_processes 2
preload_app true
timeout 30
listen "/tmp/unicorn.sock", :backlog => 64
pid "/var/www/myapp/shared/tmp/pids/unicorn.pid"

stderr_path "#{@app_path}/log/unicorn.stderr.log"
stdout_path "#{@app_path}/log/unicorn.stdout.log"

before_fork do |server, worker|
ENV['BUNDLE_GEMFILE'] = File.expand_path('Gemfile', ENV['RAILS_ROOT'])
end

before_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.connection.disconnect!
end

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
end
end
end

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


/config/environments/staging.rbは、特に指定がなければproduction.rbをそのままコピー。


/config/environments/staging.rb

Rails.application.configure do

config.cache_classes = true
config.eager_load = true
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.serve_static_assets = false
config.assets.js_compressor = :uglifier
config.assets.compile = false
config.assets.digest = true
config.log_level = :info
config.i18n.fallbacks = true
config.active_support.deprecation = :notify
config.log_formatter = ::Logger::Formatter.new
config.active_record.dump_schema_after_migration = false
end

デプロイ先のサーバ名とSSHのログインIDを指定。デプロイ元から、秘密鍵でデプロイ先サーバにアクセス可能なのが前提条件です。ssh_optionsで鍵などの細かい指定は可能ですが、そのままでいけるなら、設定不要。


/config/deploy/staging.rb

set :rails_env, "staging"

set :unicorn_rack_env, "staging"

role :app, %w{user@example.com}
role :web, %w{user@example.com}
role :db, %w{user@example.com}

server 'example.com', user: 'user', roles: %w{web app}

set :ssh_options, {
keys: %w(/home/vagrant/.ssh/id_rsa),
forward_agent: false,
auth_methods: %w(publickey)
}



ステージングサーバの環境構築

ステージングサーバ自体がRailsが稼働できるようにしておく必要があります。今どきならChefとかを使ってローカルでもステージングサーバでも同一の環境になっていたりすると思いますが。


環境変数の設定

ただ一つだけ、環境変数の管理を直接サーバで行っておきます。


/var/www/myapp/shared/.env

SECRET_KEY_BASE="hogehoge"



/var/www/myapp/shared/config/secrets.yml

staging:

secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

dotenvというGemライブラリを使っているのだけど、これで使い方が正しいのかはちょっと微妙…。


nginxの設定

nginxは単純にUnicornの前に立ってるだけなのでシンプル。


/etc/nginx/conf.d/hoge.conf

upstream unicorn_server {

server unix:/tmp/unicorn.sock
fail_timeout=0;
}

server {
listen 80;
client_max_body_size 4G;
server_name _;

keepalive_timeout 5;

# Location of our static files
root /var/www/myapp/public;

location ~ ^/assets/ {
root /var/www/myapp/shared/public;
}

location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;

if (!-f $request_filename) {
proxy_pass http://unicorn_server;
break;
}
}

error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/myapp/public;
}
}



デプロイ実行

デプロイ自体は非常に簡単なコマンドです。

$ bundle exec cap staging deploy

実行前に、簡単なチェックも可能。

$ bundle exec cap staging deploy:check

これで、上手く行けばデプロイ先のステージングサーバにUnicornのプロセスが起動しているはずです。psコマンドで確認してみてください。


Unicornの停止と再起動とか


起動

$ bundle exec unicorn -c /var/www/myapp/current/config/unicorn/staging.rb -E deployment -D


停止

$ kill -QUIT `cat /var/www/myapp/shared/tmp/pids/unicorn.pid`


再起動

$ kill -HUP `cat /var/www/myapp/shared/tmp/pids/unicorn.pid`