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

AWS RailsアプリケーションのCapistranoによるデプロイ

More than 3 years have passed since last update.

はじめに

デプロイ作業をコマンドひとつで簡単に出来るようにするために、Ruby製のデプロイツールであるcapistranoを使ってみます。

今回は、AWSにアプリケーションがとりあえずサーバー構築出来た状態からCapistranoをセットアップしていきたいと思います。

以下の、2つの記事でVPCとEC2の構築と、unicornとnginxの導入が出来ている状態からセットアップしていきます。
AWS VPCによるネットワーク構築とEC2によるサーバー構築
RailsアプリケーションのAWSによる公開|unicorn + nginx

Rails5系の Nginx+Pumaの記事も書いているので5系の方はこちらをどうぞ↓

Rails5アプリケーションのAWSによるネットワーク構築 Nginx+Puma+Capistranoな環境とAWS構築(VPC EC2 RDS CloudFlont Route53 etc)

なお、上記記事と同様に「protospace」というアプリケーションをサーバーに「shizuma」というユーザーで作成していきます。
作業は、「local」と「server」の作業どちらか確かめながら進めて下さい。コマンドを記載したエリアの左端に記述しています。

また、改善点がありましたらコメント頂けますと幸いです。

Capistranoの導入

local
[protospace] vi Gemfile
-----------------------------
# gemファイルの中にunicorn、capistrano関連のgemを追記して下さい。
group :production, :staging do
  gem 'unicorn'
  gem 'capistrano'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano3-unicorn'
end
-----------------------------
[protospace] bundle install
[protospace] bundle exec cap install
-----------------------------
# 以下のようにファイルが生成されます
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
-----------------------------

各種ファイルの編集

unicornの設定ファイル

local
[protospace] vi config/unicorn.conf.rb
# 以下のように記述
-----------------------------
  # set lets
  $worker  = 2
  $timeout = 30
  $app_dir = "/var/www/rails/protospace/current" #自分のアプリケーション名、currentがつくことに注意。
  $listen  = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
  $pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
  $std_log = File.expand_path 'log/unicorn.log', $app_dir
  # set config
  worker_processes  $worker
  working_directory $app_dir
  stderr_path $std_log
  stdout_path $std_log
  timeout $timeout
  listen  $listen
  pid $pid
  # loading booster
  preload_app true
  # before starting processes
  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
  # after finishing processes
  after_fork do |server, worker|
    defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
  end
-----------------------------

Capfileの編集

local
[protospace] vi Capfile
-----------------------------
# Load DSL and set up stages
require 'capistrano/setup'

# Include default deployment tasks
require 'capistrano/deploy'

# Include 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
#   https://github.com/capistrano/passenger
#
# require 'capistrano/rvm'
require 'capistrano/rbenv' #コメントアウトをはずす
# require 'capistrano/chruby'
require 'capistrano/bundler' #コメントアウトをはずす
require 'capistrano/rails/assets' #コメントアウトをはずす
require 'capistrano/rails/migrations' #コメントアウトをはずす
# require 'capistrano/passenger'
require 'capistrano3/unicorn' #追記

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.task').each { |r| import r }
-----------------------------

capistrano/tasks/unicorn.taskの設定

local
[protospace] vi lib/capistrano/tasks/unicorn.task
# unicornのtaskの記述。unicornのセットアップです。
-----------------------------
namespace :unicorn do
  task :environment do
    set :unicorn_pid,    "#{current_path}/tmp/pids/unicorn.pid"
    set :unicorn_config, "#{current_path}/config/unicorn.conf.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
-----------------------------
local
[protospace] vi config/deploy/production.rb
# production環境のサーバー情報
-----------------------------
#下記を追記する
server '00.00.000.000', user: 'shizuma', roles: %w{app} #serverのipとuser名は適宜
set :ssh_options, keys: '~/.ssh/first_aws_rsa' #ssh_keyの名前は適宜
-----------------------------
local
[protospace] vi config/deploy.rb
-----------------------------
# config valid only for current version of Capistrano
lock '3.4.0'

# アプリケーション名
set :application, 'protospace'

# deployするレポジトリ
set :repo_url, 'git@github.com:kuboshizuma/protospace.git'

