Help us understand the problem. What is going on with this article?

(Capistrano編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで

More than 1 year has passed since last update.

世界一丁寧なAWS解説シリーズ 項目表

No. タイトル
1 下準備編
2 DB・サーバー構築編
3 デプロイ編①
4 デプロイ編②
5 〜〜Capistrano編(今ここ)〜〜

世界一簡単なAWS解説シリーズにて、色々とネットワーク系の知識を追いながら、アプリをデプロイするところまで見ていきました。

このシリーズでは諸々の知識を深める為に敢えて難しい方法でデプロイをしたのですが、普段の作業効率を考えると、簡単に越したことは無いですよね?

そこで今回は番外編!
Capistranoと呼ばれるツールを使って、楽にデプロイしちゃおうという趣旨になります。

capistrano.png

こちら、設定さえ出来れば、コマンド一つでアプリのデプロイ、更新に対応してくれるという優れもの。

Capistranoを使いこなして、是非デプロイマスターになってくださいね!
では早速見ていきましょう。

AWS側の設定

各種設定→EC2インスタンスにrubyの環境構築までは、下記を参考に行ってください。
下準備編
DB・サーバー構築編
デプロイ編1 この途中まで

また今回Capistranoでデプロイするにあたって、gitのSSH認証を完了させている必要があります。

本編ではssh-agentという機能を用いてローカル側、サーバー側からログインができるようにしております。
手順に関しては下記にまとめておりますので、本編に入る前にぜひお読みください。
ssh-agentを利用して、安全にSSh認証を行う 

今回はその続きから見ていこうと思います。

Capistranoとは

Capistranoとは、一言で言えばデプロイ自動化ツールです。
そしてその中身を見てみると、多くのタスクの集合であることが分かります。

そしてそれらのタスクを連動させる(Ex: setupのタスク実行 → updateを確認するタスク実行 → updateを実行するタスク実行....) ことで、結果として複雑なデプロイをコマンド1つで行っているという流れとなります。

capistranoの仕組みに関してはこの記事が非常にわかりやすくまとまっておりましたので、気になる方は是非ご一読ください。
入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい

中身の詳細の解説は他の記事に任せるとして、本記事ではcapistrano自動デプロイに際する流れをテーマに、「全体的になんとなくcapistranoの仕様が分かる」ことをゴールとして、「世界一丁寧な解説」を行っていきたいと思います。

さて、早速導入を行っていきましょう。

Capistranoの導入

capistranoと、それに付随するものをgemファイルとしてインストールしていきます。
(※こちらはローカル上での作業になります! お間違えないように)

まずは、gemfileに下記を記述して、bundle installを実行します。

(local)Gemfile
group :development, :test do
 gem 'capistrano'
 gem 'capistrano-bundler'
 gem 'capistrano-rails'
 gem 'capistrano-rbenv'
end

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

ここで一点注意!
ネットの記事を見渡すと、'capistrano3-unicorn'というgemを使用しているものを多数見かけます。 
そして筆者はこのgemのせいで1週間を同じエラーと共に過ごすことになってしまいました...(知識不足と言われればそれまでなのですが。。)
詳細は後述しますが、今回はこのgem無しで話を進めていきたいと思います。

そしてgemファイルへの記述が完了したら、

bundle exec cap install

こちらのコマンドを実行してください。
すると、下記のファイルが生成されます。

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

それぞれ何を行う為のファイルなのか、何を記述すれば良いのか。
簡単に見ていくことにしましょう。

各ファイルの設定

Capfile

こちらは、capistrano全体の設定ファイルとなります。
筆者は下記のように設定しました。

(local)Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv' 
require 'capistrano/bundler'
require 'capistrano/rails/assets' 
require 'capistrano/rails/migrations' 

# require 'capistrano3/unicorn'
# require 'capistrano/rvm'
# require 'capistrano/chruby'
# require 'capistrano/passenger'

# taskを記述したファイルを読み込むよう設定。 場所と拡張子を指定。
Dir.glob('lib/capistrano/tasks/*.rb').each { |r| import r }

require ~~~~

と記述したことにより、~~~~で記述されたものが読み込まれ、そしてその中にデプロイに際して必要な動作が一通り記述されております。
一つ一つのファイルの中身に関しては今回省略しますが、この記述があることによってどのような効果があるのか... くらいは覚えておいた方が良いでしょう。
参考記事(capistrano3のコードを読んで仕組みを理解する)

ステージング環境.rb

cap installを行うと、config/deploy配下に
production.rb staging.rb の2種類のファイルが生成されます。
これらはデプロイする環境別の設定を記述するファイルとなり、具体的には

・サーバーホスト名
・AWSサーバーへのログインユーザー名
・サーバロール(※後述)
・SSH設定
・その他、そのサーバに紐づく任意の設定

などを記述していくこととなります。

今回はproduction環境にデプロイすることを想定しているので、production.rbを下記のように設定しました。

config/deploy/production.rb
# EC2サーバーのIP、EC2サーバーにログインするユーザー名、サーバーのロールを記述
server '00.00.000.000', user: 'naoki', roles: %w{app db web} 

#デプロイするサーバーにsshログインする鍵の情報を記述
set :ssh_options, keys: '~/.ssh/first_aws_rsa' 

role とは、簡単に言えばそのサーバーの役割のことですね。
例えば、

on role(:web) do [taskの内容]

といったtask(デプロイを担う諸々の処理の中の1つ)の記述がされていた場合、そのサーバーのroleがwebと設定されていた場合のみ、指定されたtaskを実行する... といった意味合いとなります。
(ここら辺はcapistrano独自の記述なので慣れていく他ないですね。。)

deploy.rb

ここには、production環境、stading環境どちらにも当てはまる設定を記述することとなります。
具体的には下記。

・アプリケーション名
・gitのレポジトリ
・利用するSCM
・タスク
・それぞれのタスクで実行するコマンド

そしてファイルの中身は下記のように設定しました。

(local)config/deploy.rb
# capistranoのバージョン固定
lock '3.4.0'

# デプロイするアプリケーション名
set :application, 'mumu'

# cloneするgitのレポジトリ
set :repo_url, 'git@github.com:NaokiMochizuki/mumu.git'

# deployするブランチ。デフォルトはmasterなのでなくても可。
set :branch, 'master'

# deploy先のディレクトリ。 
set :deploy_to, '/var/www/mumu'

# シンボリックリンクをはるファイル。(※後述)
set :linked_files, fetch(:linked_files, []).push('config/settings.yml')

# シンボリックリンクをはるフォルダ。(※後述)
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

# 保持するバージョンの個数(※後述)
set :keep_releases, 5

# rubyのバージョン
set :rbenv_ruby, '2.1.3'

#出力するログのレベル。
set :log_level, :debug

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

  desc 'Create database'
  task :db_create do
    on roles(:db) do |host|
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:create'
        end
      end
    end
  end

  desc 'Run seed'
  task :seed do
    on roles(:app) do
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:seed'
        end
      end
    end
  end

  after :publishing, :restart

  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
    end
  end
end

少し理解が難しい箇所だと思いますので、用語や構文の説明を行っていきます。

〜〜シンボリックリンク〜〜

シンボリックリンク とは、一言で言うならばファイルやディレクトリの別名のことです。
...と言われても意味不明だと思うので、少し解説を。

Capistranoを通じたデプロイの流れの中では、実はgit cloneを行っています。
ですが、例えばIDやパスワードなど、gitにプッシュしてしまうとまずいファイル等もありますよね?
その際に役立つのがこのシンボリックリンク。
gitにあげるファイルには「パスワード: [ここの値を読み込んで参照]」と言った具合に記述しておいて、
シンボリックで指定した箇所に、その値を直接書き込むのです。
「ここに書いてあるものとここに書いてるものは同じだよー」
と明示しているのですね。
こうすることで、gitに危ないファイルを上げることなく、且つプログラムも正常に動かすことができます。

set :linked_files, fetch(:linked_files, []).push('config/settings.yml')
と書かれているので、つまりは、
[ここの値を読み込んで参照]と書いた箇所と、config/settings.ymlに書かれている値は一心同体、同じものです。 と設定をしていることになります。

また、capistranoではデプロイ時に自動でsharedというフォルダが作成され、環境変数(上記で説明した一心同体の値)はこのshared以下に保存されることになっています。

今回はこの環境変数の置き場所を、「.push('config/settings.yml')」と指定しているので、デプロイより前にshared/config/settings.ymlというファイルを生成し、そこに環境変数を記載してあげる必要があります。

まずはlocalにてsecret_key_base用の乱数を生成

(local)
[mumu] $: rake secret
jr934ugr89vwredvu9iqfj394vj9edfjcvnxii90wefjc9weiodjsc9oi09fiodjvcijdsjcwejdsciojdsxcjdkkdsv
(#表示されるkeyをコピーする)

そしてサーバー上に立った今生成したsecret_key_baseを登録します。

(server)
[naoki|~] $: cd /var/www/mumu
[naoki|mumu] $: mkdir shared
[naoki|mumu] $: cd shared
[naoki|shared] $: mkdir config
[naoki|cd shared] $: cd config
[naoki|cd config] $: vi settings.yml

新規作成するsettings.ymlには、下記を記述してあげましょう。

(server)shared/config/settings.yml
production:
  secret_key_base: jr934ugr89vwredvu9iqfj394vj9edfjcvnxii90wefjc9weiodjsc9o i09fiodjvcijdsjcwejdsciojdsxcjdkkdsv 
(#ここに先ほど生成した乱数を貼り付け)

以上で環境変数の登録は完了です。

〜〜releasesフォルダ & currentフォルダ

お次はreleasescurrent

capistranoを通じてデプロイされたアプリは、releasesというフォルダにひとまとめにされます。
ここに過去分のアプリが残っていることにより、デプロイ時に何か問題が発生しても一つ前のVerに戻ったりすることが出来るのですね。
そして、その過去分の保存数を指定しているのが、set :keep_releasesの記述となります。今回は5バージョン保存するよう設定しました。

また、このreleasesフォルダの中で一番新しいものが、自動的にcurrentというフォルダ内にコピーされます(正確には一つのファイルを別の箇所から参照しています。)

ですので、このcurrent内に入っているアプリの内容が、現在デプロイされている内容ということになる訳ですね!

〜〜DSL〜〜

また、ファイルの中身を見ていただくと分かるように、多くの記述がDSLで記述されており、
set: 名前, 値 といった形が非常に多く使われています。

これは言わば変数みたいなもので、
例えばset: Name, 'value' と定義した場合、
fetch Name とすることで 'Value'が取り出せます。
また、一度setした値はdeploy.rbやステージ.rbの全域で取り出すことができます。
各設定内容はコメントを参考にしてみてください。

〜〜task〜〜

desc '○○'
task :XX do

といった記述がよく見受けられますね。
これは、先ほどCapfileでrequireしたものに加えて追加のタスクを記述した箇所となり、ここで記述したのものもcap deploy時に実行されることとなります。

※どうやら一度きり実行するものはcap deploy時にtask名を明示してあげないといけないみたいなので、初回のみ

bundle exec cap production deploy:db_create 

とDBを作成するtaskを明示してあげないといけないようです。
いやーCapistranoは謎が深い。
(今回はこのタスクを呼び出すのではなく、サーバーに手打ちでデータベースを作成するという方法を取っております。 ですので、上記のコマンドは、今回に限っては打ち込む必要のないコマンドとなります。)

スペースの関係上deploy.rbに関する説明は以上としますが、「コードは全文意味がわからないと気持ち悪い!」という阿部さん(筆者の上司、チリチリ)みたいな方はgitに詳細書いてるかと思うので参照してみたください。
そして僕に教えてください。

task.rb

ここはunicornのセットアップタスクを記述する箇所になります。
(※deploy.rbから読み込む設定をしていないと動かないので注意!)

(local)lib/capistrano/tasks/unicorn.rb
#unicornのpidファイル、設定ファイルのディレクトリを指定
namespace :unicorn do
  task :environment do
    set :unicorn_pid,    "#{current_path}/tmp/pids/unicorn.pid"
    set :unicorn_config, "#{current_path}/config/unicorn/production.rb"
  end

#unicornをスタートさせるメソッド
  def start_unicorn
    within current_path do
      execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
    end
  end

#unicornを停止させるメソッド
  def stop_unicorn
    execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"
  end

#unicornを再起動するメソッド
  def reload_unicorn
    execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})"
  end

#unicronを強制終了するメソッド
  def force_stop_unicorn
    execute :kill, "$(< #{fetch(:unicorn_pid)})"
  end

#unicornをスタートさせるtask
  desc "Start unicorn server"
  task start: :environment do
    on roles(:app) do
      start_unicorn
    end
  end

#unicornを停止させるtask
  desc "Stop unicorn server gracefully"
  task stop: :environment do
    on roles(:app) do
      stop_unicorn
    end
  end

#既にunicornが起動している場合再起動を、まだの場合起動を行うtask
  desc "Restart unicorn server gracefully"
  task restart: :environment do
    on roles(:app) do
      if test("[ -f #{fetch(:unicorn_pid)} ]")
        reload_unicorn
      else
        start_unicorn
      end
    end
  end

#unicornを強制終了させるtask 
  desc "Stop unicorn server immediately"
  task force_stop: :environment do
    on roles(:app) do
      force_stop_unicorn
    end
  end
end

unicornを再起動させたり、ストップしたり...
といったタスクやメソッドが記述されており、こちらが連動して実行されることで、結果unicornが上手いこと動いてくれる。といったものになります。
(各項目の内容は設定ファイル内のコメントを参照ください。)

また、設定ファイルをよく見て頂くと、「KILL」という単語がよく使われていることに気づくかと思います。

このKILLとは、その名の通り、「殺す」作業になります。
...あらやだお下品。
と思わずに続きをご覧ください。(ここ本当に詰まる箇所ですので)

unicornは起動する際に、PID(OSが現在実行されているプロセスを識別する為の、プロセスごとに一意な識別子。 「45364」といった形の番号形式)と呼ばれるものを生成します。

そしてこの生成されたPIDは、ファイル上部「set :unicorn_pid」で指定された箇所に自動で記述されるように設定されています。
ここに何かしらの番号が書かれているか書かれていないかで、OSはunicornが起動しているorしていないを判断しているのですね。

では、このPID番号がunicorn_pidファイルに残っている状態で、unicornを起動させようとするとどうなるか。
OSが「起動している状態」だと判断してしまう為、「start_unicorn!!」と勢いよく唱えてもエラーになってしまうのです。
(確か「PID is already running」 と言われちゃいます。)

そこでunicornを起動させる際は、

1.既に起動しているPIDがあるかどうか確認
(2.起動しているPIDがあればそれをKILL(削除)する)
3.unicorn起動

という流れを踏んでいるのです。
この流れを踏むことで、既に起動していても、まだ起動していなくても、どちらでも対応できるように配慮されているのですね。

そしてそのkillにも実は幾つか種類があり...

kill -QUIT : 停止
kill -HUP  : 再起動
kill -USR2 : 緩やかな停止

と分けれれているのです。

「...緩やかて!w ワロタ\(^o^)/」
と思いましたが、どうやら、

1.既に起動しているPIDをpid_oldbinという名前に変更し
2.新しいPIDを立ち上げて
3.新しいPIDと古いpidを一瞬同時に動かした後
4.古いPID(pid_oldbin)にkill -QUITシグナルを送って停止させる

という緩やかプロセスを経ているようです。
では、なぜこんな面倒くさいプロセスを踏むのか。
そう。お気付きの方もいらっしゃるかもしれませんが...
アプリが停止するタイミングが無くなるのです。
一瞬もアプリの稼働を止めることなく、再起動ができるといった仕組みになるのですね。
(これをホットデプロイと呼びます)

自動且つホットなデプロイを実現させている、capistrano x unicorn
という構図が見えてきたでしょうか?

加えて、このタスク設定ファイルの中で使われている構文に関しても少し触れておきます。

task hogehoge do  ~~  end

の形で、様々なtaskを登録しており。

・run_locally do; ... end : ローカルマシン上での設定
・on 対象サーバ do; ... end : 対象サーバー上での設定

という2種類の環境下においてtaskを実行するorしないの設定を振り分けております。

加えて主要DSLを例示しておきますと、

・execute 'hogehoge' : 'hogehoge'というコマンドを実行する
・piyopiyo = capture 'hogehoge' : 'hogehoge'というコマンドの実行結果を取得し、piyopiyoという変数に格納する
・info output : outputの中身をログとして出力する

などが主になってくるかと思います。
(使用可能なメソッド一覧に関してはこちらをご確認下さい)

筆者はこのunicorn設定で一番躓きました...
どうやら、記事冒頭でお伝えしたgem 'capistrano3/unicorn'というgemを使用すると、unicornのPIDファイルがgem内で決められてしまっているようで、設定ファイルにディレクトリの記述した結果重複してエラーとなってしまっていたようです...

なので、gemは使わず自身で明示してしまった方が無難でしょう。

config/unicorn/production.rbの設定

こちらはunicornの設定ファイルです。
どうやらcapistrano内で、「unicornの設定ファイルディレクトリはここを読みに行く」という設定がなされているようですね。
なので、面倒ですがディレクトリとファイル名を合わせて生成してあげましょう。

config/unicorn/production.rbを新規作成し、下記のように設定してあげてください。

(local)config/unicorn/production.rb
#ワーカーの数。後述
  $worker  = 2
#何秒経過すればワーカーを削除するのかを決める
  $timeout = 30
#自分のアプリケーション名、currentがつくことに注意。
  $app_dir = "/var/www/mumu/current"
#リクエストを受け取るポート番号を指定。後述
  $listen  = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
#PIDの管理ファイルディレクトリ
  $pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
#エラーログを吐き出すファイルのディレクトリ
  $std_log = File.expand_path 'log/unicorn.log', $app_dir

# 上記で設定したものが適応されるよう定義
  worker_processes  $worker
  working_directory $app_dir
  stderr_path $std_log
  stdout_path $std_log
  timeout $timeout
  listen  $listen
  pid $pid

#ホットデプロイをするかしないかを設定
  preload_app true

#fork前に行うことを定義。後述
  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
        Process.kill "QUIT", File.read(old_pid).to_i
      rescue Errno::ENOENT, Errno::ESRCH
      end
    end
  end

#fork後に行うことを定義。後述
  after_fork do |server, worker|
    defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
  end

ここも掻い摘んで見ていきましょう。

〜〜master、worker、listen〜〜

unicornには、masterworkerという概念があります。
masterは、unicornに送られる全ての指令を一旦受け取る役目を担います。
そして指令を受け取ったmasterは、workerに指令を出します。
「worker1はこの仕事をして!」
「worker2はこの仕事をして!」
といった具合ですね。

そしてこのworkerの数を決めているのが、
worker = 2(worker2個作成)
の箇所であり、稼働するworkerが多ければその分消費するメモリも大きくなってしまうので、どのタイミングで削除するかを定義しているのが、
timeout = 30(30秒経過したら)
の箇所となります。

また、その指令を受け取るための窓口を、
listen = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
で指定しています。
逆に言うと、unicornに指令を送る側のプログラムはここに指定された箇所に指令を送らなければいけない... ということになりますね。

〜〜fork〜〜

forkとは、masterがworkerを生み出すプロセスのことを指します。
つまり、before_fork、after_forkとは、workerが生み出される前後で実行するタスクを定義していたということになります。

before_forkで古いmasterとworkerを削除して、(削除できなかったらエラーログを吐いて)、after_forkで... 
何してるんでしょこれ? 笑 なんかデータベース周りのことをしてるくさいですが... 分かったら更新します! 申し訳ない!

uniconrの設定が完了したら、サーバーにログインして、nginxの設定を行っていきます。

Nginxの設定

(※こちらはサーバー側での処理になりますのでご注意ください。)

まずはsshでサーバーにログインし、nginxをインストール

[naoki|~] sudo yum install nginx

お次は設定ファイルの編集です。

[naoki|~]$ cd /etc/nginx/conf.d/
[naoki|conf.d]$ sudo vi mumu.conf 

下記のように記述してください。

(server)/etc/nginx/conf.d/mumu.conf
#各種ログのディレクトリ設定
  error_log  /var/www/rails/mumu/current/log/nginx.error.log;
  access_log /var/www/rails/mumu/current/log/nginx.access.log;
#処理を受け取る最大許容量
  client_max_body_size 2G;
  upstream app_server {
# 連携するunicornのソケットのパス
    server unix:/var/www/rails/mumu/current/tmp/sockets/.unicorn.sock fail_timeout=0;
  }
  server {
    listen 80;
    server_name 127.0.0.1; # 自分のIPアドレスに変えてください!
#次のリクエストが来るまでの待ち時間(秒
    keepalive_timeout 5;
#静的ファイルを読みに行くディレクトリ
    root /var/www/rails/mumu/current/public;
#キャッシュのディレクトリ
    try_files $uri/index.html $uri.html $uri @app;
    location @app {
      # HTTP headers
      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;
    location = /500.html {
      root /var/www/rails/mumu/current/public;
    }
  }

server_nameのIPアドレスは、自分のものに変更してください。

nginxのファイルの読み方に関してはこちらが非常に参考になります。
詳細は他記事に任せるとして、今回は流れを簡単に追いましょう。

〜〜ブロック、ディレクティブ〜〜
nginxには、ブロックディレクティブと言う概念が存在します。

ブロックとは処理の一塊のことを指し、通常{}で囲われた範囲が1ブロックとなります。また、ディレクティブとは処理の一つ一つのことを指し、{}の中に hoge fuga; となっている箇所の、hogeがディレクティブ名、fuga;がその内容 ...といった具合に分かれていますので、覚えておくと設定ファイルが読みやすくなるかと思います。

設定ファイルで大事なポイントは2点です。

〜〜unicornとの連携〜〜

serverディレクティブ
  upstream app_server {
# 連携するunicornのソケットのパス
    server unix:/var/www/mumu/current/tmp/sockets/.unicorn.sock fail_timeout=0;
  }

ここに設定されているディレクトリをもとに、nginxはunicornとの接続を行います。ですので、しっかりとunicornのソケットファイルが設置されたディレクトリを記述してあげましょう。
(nginx→unicornへの連絡窓口を.unicorn.sockといった具合に指定したので、unicorn側でもnginxからの連絡窓口(=listen)にこのファイルを指定してあげないといけません)

〜〜静的ファイルと動的ファイル〜〜

#静的ファイルを読みに行くディレクトリ
    root /var/www/mumu/current/public;

静的ファイルを読みに行く... と言われても意味不明だと思いますので、まず簡単にアプリケーション表示の仕組みの解説から行いたいと思います。

これまでにEC2を設置し、その中に自作のアプリケーションを設置しました。
そしてunicornやnginxを設置して、いざ起動、アプリ表示となった際、アプリケーションは実は2つに分けられます。

・静的ファイル(画像やエラーページなど)
・動的ファイル(index.html.erbなど)

そしてこれらは全く別の場所に配置され、それぞれが別の場所でクライアントからのリクエストが来るまで待機しているのです。

では、クライアントから表示のリクエストが来たらどういう流れを踏むのか。
下記のようになります。

1.まずはwebサーバー(nginx)が処理を一括して受け取る
2.静的ファイルに対するアクセスはnginxが直接処理をして、動的ファイルに対するアクセスはアプリケーションサーバー(unicorn)にその処理を委ねる
3.指令を受けたunicornは、動的ファイルの処理を行い、処理結果をnginxに返す
4.自身で処理した静的ファイルとunicornから受け取った動的ファイルを合わせてクライアントにレスポンスとして返す

ということは、上記の設定は、
unicornが直接参照しに行く静的ファイルの配置場所を指定している
ということになりますね。
(assets配下に画像を置いてしまうと本番環境でエラーとなってしまうことがあるのはこのためです。)

その他は特に気にせずこのままの設定で大丈夫だと思います。
これでNginxの設定は完了です。

データベースの作成

初回のデプロイ時のみ、サーバーにデータベースを作成してあげる必要があります。
今回はMySQLをEC2内にインストールしている為、そこに新しくデータベースを作成してあげましょう。

(server)
[naoki | ~] $ sudo service mysqld start
(#mysqldを起動させる)
[naoki | ~] $ mysql -u root -p
(#mysqlにログイン)
[mysql]> CREATE DATABASE mumu_production;
(#データベースの名前はご自身の環境に合わせて作成)
[mysql]> SHOW DATABASES;
(#今作成したデータベースが存在しているか確認)
[mysql]> QUIT;
(#問題なければmysqlからログアウト)

これで見事データベースが作成されました。

そしてローカル側で、production環境用のDB設定を記述していきます。
(ローカルとサーバーでMySQLが配置される位置が異なるなど、微調整が必要です。)

(local)config/database.yml
#producton環境の設定を下記のように変更

production:
  adapter: mysql2
  encoding: utf8
  pool: 5
  database: pictweet_production
  username: root
  password:
  socket: /var/lib/mysql/mysql.sock

これでDB周りの設定は完了ですので、
いよいよ、gitに変更履歴をプッシュしていきます。

gitにプッシュ

※ここからまたローカル処理に戻ります。

最後の仕上げとして、ローカルでgitに今までの変更履歴をプッシュします。

[mumu] git add .
[mumu] git commit -m "Add for deploy by capistrano"
[mumu] git push origin master

これで準備は完了です。

デプロイ

...さぁ、準備はいいですか?
いよいよデプロイです。
ターミナルはローカルを操作しようとしていますか?
確認が終わりましたら、下記コマンドを実行してみてください。

[mumu]bundle exec cap production deploy

これを実行することで、先ほど説明させて頂いたタスクが次々と実行され、
bundle install → unicornの設定 → 起動 → git pull ........

といった具合にデプロイに際して必要な手順が一通り実行されるはずです。

ちなみにデプロイしたものをローカルで編集して再度デプロイし直したい場合はどうすれば良いのか...??
はい。みなさんご一緒に。

[mumu]bundle exec cap production deploy

完了です。
...なんて楽なんだぁぁぁぁぁぁ!!!!

まとめ

capistranoは本当に学習コストが高い(「とりあえずcapistranoやるかー」くらいのテンションで取り組むと大怪我しますw)
でも、一旦設定が完了してしまえば以後は非常に楽になる&作業ミスのリスクが激変するので、今後も継続して運用していく予定のプロダクションには時間を掛けて導入する価値ありですね。(一旦コツを抑えてしまえば次回以降のプロダクションでも非常に楽になりますし) 

では、長くなってしまいましたが、
以上、世界一丁寧な...(長いので省略) Capistrano編でした!

naoki_mochizuki
Keeyls株式会社のエンジニアしてます。 無人の鍵受け渡しシステムKEYSTATION(https://key-stations.com/ )や、 シェアオフィスの管理システム( https://sharedoffice-keystations.com/ )など作ってます。
https://keeyls.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away