読者です 読者をやめる 読者になる 読者になる

プログラミング 美徳の不幸

Ruby, Rails, JavaScriptなどのプログラミングまとめ、解説、備忘録。

Railsのステージング環境を構築②(Capistranoによるデプロイ)

ここが山場になります。私はここでかなりどん詰まりしました笑

Amazon EC2
◯Cent OS 6.2
Ruby 1.9.2
Rails 3.2.1
◯rvm 1.14.2
unicorn 4.3.1
Capistrano 2.12.0
◯nginx 1.2.1
mysql 5.5.25

unicornほかのインストール(Gemfileに記述)

gem 'unicorn'
gem 'capistrano'
gem 'capistrano-ext' #=>capistranoを複数環境で状況に分けて使う(https://github.com/jamis/capistrano-ext)
gem 'capistrano-unicorn' #=> capistranoからunicornの再起動を行う(https://github.com/sosedoff/capistrano-unicorn)
gem 'rvm-capistrano' #=>rvmとcapistranoを併用する際のライブラリ(https://github.com/wayneeseguin/rvm-capistrano)
bundle install
unicorn -v
#=>unicorn v4.3.1
capify .
#=> Capfileが作成される


unicornの設定ファイル(config/unicorn/staging.rb)

app_path = '/var/www/プロジェクト名/current' 
#Capistranoを使う場合、基本的にアプリケーションのパスを設定するときは
#最新のリリースへのシンボリックリンクであるcurrentを使う
worker_processes 2
working_directory app_path
preload_app true
timeout 180

stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
 
pid "#{app_path}/tmp/pids/unicorn.pid"
#unicornを立ち上げた時にpidをどのファイルに記録するか。

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
 
  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|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

説明。
上から数行は単純な設定で、stderr_path,stdout_pathというのはこれも字のごとく標準エラー出力と標準出力をlogに書き出す、
logファイルの指定です。
pidというのはプロセスid。これをファイルで管理するんですが、unicornをstopさせるとき、このプロセスidを見てkillします。もしunicornをストップさせるところで
詰まるようだったら、このpid設定が間違っていることが多いです。

capistrano-extを使用すると、capistranoに引数として環境を渡すことができます。
例) cap staging deploy:update
unicornの場合、通常設定ファイルは
config/unicorn.rb
の位置に置きますが、これだとすべての環境で共通設定になってしまうので、
config/unicorn/環境名.rb
のファイルを作成してやります。共通部分についてはconfig/unicorn.rbに書いてもおkです。

capistranoの設定ファイル(config/deploy.rb)

require 'capistrano/ext/multistage' #=>capistrano-extを使用する際に必要
set :rvm_ruby_string, '1.9.2-p290'
require "rvm/capistrano"
set :application, "アプリケーション名"
set :rvm_type, :system
set :rvm_ruby, '1.9.2-p290'
set :rvm_gem_home, '/usr/local/rvm/gems/ruby-1.9.2-p290'
set :rvm_ruby_path, "/usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby"
set :repository,  "git@github.com:hoge/hoge.git"
set :user,'admin'
set :password, 'パスワード'
set :scm_username, "githubのアカウントID(privateリポジトリの場合のみ)"
set :scm_password, "githubのアカウントパスワード(privateリポジトリの場合のみ)"
set :sudo_password, 'パスワード'
set :deploy_to, '/var/www/アプリケーション名'
set :scm, :git
set :deploy_via, :checkout
default_run_options[:pty] = true #sorry, you must have a tty to run sudoと言われた時の対策

ssh_options[:keys] = %w(~/.ssh/秘密鍵.pem)

task :show_uname  do 
  run "uname -a"
end
#webサーバ,dbサーバ等の情報はすべて環境ごとのファイルに書く
require 'capistrano-unicorn' #=>capistrano-unicornを使用する際に必要

capistranoの設定ファイル環境ごと(config/deploy/staging.rb)

set :deploy_env, 'staging'
set :unicorn_env, 'staging'
set :rails_env, 'staging'                  # Rails上の環境名
set :app_env, 'staging'
set :deploy_to, '/var/www/アプリ名'  # デプロイ先の絶対パス

[:web, :app].each do |type|
  role type,
  '54.248.×××.×××'
end

role :db, '54.248.×××.×××', :primary => true  # rake:db:migrateを実行するサーバー

task :db_create do
  run "cd /var/www/アプリ名/current rake db:create RAILS_ENV=staging"
end

database.ymlにstagingを追加(config/database.yml)

staging:
  adapter: mysql2
  encoding: utf8
  host: 127.0.0.1
  username: admin
  password: パスワード
  database: アプリ名_staging
  pool: 5
  timeout: 5000
  socket: /var/lib/mysql/mysql.sock 

もちろんこのファイルは各々の環境に合わせて書いてください。

Capfileに変更加える

#先頭に次の行を加える
require 'bundler/capistrano'

これはcapistranoからbundle installするため必要

nginxの設定(/etc/nginx/nginx.conf)

user admin;
worker_processes 2;

events {
  worker_connections 1024;
}

http {
  upstream backend {
    server 127.0.0.1:8080; #APサーバのポート番号を指定
  }
  server {
    listen 80;
    server_name hogehoge; #待ち受けるドメインを指定
    root /var/www/アプリ名/current/public; #=>capistranoを使う場合はcurrentをつけること。間違いやすい
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/access.log;
    error_log /var/log/error.log;

    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;

    location ~* ^/assets {
      expires max;
      proxy_path http://backend;
    }

    location / {
      if (-f $request_filename){
        break;
      }
    
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_pass http://backend;
    }
    location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
      expires 1y;
    }
  }
}
  

あとはローカルの端末から

cap staging deploy:setup
cap staging deploy:cold

2行目うまくいかなかったら
cap staging db_create
cap staging bundle:install
cap staging db:migrate
cap staging db:seed
cap staging deploy:update
cap staging deploy:restart
など、コマンドを分けてみてどの挙動でおかしくなっているかチェックしてください。
(このへんは私も試行錯誤しながらやったので、手順的に記憶していないが抜けているところがあるかもしれません)



もう少し理解しながら確実にやりたい方は前回の記事からnginxをインストールして、まずunicorn、nginxでrailsアプリが動くところまでを目指すとよいでしょう。(production環境でもいいので)

実際、productionとstagingを分けているのは単純にそうしないとstaging環境でproductionのDBを読みに行ってしまうためで、例えばstagingのサーバのconfig/database.ymlをgitの対象外にしてしまって
productionの部分を書き換えるといった手もありそうですが、その他にもcapistrano側から対象のサーバを分けたりすることを考えてstagingを作成しました。
Rails.envとかでstagingは返ってくるんですが、rails console -e staging などはできないっぽい。
ただ、rake db:migrate RAILS_ENV=stagingは可能。どういうことだろう?