Capistranoを使用したデプロイについて-設定手順総まとめ

Capistranoを使用したデプロイを学習したので、備忘録もかねて、以下にまとめます。

#Capistranoとは
Capistrano
自動デプロイツールと呼ばれるものの一種で、自動デプロイツールを使用することで、デプロイ時に必要なコマンド操作が1回で済むようになります。これにより、手動でデプロイする場合に起こりがちな下記の問題を解消することができます。

以前に自動デプロイツールを使用しないデプロイ方法について、Qiitaに記事を投稿しましたが、非常に記述も多く、ミスの生じやすかったかと思われますので、自動デプロイツールはありがたいですね。

それでは、早速Capistranoを使用してみましょう。

#Capistranoを使用するための準備
まず、Gemfileを編集します。

```rb:Gemfile
group :development, :test do
  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-unicorn'
end
```

続いて、ターミナルでいくつかコマンドを実行します。

```:terminal(ローカル）
$bundle install

#関連ファイルを生成します。
$bundle exec cap install
```

上記コマンドで生成されるファイルは以下の通りです。

```
railsルート
├─	Capfile
├─  config
│	├─	deploy
│	│	├─production.rb
│	│	└─staging.rb
│	└─deploy.rb
└─	lib
	└─capistrano
		└─tasks
```

1.利用するライブラリを指定するファイル
Capfile
Capistranoの機能を提供するコードはいくつかのライブラリ(Gem)に分かれているため、Capistranoを動かすにはいくつかのライブラリを読み込む必要があります。Capfileで、Capistrano関連のライブラリのどれを読み込むかを指定します。
Capfileは、capistrano全体の設定ファイルとなります。

2.デプロイについての設定を書くファイル
deploy.rb、production.rb、staging.rb
Githubへの接続に必要なsshキーの指定、デプロイ先のサーバのドメイン、AWSサーバへのログインユーザー名、サーバにログインしてからデプロイのために何をするか、といった設定を記載します。

それでは、各ファイルに記述をしていきましょう。

###capfileの編集
下記が最初から記載されているので、必要に応じてコメントアウトを外しましょう。

```rb:capfile
# Load DSL and Setup Up Stages
require 'capistrano/setup'

# Includes default deployment tasks
require 'capistrano/deploy'

# 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/tree/master/assets
#   https://github.com/capistrano/rails/tree/master/migrations
#
# require 'capistrano/rvm' #rvmならこちらを外す
require 'capistrano/rbenv' #rbenvならこちらを外す
set :rbenv_type, :system # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.1.0' #rubyのバージョンを指定
# require 'capistrano/chruby'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
# Dir.globメソッドを使用することで、引数としておかれた文字列が指すディレクトリが読み込むようにします。
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

###デプロイについての設定を記載するファイル(staging.rb/production.rb)の編集

```rb:config/deploy/production.rb
server '用意したElastic IP', user: 'ec2-user', roles: %w{app db web}
```

```rb:config/deploy.rb
# capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する
# Gem.lockファイル内にcapstranoのバージョンの記載あり
lock 'Capistranoのバージョン'

# Capistranoのログの表示に利用する
set :application, '自身のアプリケーション名'

# どのリポジトリからアプリをpullするかを指定する
set :repo_url,  'git@github.com:Githubのユーザー名/レポジトリ名.git'

# バージョンが変わっても共通で参照するディレクトリを指定
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

set :rbenv_type, :user
set :rbenv_ruby, 'このアプリで使用しているrubyのバージョン' 
#ターミナル上、ruby -vでチェックできます。

# どの公開鍵を利用してデプロイするか。
# パスはターミナルでcd .sshに移動後、lsコマンドで確認できます。
set :ssh_options, auth_methods: ['publickey'],
                  keys: ['<ローカルPCのEC2インスタンスのSSH鍵(pem)へのパス（例：~/.ssh/key_pem.pem）>'] 

# プロセス番号を記載したファイルの場所
set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" }

# Unicornの設定ファイルの場所
set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" }
set :keep_releases, 5

# デプロイ処理が終わった後、Unicornを再起動するための記述
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end
```
ファイル内を確認すると、set: 名前, 値といった記述が多く使われていますが、これはDSLの一種です。

DSL
ある特定の処理における効率をあげるために特化した形の文法を擬似的に用意したプログラム。
上記のset :名前, 値、これは変数のようなものです。
例えばset: Name, 'value' と定義した場合、fetch Name とすることで 'Value'が取り出せます。
一度setした値はdeploy.rbやproduction.rbなどの全域で取り出すことができます。

##Capistrano実行の対応
Capistranoを実行時に、下記のような配置が実行されます。
そのため、下記に合わせた記述を行う必要がございます。

```
railsルート
├─current #releases内最新のシンボリックリンク
├─	releases #配下に現在時刻に基づく名前のサブディレクトリが作成され最新コードがチェックアウトされる
└─	shared #リリース間で共用するファイルを置いておく
```

上記のようにCapistranoを使った場合には、Railsのアプリケーションのディレクトリが1段階深くなっているため、それを考慮した記述が必要となります。
まずはunicornの記述をしましょう。app_pathに関連する記述に注意しましょう。

```rb:config/unicorn.rb
#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../../', __FILE__)

