Capistrano3.xを初めから丁寧に

Capistranoって、2.x系と3.x系の情報がWeb上で入り混じってて、情報の整理に、現在苦労している方（または、過去に苦労したよーって方）、いらっしゃいませんか？
自分は、当初、困惑しました。。。

そこで、手順をまとめてみました。


### 1. Gemの追加

* `Gemfile` に次のGemを追加

```rb
# これを使用しないと、デプロイ時にエラーが発生する
gem 'therubyracer',  platforms: :ruby

# cronを使う場合のみ
gem 'whenever', require: false

group :deployment, :test do
  gem 'capistrano', '~> 3.2.1'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano3-unicorn' # unicornを使っている場合のみ
end

gem 'unicorn'
```

* bundle installを実行

```console
$ bundle install
```


### 2. Capistranoの設定ファイルを生成

```console
$ cap install
```

* 以下のファイルが作られる。

```console
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
Capifie
```

### 3. Capfileの設定

* `Capfile` を以下のように変更

```rb
# [必須] Capistranoの設定を読み込む。おまじない
require 'capistrano/setup'

# [必須] デプロイフレームワークを読み込み。
require 'capistrano/deploy'

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

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

require 'capistrano/bundler'
require 'capistrano3/unicorn' # unicornを使っている場合のみ
require 'whenever/capistrano' # wheneverを使っている場合のみ


# [必須] `lib/capistrano/tasks' に定義されたタスクを読み込む
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
```

### 4. Capistrano 共通のデプロイ設定

* 共通のデプロイ情報を `config/deploy.rb` に記入

```rb
# config valid only for Capistrano 3.1
lock '3.2.1'

# アプリケーション名
set :application, 'first_app'
# githubのurl。プロジェクトのgitホスティング先を指定する
set :repo_url, 'git@github.com:YOUR_ACCOUNT/your_app.git'

# Default branch is :master
# deploy時にブランチを選択したい場合は、以下のコメント部分を外す
# ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call

# デプロイ先のサーバーのディレクトリ。フルパスで指定
set :deploy_to, '/path/to/dir/your_app'
# Version管理はgit
set :scm, :git

# ログを詳しく表示
set :format, :pretty
set :log_level, :debug

# sudo に必要
set :pty, true

# 何世代前までリリースを残しておくか
set :keep_releases, 5

#rbenvをシステムにインストールしたか? or ユーザーローカルにインストールしたか?
set :rbenv_type, :user # :system or :user
# rubyのversion
set :rbenv_ruby, '2.1.4'
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 :rbenv_roles, :all # default value

# デプロイ先のサーバーの :deploy_to/shared/config/database.yml のシンボリックリンクを
# :deploy_to/current/config/database.yml にはる。
# ただ、注意すべきは、先にshared以下にファイルをアップロードする必要があること
# 上記のファイルアップロード処理は、下記の「upload」タスクで行う
set :linked_files, %w{config/database.yml}

# 同じくsharedに上記のディレクトリを生成し、currentにシンボリックリンクを張る
set :linked_dirs, %w{bin log tmp/backup tmp/pids tmp/cache tmp/sockets vendor/bundle}

# bundle installの並列実行数
set :bundle_jobs, 4


namespace :deploy do

  # 上記linked_filesで使用するファイルをアップロードするタスク
  desc 'Upload database.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/database.yml', "#{shared_path}/config/database.yml")
    end
  end

  desc 'Restart application'
  task :restart do
    on roles(:app) do
      invoke 'unicorn:restart'
    end
  end
end

# linked_filesで使用するファイルをアップロードするタスクは、deployが行われる前に実行する必要がある
before 'deploy:starting', 'deploy:upload'
# Capistrano 3.1.0 からデフォルトで deploy:restart タスクが呼ばれなくなったので、ここに以下の1行を書く必要がある
after 'deploy:publishing', 'deploy:restart'
```


### 5. 環境別のデプロイ設定

* 環境ごとに異なる設定を `config/deploy/staging.rb(production.rb)` に記述

```rb
set :stage, :staging
set :rails_env, "staging"
set :unicorn_rack_env, "staging"

# この設定がないと、デプロイ先でdb:migrateされない
set :migration_role, 'db'

role :app, %w{USER_NAME@IP_ADDRESS}
role :web, %w{USER_NAME@IP_ADDRESS}
role :db,  %w{USER_NAME@IP_ADDRESS}, :primary => true
#role :db,  %w{USER_NAME@IP_ADDRESS}

server 'IP_ADDRESS', user: 'USER_NAME', roles: %w{web app db}

