追記
http://qiita.com/kizashi1122/items/6e22b2a27fa4da487884
に更新版を載せました。
はじめに
最初に構成ですが、以下のとおりです。
- クライアント
- Mac 上の仮想環境(CentOS)
- Capistrano3 もここで動きます
- サーバ
- EC2(AWS)
- 構成管理
- Github
ミドルウェアは、以下のとおりです。Nginx は yum
でインストールしておいてください。
- Nginx
- Unicorn
Capistrano3 で、クライアントからコマンド一発でサーバにデプロイすることを目的としてます。
しかし、Capistrano3 や Unicorn 関連の情報は Web 上にたくさんあり、それぞれ少しずつ設定が違ってたりして部分的につまみ食いすると設定に食い違いがでてしまって動かなくなります。
私はまだまだ初心者ですがとりあえず動くところまで行ったので共有したいと思います。
各種設定ファイル
Nginx
Nginx はインストールしている前提です。
Unicorn とは同一サーバ上で動かしているため、Unix Domain Socket
で接続します。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server_tokens off; # 1
include /etc/nginx/conf.d/*.conf;
index index.html index.htm;
upstream sub.example.com { # 2
server unix:/tmp/unicorn.sock; # 3
}
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
location ~ ^/assets/ {
root /home/ec2-user/deploy/current/public; # 4
}
location / {
proxy_pass http://sub.example.com; # 5
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
コメントにつけた番号について注意点をあげておきます。
番号 | 説明 |
---|---|
1 | セキュリティのためです。今回の設定とは無関係です。 |
2 | ちゃんとFQDNにしておかないとアプリでフルパスを取得するとき失敗してしまいました。 |
3 | socket ファイルの指定。unicorn の設定と合わせる必要あり。 |
4 | コンパイル後の css, js が生成されるパスを指定する。 |
5 | http:// より後ろは 2 と合わせておいてください。 |
Capistrano 3
Capistrano 3 のインストールをします。
gem 'capistrano', '~> 3.1.0'
gem 'capistrano-rails', '~> 1.1.0'
gem 'capistrano-bundler'
gem 'capistrano-rbenv', "~> 2.0"
gem 'unicorn'
capistrano-unicorn なる便利そうな gem もあるみたいですが、まだ使っていません。入れなくてもデプロイはできます。
次にインストールします。
$ bundle install
$ bundle exec cap install
するとフォルダやファイルが生成されます。
├── Capfile
├── config
│ ├── deploy
│ │ ├── production.rb
│ │ └── staging.rb
│ └── deploy.rb
└── lib
└── capistrano
└── tasks
次に、Capfile を編集します。私のファイルの有効行だけを紹介します。
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
set :rbenv_type, :user # ADDED
set :rbenv_ruby, '2.0.0-p451' # ADDED
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
ほとんどそのまま使っていますが、#ADDED のところは追加しています。
次に、deploy.rb です。
lock '3.1.0'
set :application, 'rails_app' ## 1
set :repo_url, 'git@github.com:kizashi1122/rails_app.git' ## 2
set :deploy_to, '/home/ec2-user/deploy' ## 3
set :scm, :git
set :log_level, :debug
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets public/system public/assets}
set :default_stage, "production"
namespace :deploy do
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
# Your restart mechanism here, for example:
# execute :touch, release_path.join('tmp/restart.txt')
invoke "unicorn:restart" ## 4
end
end
after :publishing, :restart
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
end
番号 | 説明 |
---|---|
1 | アプリ名です |
2 | デプロイ対象のアプリです。git のURLパスです。 |
3 | サーバ側のデプロイ先のディレクトリです。/var/www などのディレクトリを使っているサンプルが多かったのですが、パーミッションの問題もあるだろうと思い、ssh ユーザがアクセスできるように、ホームディレクトリ以下にしました。このディレクトリは先に mkdir しています。 |
4 | deploy コマンドをたたいたときには unicorn を起動してもらわないといけないので追加しました。 |
デプロイするソースは master から取得する前提なので明示的に指定はしていません。
ssh と書きましたが、まだ ssh の接続に関する設定をしていません。これは production.rb でします。以下、有効行のみです。
set :stage, :production
set :rails_env, 'production'
server 'sub.example.com', user: 'ec2-user', roles: %w{web app db}
set :ssh_options, {
keys: [File.expand_path('~/.ssh/aws.pem')],
}
stage, rails_env は明示的に指定するようにしました。なぜか unicorn 起動時の引数に渡されないことがあったからです。
もともとのファイルの上部には role の設定などがあり困惑しましたがそれらはコメントアウトしています。
Unicorn
つぎに Unicorn の設定です。
すでに、Gemfile に gem 'unicorn'
と書いて bundle install
しているのでインストールは終わっています。
あとは設定です。これがややこしいです。
unicorn.cap は capistrano で unicorn を停止起動するためのラッパー関数が書かれた設定ファイルです。
Capfile の最下行に lib/capistrano/tasks/ 以下の *.cap ファイルを自動で import する記述があるのでこのファイルは自動的にインポートされます。
一応ファイルの中身も記載しますが、http://qiita.com/satococoa/items/9b0cc416ffc042680b9b のままです。
namespace :unicorn do
task :environment do
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_config, "#{current_path}/config/unicorn.rb"
end
def start_unicorn
within current_path do
execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
end
end
def stop_unicorn
execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"
end
def reload_unicorn
execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})"
end
def force_stop_unicorn
execute :kill, "$(< #{fetch(:unicorn_pid)})"
end
desc "Start unicorn server"
task :start => :environment do
on roles(:app) do
start_unicorn
end
end
desc "Stop unicorn server gracefully"
task :stop => :environment do
on roles(:app) do
stop_unicorn
end
end
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
desc "Stop unicorn server immediately"
task :force_stop => :environment do
on roles(:app) do
force_stop_unicorn
end
end
end
Unicorn を起動・停止するラッパー処理は、capistrano-unicorn
を gem でインストールしたら書く必要ないような気もしてきました。
それはともかく、ここで大事なのは以下の2行です。
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_config, "#{current_path}/config/unicorn.rb"
1行目の unicorn.pid
は unicorn のプロセスIDを保持するテキストファイルです。このファイルを使ってプロセスIDを取得し停止などの操作をしているわけです。しかしこの pid
の指定は2行目の起動設定ファイルにも記述する必要があります。 つまり二重管理になります。 さらに、unicorn.rb では同様に #{shared_path}
という書き方ができないので、明示的にフルパスを書かないといけないことに注意してください。
worker_processes 2
listen '/tmp/unicorn.sock' # 1
pid "/home/ec2-user/deploy/shared/tmp/pids/unicorn.pid" #2
stderr_path File.expand_path('unicorn.log', File.dirname(__FILE__) + '/../log')
stdout_path File.expand_path('unicorn.log', File.dirname(__FILE__) + '/../log')
preload_app true
番号 | 説明 |
---|---|
1 | nginx.conf に記述する unicorn の socket ファイルのパスと合わせてください。 |
2 | unicorn.cap で指定した pid のパスを変数を使わずに書きます。 |
あとは、git push
して、以下のデプロイコマンドを打ってください。
$ bundle exec cap production deploy
特にどこにも指定したつもりはないですが、assets のプリコンパイルも走ってます。
課題
cap deploy
をするとズラズラーとコマンドの結果がコンソールに表示されますが、結構 failed
が出ています。ファイルやフォルダの存在のテストだけならいいんですが、結構気持ち悪いです。
また内部的に git で接続してる最中に
Error reading response length from authentication socket.
が表示されているのが気持ち悪い。処理自体はうまくいっているぽいのだが・・。