Nginx + UnicornなEC2にRailsアプリケーションをCapistranoでデプロイしてみる

Nginx + UnicornなEC2にRailsアプリケーションをCapistranoでデプロイしてみる

はじめに

こんにちは。へっぽこエンジニアのkurashitaです。

今回はAWS上のEC2インスタンスにNginxとUnicornをインストールしてRailsアプリケーションをCapistrano でデプロイした時の手順メモです。
なお各用語についてはおググりください。

サーバはAWSのEC2を用意してある前提です。
以下の一連の記事が非常に参考になりました。
http://qiita.com/hiroshik1985/items/6433d5de97ac55fedfde

EC2上でのインストール作業

今回はstaging環境として構築します。
EC2に ec2-user で ssh ログインします。

git

gitをインストールします。

sudo yum install git

各パッケージ

各パッケージをインストールします。

sudo yum install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel libffi-devel libxml2-devel libxslt-devel file file-devel mysql-devel ImageMagick ImageMagick-devel
# ruby 2.2.0 以降を入れる場合は libffi-develが必要
# nokogiri のインストールに libxml2-devel libxslt-devel が必要
# ruby-filemagic のインストールに file file-devel が必要
# mysql2 のインストールに mysql-devel が必要
# RMagick のインストールに ImageMagick ImageMagick-devel が必要

ruby-build

cd
git clone git://github.com/sstephenson/ruby-build.git
cd ruby-build
sudo ./install.sh

rbenv, ruby

cd
git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bashrc
exec $SHELL -l
rbenv install -l # 一覧の中から最新か安定版を選ぶ
rbenv install 2.2.1;rbenv rehash
rbenv global 2.2.1
ruby -v
# ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]

bundler

gem install bundler --no-rdoc --no-ri

Nginx

sudo yum install nginx

Nginx設定

/etc/nginx/conf.d/myapp.conf を作成して以下を記述します。

upstream unicorn_server {
  server unix:/tmp/unicorn.sock
  fail_timeout=0;
}

server {
  listen 80;
  client_max_body_size 4G;
  #server_name _; ドメイン未登録

  keepalive_timeout 5;

  root /var/www/myApp/current/public;
  access_log /var/log/nginx/myApp_access.log;
  error_log /var/log/nginx/myApp_error.log;

  error_page 500 502 503 504 /500.html;

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

  location @unicorn {
    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://unicorn_server;
  }

  location ~ ^/assets/ {
    root /var/www/myApp/current/public;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/myApp/current/public;
  }
}

linked file

gitでは扱わないsecrets.ymldatabase.ymlをあらかじめサーバに置いておきます。
var/www/myApp/shared/config/ に secrets.yml と database.yml を作成します。

# secrets.yml
staging:
secret_key_base: <長い文字列>
secret_token: <長い文字列>

文字列は何でも良いのでローカル環境で rake secret などでも作成できます。

# database.yml
staging:
  adapter: mysql2
  encoding: utf8
  pool: 5
  socket: /var/lib/mysql/mysql.sock
  database: myapp_staging
  host: myapp_hogehoge_host
  username: root
  password: password
  port: 3306

ディレクトリ所有権の変更

DocumentRootに使う www 以下の所有権を変更しておきます。

chown -R ec2-user www

ローカルでの作業

ここからローカルでの作業になります。

gem インストール

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

group :development do
  gem 'capistrano'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano3-unicorn'
end
bundle install

Unicorn

設定

config/unicorn/staging.rb に記述します。

# staging.rb
root = "/var/www/myApp/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

listen "/tmp/unicorn.sock"
worker_processes 3
timeout 30

preload_app true

before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
end

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "#{root}/tmp/pids/unicorn.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
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  # the following is *required* for Rails + "preload_app true",
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end
end

Unicornのタスク登録

lib/capistrano/tasks/unicorn.cap を作成します。

# unicorn.cap
namespace :unicorn do
  task :environment do
    set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
    set :unicorn_config, "#{current_path}/config/unicorn/#{fetch(:rails_env)}.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 "Stop unicorn server immediately"
  task :force_stop => :environment do
    on roles(:app) do
      force_stop_unicorn
    end
  end
end

capistrano

まずは Cpafile を作ります。

bundle exec cap install
# Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'

set :rbenv_type, :user
set :rbenv_ruby, '2.2.1'

require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/bundler'
require 'capistrano3/unicorn'

set :linked_files, %w{config/secrets.yml config/database.yml}

Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

デプロイ設定

config/deploy.rb に記述します。

# deploy.rb
lock '3.4.0'

set :application, 'myApp'
set :repo_url, 'git@github.com:lanchester/myApp.git'
set :deploy_to, '/var/www/myApp'
set :log_level, :debug

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/assets}

# nokogiri はシステムライブラリを使うため bundle install にオプションを指定する
set :bundle_env_variables, { nokogiri_use_system_libraries: 1 }

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

各環境ごとのデプロイ設定

今回はstaging環境のみ。config/deploy/staging.rb を作成します。
デプロイユーザは本来であれば別に用意するべきなのですが、今回はとりあえずそのまま行きます。

# staging.rb
set :stage, :staging
set :unicorn_rack_env, "staging"
set :branch, 'dev'
set :rails_env, 'staging'
set :migration_role, 'db'

role :app, %w{ec2-user@*.*.*.*} # 実際のサーバのIPアドレスを指定します。
role :web, %w{ec2-user@*.*.*.*}
role :db, %w{ec2-user@*.*.*.*}

set :ssh_options, {
keys: [File.expand_path('~/.ssh/myApp-stg.pem')],
forward_agent: true,
auth_methods: %w(publickey)
}

server '*.*.*.*', user: 'ec2-user', roles: %w{web app db}

デプロイ先サーバの設定

GitHubのリポジトリにアクセスするための公開鍵・秘密鍵を生成

cd ~
ssh-keygen -t rsa -C myApp-stg
ssh-add ~/.ssh/id_rsa # 上記の鍵をssh-agent に登録します
# すでに別の鍵を追加していた場合、デプロイ時にpermission deniedになることがあるので
# ssh-add -D 等で登録削除したのち改めて登録

GitHubリポジトリの設定

1) 対象のリポジトリのページへ移動
2) Settingsへ移動
3) Deploy keysへ移動
4) Add Deploy Keysで上記で作成した公開鍵をコピペ

接続確認

初回デプロイ時のみgithubへの接続確認が必要です。

ssh -T git@github.com

デプロイ

unicorn の起動

config/unicorn/staging.rb の設定で unicorn を起動します。

bundle exec unicorn -D -c /var/www/myApp/current/config/unicorn/staging.rb -E staging

以上設定で不備がなければデプロイ可能です。

cap staging deploy

お疲れ様でした。

TAG

  • このエントリーをはてなブックマークに追加
kurashita
エンジニア kurashita kurashita

基本的にRuby on Railsで開発してます。最近はvue.jsも。好きな塔は円城です。