set :ssh_options, {
    keys: [File.expand_path('/key/path/to/')],
    forward_agent: true,
    auth_methods: %w(password),
    password: 'password'
}
```

### 6. Unicornの設定ファイルを作成

* web上には、`lib/capistrano/task` の直下に、`unicorn.rb`ファイルを作成し、unicornの起動や停止コマンドを書くものもある。  
しかし、以下のような設定ファイルを記載する方法が最近のトレンドのようです。

* configディレクトリの直下に、unicorn.rbを作成していた場合は、削除

```console
$ cd config
$ rm unicorn.rb
```

* 環境ごとの設定を格納するディレクトリを作成

```console
$ cd config
$ mkdir unicorn
$ cd unicorn
```

* 以下の内容を、`config/unicorn/staging.rb(production.rb)` に記述

```rb
app_path = '/path/to/dir/your_app'

worker_processes 2
working_directory "#{app_path}" + "/current"

# This loads the application in the master process before forking
# worker processes
# Read more about it here:
# http://unicorn.bogomips.org/Unicorn/Configurator.html
preload_app true

timeout 30

# This is where we specify the socket.
# We will point the upstream Nginx module to this socket later on
#listen "#{app_path}/tmp/sockets/unicorn.sock", :backlog => 64
listen "/tmp/unicorn.sock", :backlog => 64

#pid "/tmp/unicorn.pid"
pid "#{app_path}/shared/tmp/pids/unicorn.pid"

# Set the path of the log files inside the log folder of the testapp
stderr_path "#{app_path}/current/log/unicorn.stderr.log"
stdout_path "#{app_path}/current/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|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

  sleep 1
end

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

### 7. デプロイ前のチェックリスト・準備

1. Capistranoの以下の設定ファイルについて、パラメータをすべて設定したか？
	* `config/deploy.rb`
	* `config/deploy/staging.rb`
	* `config/deploy/production.rb` 

2. `config/environments/(staging.rb)production.rb` の `config.assets.compile` 値を `true` にしたか?

3. `config/environments/(staging.rb)production.rb` の`config.serve_static_assets`の値を`true`にしたか?
	* これを`true`にしていないと、「**ActionController::RoutingError**」が発生し、画面が表示されない場合がある。

3. Unicornの以下の設定ファイルを準備したか？
	* `config/unicorn/staging.rb`
	* `config/unicorn/production.rb `

4. ステージングDB、本番DBの設定を、`config/database.yml` に追加しているか？
5. ステージング、本番用のシークレットキーの設定を `config/secret.yml` に追加しているか？
	* 設定していないと、画面が真っ白になります。  
	* シークレットキーの生成コマンドは 
	
		```
	$ bundle exec rake secret
		``` 
6. デプロイ用Gitリポジトリのブランチが最新の状態か？

### 8. 上のチェックが完了したら、capコマンドで設定を確認。

* フォルダの生成等も行ってくれます。

```console
# stagingのチェック
$ cap staging deploy:check

# productionのチェック
$ cap production deploy:check
```

### 9. デプロイを実行

```console
# stagingへのデプロイ
$ cap staging deploy

# productionへのデプロイ
$ cap production deploy
```

### 10. 以下のエラーが吐かれてしまう場合の対処

* デプロイ先のサーバーで、以下のコマンドを実行

```console
$ yum install postgresql-devel
```

* エラー内容

```console
DEBUG [52c45f3f] 	[31mAn error occurred while installing pg (0.18.1), and Bundler cannot continue.
DEBUG [52c45f3f] 	Make sure that `gem install pg -v '0.18.1'` succeeds before bundling.[0m
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as root@192.168.33.12: bundle exit status: 5
bundle stdout: An error occurred while installing pg (0.18.1), and Bundler cannot continue.
Make sure that `gem install pg -v '0.18.1'` succeeds before bundling.
bundle stderr: Nothing written

SSHKit::Command::Failed: bundle exit status: 5
bundle stdout: An error occurred while installing pg (0.18.1), and Bundler cannot continue.
Make sure that `gem install pg -v '0.18.1'` succeeds before bundling.
bundle stderr: Nothing written

Tasks: TOP => deploy:updated => bundler:install
(See full trace by running task with --trace)
The deploy has failed with an error: #<SSHKit::Runner::ExecuteError: Exception while executing as root@192.168.33.12: bundle exit status: 5
bundle stdout: An error occurred while installing pg (0.18.1), and Bundler cannot continue.
Make sure that `gem install pg -v '0.18.1'` succeeds before bundling.
bundle stderr: Nothing written
```

いかがでしたか？参考になれば幸いです。