はじめに
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一覧です。
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全体の設定ファイルです。
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
本番環境の設定を記述するファイルです。
# 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環境どちらにも当てはまる設定を記述します。
# 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のセットアップタスクを記述するファイルです。
# 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の設定ファイルです。
# ワーカーの数。後述
$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
production:
secret_key_base: jr934ugr89vwredvu9iqfj394vj9edfjcvnxii90wefjc9weiodjsc9o i09fiodjvcijdsjcwejdsciojdsxcjdkkdsv
(#生成した乱数を貼り付け)
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で使っている環境変数を記述します。
DB_NAME=*****
DB_USERNAME=*****
DB_PASSWORD=*****
DB_HOSTNAME=*****
/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のメモリが足りないために発生していた。
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"のエラーが出る