nodeのデプロイ洗ってみたけど、capistranoでやっていたり、shellでやっていたりベストプラクティスなんなんだろーと色々悩んでました。
必須の要件としてはシンプルに2つ。
- rollbackできるようにデプロイのバージョニング
- downtime無しで管理
Require
スクリプト本体+http server
- nodejs
- coffee-script
process管理
deploy
- capistrano
下ごしらえ
packages
$ npm i pm2@latest -g
gems
$ bundle init
$ vim Gemfile
source 'https://rubygems.org'
gem 'capistrano', '~> 3.2.0'
gem 'capistrano-bundler', '~> 1.1.3'
gem 'capistrano-npm'
$ bundle
デプロイ設定
initialize
$ cap init
cap設定
$ vim Capfile
# Load DSL and Setup Up Stages
require 'capistrano/setup'
# Includes default deployment tasks
require 'capistrano/deploy'
require 'capistrano/bundler'
require 'capistrano/npm'
require 'capistrano/console'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
pm2をキックするスクリプトを作る
$ vim lib/capistrano/tasks/pm2.rake
require 'json'
require 'pry'
namespace :pm2 do
def start_app
within current_path do
execute :pm2, :start, fetch(:app_command)
end
end
def restart_app
within current_path do
execute :pm2, :restart, fetch(:app_command)
end
end
def stop_app
within current_path do
execute :pm2, :stop, fetch(:app_command)
end
end
def force_stop_app
within current_path do
execute :pm2, :stop, fetch(:app_command), '--force'
end
end
def graceful_reload_app
within current_path do
execute :pm2, :gracefulReload, fetch(:app_command)
end
end
def delete_app
within current_path do
execute :pm2, :delete, fetch(:app_command)
end
end
def app_status
within current_path do
ps = JSON.parse(capture :pm2, :jlist, fetch(:app_command))
if ps.empty?
return nil
else
# status: online, errored, stopped
return ps[0]["pm2_env"]["status"]
end
end
end
desc 'Start app'
task :start do
on roles(:app) do
start_app
end
end
desc 'Stop app'
task :stop do
on roles(:app) do
stop_app
end
end
desc 'Restart app gracefully'
task :restart do
on roles(:app) do
case app_status
when nil
info 'App is not registerd'
start_app
when 'stopped'
info 'App is stopped'
restart_app
when 'errored'
info 'App has errored'
restart_app
when 'online'
info 'App is online'
graceful_reload_app
end
end
end
desc 'Stop app immediately'
task :force_stop do
on roles(:app) do
force_stop_app
end
end
desc 'Delete app'
task :delete do
on roles(:app) do
delete_app
end
end
end
capistranoのdeploy設定をごにょごにょ
$ vim config/deploy.rb
lock '3.2.1'
set :application, 'YOUR_APP_NAME'
set :app_command, 'egg.coffee'
set :repo_url, 'git@github.com:foo/foo.git'
set :deploy_to, "/var/www/#{fetch(:application)}"
set :log_level, :debug
set :linked_dirs, %w{ bin log node_modules }
set :default_env, { node_env: "local" }
namespace :deploy do
desc 'Restart application'
task :restart do
invoke 'pm2:restart'
end
after :publishing, :restart
end
targetにlocal作る(local VMを対象にしています)
$ vim config/deploy/local.rb
set :branch, ENV['BRANCH'] || 'master'
set :user, 'vagrant'
role :app, [ "#{fetch(:user)}@foo-server" ]
server 'foo-server', user: fetch(:user), roles: %w{ app }
localVMに流す
VMにPM2入れておいて
$ cap local deploy:check
$ cap local deploy
ok
pm2の操作はrakeの通り
P.S.
pm2でもgitでdeployが可能(rubyで言うところのmina gem 形式)なので、それでやるほうがnodeの哲学に合ってるかもしれません。