#アプリケーションサーバの性能を決定する
worker_processes 1

#アプリケーションの設置されているディレクトリを指定。currentを指定します。
working_directory "#{app_path}/current"

#Unicornの起動に必要なファイルの設置場所を指定。sharedの中を参照するようにします。
pid "#{app_path}/shared/tmp/pids/unicorn.pid"

#ポート番号を指定。sharedの中を参照するようにします。
listen "#{app_path}/shared/tmp/sockets/unicorn.sock"

#エラーのログを記録するファイルを指定。sharedの中を参照するようにします。
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"

#通常のログを記録するファイルを指定。sharedの中を参照するようにします。
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"

#Railsアプリケーションの応答を待つ上限時間を設定
timeout 60

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

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

  if run_once
    run_once = false # prevent from firing again
  end

  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exist?(old_pid) && server.pid != old_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 => e
      logger.error e
    end
  end
end

after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end
```

続いてnginxの設定ファイルに関しても、以下のように記述しましょう。
Unicorn同様階層が深くなっているので、今回生成されるcurrent、sharedなどのディレクトリと連携するように設定する必要があります。

```:terminal(EC2サーバー)
$ sudo vim /etc/nginx/conf.d/rails.conf

#vimコマンド'i'で挿入モードにして、以下の記述をしましょう。

upstream app_server {
  # sharedの中を参照
  server unix:/var/www/アプリケーション名/shared/tmp/sockets/unicorn.sock;
}

server {
  listen 80;
  server_name Elastic IP;

  # currentの中を参照
  root /var/www/アプリケーション名/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
    # currentの中を参照
    root   /var/www/アプリケーション名/current/public;
  }

  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
}

#記載が完了したら、escしてから、vimコマンド':wq'で保存しましょう。
#全ての記述を消したい時はvimコマンド':%d'で全削除できます。

```

nginX編集後は、再起動をしましょう。

```:terminal(EC2サーバー)
$ sudo service nginx reload
$ sudo service nginx restart
```

いよいよデプロイ作業となりますが、下記項目をチェックしましょう。
#デプロイ前のチェック
##1.MySQLの起動を確認
MySQLが立ち上がっていないとデプロイが失敗します。以下のコマンドで再起動をしておきましょう。

```:terminal(EC2サーバー)
$ sudo service mysqld restart
```

##2.unicornのプロセスをkill
自動デプロイを実行する前にunicornのコマンドをkillしておきましょう。

```:terminal(EC2サーバー)
#まず、プロセスを確認
$ ps aux | grep unicorn

#続いてプロセスをkill
$ kill <確認したunicorn rails masterのPID>
```

##3.ローカルでの修正を全てmasterにpush
ローカルでのコードの変更が、全てmasterにpushされていることを確認しておきましょう。

以上で準備は完了です！デプロイしましょう！

#デプロイ実行！
ローカルのターミナルで以下のコマンドを実行しましょう。

```:terminal
# アプリケーションのディレクトリに移動してから実行
$ bundle exec cap production deploy
```
もし、エラーが発生したら、下記を確認しましょう。
-そのままもう一度同じコマンドを実行
*特にbundle installのタスクの際、初めて自動デプロイを実行する場合は、メモリ不足で落ちることがあります

-記述ミスが無いか

-手順を飛ばしていないか
*bundle installなどのコマンド実行を忘れていないか確認しましょう

#最後に
以上で自動デプロイの設定は完了となります！
設定後の今後は、ローカルのターミナルからコマンド1つでデプロイし、アプリケーションを安全に更新することが可能となります！

今後のアプリケーションの更新の流れは以下です。
1.アプリをローカルで更新し、リモートリポジトリにプッシュする
2.ローカルのプロジェクトのディレクトリにいる状態のターミナルでbundle exec cap production deploy を実行する

以上となります。最後までご覧いただき、ありがとうございました！
今後も学習した事項に関してQiitaに投稿していきますので、よろしくお願いします！
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。

#参照
RubyのDir.glob の使い方を現役エンジニアが解説【初心者向け】
https://techacademy.jp/magazine/19800

Capistrano3でrailsをdeployしてみる
https://qiita.com/ea54595/items/12ab7b3a8213b35cca10