プロジェクト初期にしかやらないから、毎回忘れて泣きながら調べてる。 ので、備忘録を兼ねて。
ちなみにこの手順を書くにあたって、社内のナレッジ ( docbase 使ってます ) を確認していたら、退社した元 CTO 兄貴迫真のカレーレシピを見つけて「日本人だいたい同じようなレシピに行き着くのか?」と DASH カレーと見比べてた。
作業環境は既存のプロジェクトでも、なんなら rails #{プロジェクト名}
して scaffold しただけの環境でも構いません。 デプロイするのが目的なら、他の Gem と競合することもそうそう無いはず。
また、本作業は以下を想定しています。
- puma
- Unicorn だとスロークライアント掴んだ時や、同期系処理でプロセス掴みっぱなしの時、処理できる数が減ってしまうのでその対策も兼ねています。
- webpack
- 使ってなかったりわかんなかったら読み飛ばしてオッケー
- ENV に staging を追加
- 標準で用意されてるの development と production だけなの難しくない?
Gem の追加
以下を Gemfile に追加します。
基本的にデプロイ先の環境には不要な Gem ので、 group :development do
内に記載するのが簡単で良いでしょう。 しっかりやるのであれば、 group :deploy
なり、デプロイ向けの専用グループを作ると良いですが、普段めったにやらないので割愛します。
- capistrano
- capistrano-bundler
- capistrano-rails
- capistrano3-puma
記述追記後に、以下のコマンドで Gem を追加します
bundle install ${必要なら path をつける}
Gemfile
group :development do # なんか他の記述がある # # Deploy gem "capistrano", "~> 3.11", require: false gem 'capistrano-bundler', require: false gem 'capistrano-rails', require: false gem 'capistrano3-puma', require: false end
デプロイファイルの雛形を作る
以下のコマンドでジュッと追加します。
bundle exec cap install
ENV に staging を追加する
そんなに難しくなくて、用意されたファイルをコピーすれば追加できます。
vi config/database.yml cp config/environments/production.rb config/environments/staging.rb vi config/webpacker.yml
config/database.yml
以下を production:
の直上あたりに追加。
staging: <<: *default database: データベース名_staging
config/webpacker.yml
webpacler 使ってるかわからない場合、ファイルが存在しなければ飛ばして良いです。
staging: <<: *default compile: false extract_css: true cache_manifest: true public_output_path: packs
デプロイファイルを各種設定する
Capistrano がデプロイ時に参照するファイルを設定します。
Capfile
Capistrano が読み込む動作を設定します。
Capfile
# Load DSL and set up stages require "capistrano/setup" # Include default deployment tasks require "capistrano/deploy" # Load the SCM plugin appropriate to your project: require "capistrano/scm/git" install_plugin Capistrano::SCM::Git # Include tasks from other gems included in your Gemfile require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'capistrano/puma' install_plugin Capistrano::Puma # Default puma tasks install_plugin Capistrano::Puma::Workers # if you want to control the workers (in cluster mode) # Load custom tasks from `lib/capistrano/tasks` if you have any defined Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
deploy.rb 共通処理
デプロイ時の設定のうち、すべての環境で共通するものを記載します。 ENV ごとに個別に記載したいものは、以降の項目で別途記載できます。
また、以下に必要と思われる項目を簡単に解説をば。
set :application, "example"
- デプロイするアプリケーション名です、なんでもよかったはず。 大体リポジトリ名をつけとくとわかりやすくて良い。
set :repo_url, "git@github.com:example/example.git"
- デプロイに利用するリポジトリ URL です。以下で指定する
:local_user
で何もオプションつけず特別なことをせず、git clone ${URL}
だけで clone できるようにしておく必要があります。
- デプロイに利用するリポジトリ URL です。以下で指定する
set :branch, ENV['BRANCH'] || "master"
set :deploy_to, "/var/www/example"
- デプロイ先ディレクトリです。
- 実際のドキュメントルートはこのパスに
/current/
が付与されます。 Capistrano はこの current をシンボリックリンクを切り替える事で管理しており、実体は同階層の/releases/年月日時/
となります。
set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
- Capistrano の動作ログを、実行した環境の log 以下に出力します。
set :local_user, -> { "ec2-user" }
- デプロイ先で操作やらプロセス立ち上げを担当するローカルユーザを指定します、 SSH 操作を行うユーザと同一にしておいてください。 たぶん分離できると思うけどめんどくて試してない(本音)。
set :keep_releases, 5
- デプロイしたファイルを何世代残すかの設定です。 この残してる世代だけロールバックできるので、最低でも
2
は指定するようにしてください。5
なのは過去の知見から「5回も連続してデプロイ失敗せんじゃろ」という感じで設定しています。
- デプロイしたファイルを何世代残すかの設定です。 この残してる世代だけロールバックできるので、最低でも
config/deploy.rb
lock "~> 3.11.2" # cap set :application, "example" set :repo_url, "git@github.com:example/example.git" set :branch, ENV['BRANCH'] || "master" set :deploy_to, "/var/www/example" set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto set :local_user, -> { "ec2-user" } set :keep_releases, 5 append :linked_files, "config/puma.rb" append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/assets", "public/packs", "node_modules" # puma set :puma_conf, -> { File.join(shared_path, 'config/puma.rb') } set :puma_daemonize, true desc "Restart Application" task :restart do on roles(:app), in: :sequence, wait: 5 do invoke 'puma:restart' end end after 'deploy:publishing', 'deploy:restart'
staging.rb / production.rb ENV ごとの設定ファイル
与えられた ENV によって、設定 ( 実行 ) する内容を分けるために利用します。 最低限 IP は別だと思うのと、ファイル無いと動かないと思うので、DRY! とかいって全部 deploy.rb にかくのはやめようね……。
set :stage, :staging
- 動作させる
RAILS_ENV
を指定します、ファイル名とイコールとしましょう、紛らわしいからね
- 動作させる
server 'xxx.xxx.xxx.xxx', user: 'ec2-user', roles: %w[web app db]
- server: IP もしくはドメインを指定します
- user: SSH 接続するユーザを指定します
- roles: これがちょっと難しくて、Capistrano の操作 ( タスク ) には、それと関連した role が割り当てられています。 たとえば、 db の role を持つ対象は
rails db:〜
を実行しますが、割り当てられなければ実行されません。- これは複数台のサーバが存在する場合、同時に migrate が実行されるのを防ぐ、などのパターンが思いつきます。 とりあえずわからないうちは、 role は基本
[web app db]
を書くのは一台だけ、複数台ある場合の残りは[web app]
のみ、と考えてると良いです、残りは頑張って公式の README とかコード読もう。
- これは複数台のサーバが存在する場合、同時に migrate が実行されるのを防ぐ、などのパターンが思いつきます。 とりあえずわからないうちは、 role は基本
keys: [File.expand_path('~/.ssh/aws_good-looking.pem')],
vi config/deploy/staging.rb vi config/deploy/production.rb
config/deploy/staging.rb
set :stage, :staging server 'xxx.xxx.xxx.xxx', user: 'ec2-user', roles: %w[web app db] set :ssh_options, { keys: [File.expand_path('~/.ssh/aws_good-looking.pem')], forward_agent: false, auth_methods: %w{publickey} }
config/deploy/production.rb
set :stage, :production server 'xxx.xxx.xxx.xxx', user: 'ec2-user', roles: %w[web app db] set :ssh_options, { keys: [File.expand_path('~/.ssh/aws_good-looking_production.pem')], forward_agent: false, auth_methods: %w{publickey} }
デプロイ先ディレクトリを用意する
以下のコマンドを実行すると、 SSH 疎通可能であれば、デプロイ先ディレクトリの不足分を補って雛形を生成してくれます。 ${ENV}
の部分は、環境に合わせて変更してください。 本手順なら staging
production
のどちらかが利用できますね。
また、初回実行するとディレクトリがなくてエラー吐いて停止すると思いますが、そのときにひな形ディレクトリが生成さっるため、続けて二回目を実行すると正常に終了すると思います。 だめだったらなんか独自設定追加されてるはずだから、手動で足りないディレクトリとかファイル追加してね。
この時、 config/
以下の puma 系の値に応じて puma 用のコンフィグファイルがついでに生成されます。 手動で作る場合は bundle exec cap ${ENV} puma:config
でリモートに再生成できます。 しかし強制的に上書きしてバックアップも残らないので、リモートでだけ、手動で変更を加えていると死が待っているので、手動での変更は……やめようね!
参考: qiita.com
bundle exec cap ${ENV} deploy:check
デプロイする
おまたせしました、実際に反映してみましょう! コマンドは以下です。 また、例によって ${ENV}
は環境に合わせてご記載ください。
bundle exec cap ${ENV} deploy
内容によってはだいぶ時間がかかりますが無事デプロイできたでしょうか? ちなみに上記は環境変数 BRANCH
未設定のため、 master が反映されるはずです。 ブランチを指定したい場合は以下を利用してください。
BRANCH=${対象のブランチ名} bundle exec cap ${ENV} deploy
ついでですが、Capistrano はリモートリポジトリの内容を反映するので、自分のローカルの作業中ブランチを反映するものではありません。 試した内容を誰にもバレずにこっそり試したい、というのはあると思いますが、諦めて適当なブランチをリモートに切ってください。 そしてやらかしたらブランチをクローズするのです。
このあとやること
実運用に持っていくには、おそらく whenever
と delayed_job
あたりが大体必要じゃないかな、と思います。 以下簡単に説明を記載しておきます。
whenever
Gemfile
全体に適応する箇所に記載して、 bundle install
する。
gem 'whenever', require: false
Capfile
require 'whenever/capistrano'
config/deploy.rb
set :whenever_identifier, -> { "#{fetch(:application)}_#{fetch(:stage)}" }
config/schedule.rb
require File.expand_path(File.dirname(__FILE__) + "/environment") rails_env = ENV['RAILS_ENV'] || :development set :environment, rails_env set :output, "#{Rails.root}/log/whenever.log" env :PATH, "/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin" 実行するスケジュールを指定する do rake とかのむっちゃ良い処理を書く end
delayed_job
探すと Gem ふたつ出てくると思うんですが、こっちが delayed_job の README に記載されている ( たぶんこっちしか最新バージョンだと動かない ) ので、採用しています。
何より以下の記述どおりにすると、 デプロイ後に :publishing
と書いている所で合わせて delayed_job 再起動してくれるので反映し忘れも防げるはず。
Gemfile
全体に適応する箇所に記載する。
gem 'delayed_job_active_record' gem 'daemons'
capistrano 系の記述の直下に追加する。
gem 'capistrano3-delayed-job', require: false
終わったら bundle install
する。
Capfile
require 'capistrano/delayed_job'
config/deploy.rb
desc "Restart Application"
の直上に挿入しておいてください、 role はとりあえず app で良いと思います。 場合によって変更してね。
# delayed_job set :delayed_job_workers, 1 set :delayed_job_roles, [:app]