LoginSignup
3
1

More than 3 years have passed since last update.

Capistrano + AWS(EC2) + Rails で簡単デプロイ

Posted at

はじめに

Capistranoを使って、自作しているRailsアプリをデプロイしてみました。

基本的には以下の記事を参考にしました。
(Capistrano編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで

本記事では、参考記事に沿ってエラーで詰まった部分をまとめたので参考になれば嬉しいです。

作成するファイル

ローカル側
- Gemfile
- Capfile
- config/deploy.rb
- config/deploy/production.rb
- lib/capistrano/tasks/unicorn.rake
- config/unicorn/production.rb

サーバー側(EC2)
- shared/config/settings.yml
- shared/config/database.yml
- shared/.env
- /etc/nginx/conf.d/app.conf

ソースコード

Gemfile

インストールするGem一覧です。

(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を使用している事がありますが、詳細な設定をするためここでは使用しません。

Capfile

capistrano全体の設定ファイルです。

(local)Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
# taskを記述したファイルを読み込むよう設定。場所と拡張子を指定。
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

参考記事では最終行が"~/*.rb"となっていますが、現在のバージョンではrakeがデフォルトになっているのでそちらを使用します。

後ほど作成する lib/capistrano/tasks/unicorn.rake のファイル形式は.rakeになっていることに注意してください。設定したファイル形式を同じファイル形式にしましょう。(筆者はここで少し詰まりました。)

config/deploy/production.rb

本番環境の設定を記述するファイルです。

(local)config/deploy/production.rb
# EC2サーバーのIP、EC2サーバーにログインするユーザー名、サーバーのロールを記述
server '**.***.***.***', user: 'yuki', roles: %w[app db web]

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

config/deploy.rb

ここには、production環境、stading環境どちらにも当てはまる設定を記述します。

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

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

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

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

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

# secret_base_keyを読み込ませるため追記
set :linked_files, %w[config/master.key]

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

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

# 保持するバージョンの個数。過去5つまで履歴を保存。
set :keep_releases, 5

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

# 出力するログのレベル。
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

参考記事に加えて追記したコードが以下の二つです。

ここでは、シンボリックリンク(gitignoreに設定するようなオープンにしたくないファイル)に
- master.key
- database.yml
- .env
を加えることでデプロイ時に必要なkeyや環境変数を読み込ませています。

# secret_base_keyを読み込ませるため追記
set :linked_files, %w[config/master.key]

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

lib/capistrano/tasks/unicorn.rake

unicornのセットアップタスクを記述するファイルです。

(local)lib/capistrano/tasks/unicorn.rake
# 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

Capfileのところでも説明しましたが、unicorn.rakeになっていることに注意してください。
Capfileで.rbで設定した場合はunicorn.rbとなります。

config/unicorn/production.rb

unicornの設定ファイルです。

(local)config/unicorn/production.rb

# ワーカーの数。後述
$worker = 2
# 何秒経過すればワーカーを削除するのかを決める
$timeout = 30
# 自分のアプリケーション名、currentがつくことに注意。
$app_dir = '/var/www//rails/golfour/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

shared/config/settings.yml

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

shared/config/database.yml

(server)shared/config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: golfour_development
  username: yuki
  password: password
  host: db
  socket: /tmp/mysql.sock

test:
  <<: *default
  database: golfour_test
  username: yuki
  password: password
  host: db-test
  socket: /tmp/mysql.sock

production:
  <<: *default
  database: <%= ENV['DB_NAME'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOSTNAME'] %>

shared/.env

ここでdatacbase.ymlで使っている環境変数を記述します。

(server)shared/.env
DB_NAME=*****
DB_USERNAME=*****
DB_PASSWORD=*****
DB_HOSTNAME=*****

/etc/nginx/conf.d/app.conf

(server)/etc/nginx/conf.d/app.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;
    }
  }

デプロイコマンド

bundle exec cap production deploy

エラー一覧

メモリ不足

デプロイ実行後に発生。
EC2のメモリが足りないために発生していた。

console
virtual memory exhausted

解決策:スワップファイルを作成する

使用していないメモリを一時的に別の場所に移動させることで、使用可能なにメモリを増幅させる方法です。
以下の記事を参考にすれば、簡単に実装できるのでぜひ。

参考:
スワップファイル (swap file)とは
[Rails] CapistranoでEC2へデプロイ:EC2仮想メモリ不足トラブルシュート

Missing secret_key_base for 'production' environment

こちらもデプロイを実行した際に発生したエラーです。

原因はmaster.keyファイルがなかったためでした。
そもそもsecret_key_base はcredentials.yml.encファイルに記載されています。
しかしこのファイルは暗号化されていて、読み込むにはmaster.keyが必要になるのですが、それがなかったためエラーが発生していました。

解決策

・shared/master.keyを作成
・シンボリックリンクにmaster.keyを追加

することでエラーがなくなりました。

参考:Capistranoで自動デプロイすると"Missing secret_key_base for 'production' environment"のエラーが出る

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1