捨棄 forever 吧, PM2 是個好東西.

本機安裝 Capistrano:

gem install capistrano

初始化:

cap install

然後在 config/deploy/ 中設定各環境的主機位置等資訊.

config/deploy.rb 設定共同資訊 (application, repo_url, deploy_to, …), 其中要特別提到的有:

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('node_modules')

# Default value for default_env is {}
set :default_env, { node_env: 'production' }

node_modules 設為 linked_dirs 可以避免每次 npm install 都重新安裝一樣的套件.

加入這些代碼, 就會在更新完程式碼後跑 npm install --production:

namespace :npm do
  task :install do
    on roles(:app) do
      within release_path do
        execute :npm, 'install', '--production'
      end
    end
  end
end

before 'deploy:updated', 'npm:install'

在遠端主機上準備 <deploy_to>/shared/processes.json (自行替換 <deploy_to><application_name>):

{
  "apps" : [{
    "name"        : "<application_name>",
    "script"      : "index.js",
    "instances"   : 0,
    "exec_mode"   : "cluster_mode",
    "run_as_user" : "deploy",
    "watch"       : false,
    "node_args"   : "--harmony",
    "merge_logs"  : true,
    "cwd"         : "<deploy_to>/current",
    "env": {
      "NODE_ENV": "production"
    }
  }]
}

並且安裝好 pm2:

npm install -g pm2

回到本機, 在 deploy.rb 中加入:

namespace :pm2 do
  task :start do
    on roles(:app) do
      within shared_path do
        execute :pm2, 'start processes.json'
      end
    end
  end

  task :restart do
    on roles(:app) do
      execute :pm2, 'startOrReload <application_name>'
    end
  end

  task :stop do
    on roles(:app) do
      execute :pm2, 'stop <application_name>'
    end
  end
end

after 'deploy', 'pm2:restart'

現在就可以使用 cap staging deploy 部署了.