# Default branch is :master
# deployするブランチ。デフォルトはmasterなのでなくても可。
set :branch, 'master'
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
# deploy先のディレクトリ
set :deploy_to, '/var/www/rails/protospace'

# Default value for :scm is :git
# set :scm, :git

# Default value for :format is :pretty
# set :format, :pretty

# Default value for :log_level is :debug
# set :log_level, :debug

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
# シンボリックリンクをはるファイル。今回はgemのconfigを使用して、production.ymlを共通化。
set :linked_files, fetch(:linked_files, []).push('config/settings/production.yml')

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

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for keep_releases is 5
# 保持するバージョンの個数
set :keep_releases, 5

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

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
      # Here we can do anything such as:
      # within release_path do
      #   execute :rake, 'cache:clear'
      # end
    end
  end
end
-----------------------------

環境変数の設定

今回はconfigというgemを使っています。dotenvとかでもいいと思います。
この設定は適宜必要に応じて設定して下さい。(ただし、secret_key_baseの設定は必要になります。)

local
[protospace] vi config/database.yml
-----------------------------
production:
  <<: *default
  database: protospace_production
  username: <%= Settings.database[:user_name] %>
  password: <%= Settings.database[:password] %>
-----------------------------

[protospace] vi config/secret.yml
-----------------------------
production:
  secret_key_base: <%= Settings.production[:secret] %>
-----------------------------

[protospace] rake secret RAILS_ENV=production
erfffer34gfdv19b5ec7523a23tgrege45gertf3d15b94dfgvere23r34g87253a6f6b7b80c6cb47c598e000500b4654143gergfwer3r4cca04d
server
[shizuma|protospace] mkdir -p shared/config/settings/
[shizuma|protospace] vi shared/config/settings/production.yml
-----------------------------
database:
  user_name: 'root'
  password:
production:
  secret: 'erfffer34gfdv19b5ec7523a23tgrege45gertf3d15b94dfgvere23r34g87253a6f6b7b80c6cb47c598e000500b4654143gergfwer3r4cca04d'
-----------------------------

nginxの設定

releaseバージョンはcurrentディレクトリなので微調整。

server
[shizuma|~] sudo yum install nginx
[shizuma|~]$ cd /etc/nginx/conf.d/
[shizuma|conf.d]$ sudo vi protospace.conf 
----------------------------
  # log directory
  error_log  /var/www/rails/protospace/current/log/nginx.error.log; #currentつける
  access_log /var/www/rails/protospace/current/log/nginx.access.log; #currentつける
  # max body size
  client_max_body_size 2G;
  upstream app_server {
    # for UNIX domain socket setups
    server unix:/var/www/rails/protospace/current/tmp/sockets/.unicorn.sock fail_timeout=0; #currentつける
  }
  server {
    listen 80;
    server_name 127.0.0.1;
    # nginx so increasing this is generally safe...
    keepalive_timeout 5;
    # path for static files
    root /var/www/rails/protospace/current/public; #currentつける
    # page cache loading
    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;
    }
    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/protospace/current/public; #currentつける
    }
  }
----------------------------
[shizuma|protospace]$ sudo nginx -s reload # nginxの再起動

githubへ変更をpush

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

デプロイ

local
[protospace] bundle exec cap deploy production

これで、完了!

エラー対応

rails_config

gemでrails_configを使っていたが、エラーが出たので、configというgemに変更。

mysql2

バージョン指定で対応。

Rails mysql2でrake db:createがエラー問題

/tmp/mysql.sockエラー

シンボリックリンクで対応。

$ ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock

参考

AWSはこれで大丈夫|unicorn + nginx + capistrano + slackistrano のコマンド一覧

shizuma
web&DeepLearningエンジニア。 ACES.inc←東京大学大学院/東京←鹿児島/blog https://blog.seishin55.com ; Qiita https://qiita.com/shizuma ; note https://note.mu/seishin55
https://seishin55.com
aces
ACES(エーシーズ)は、 画像認識を中心としたAIアルゴリズムの力で、リアル産業のDX(デジタルトランスフォーメーション)を推進し、シンプルな社会を実現する会社です。ヒトの働き方をデジタルの力で 自動化・効率化することで、誰もが生き生きと 生きられる社会を実現していきます。
https://acesinc.co.jp/
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