使用Capistrano部署Node.js脚本

尝试部署Node.js时,我对于使用Capistrano还是使用shell脚本,以及什么是最佳实践等问题纠结了很久。

必须有两个基本要求。

    • rollbackできるようにデプロイのバージョニング

 

    downtime無しで管理

需要

脚本本体+HTTP服务器

    • nodejs

 

    coffee-script

进程管理

    pm2

部署

    capistrano

准备工作

软件包

$ npm i pm2@latest -g

宝石

$ bundle init
$ vim Gemfile
source 'https://rubygems.org'

gem 'capistrano', '~> 3.2.0'
gem 'capistrano-bundler', '~> 1.1.3'
gem 'capistrano-npm'

$ bundle

部署设置

初始化

$ cap init

帽子的设置

$ 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的部署设置进行调整

$ 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)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 }

将其发送到本地虚拟机中。

在虚拟机里安装PM2。

$ cap local deploy:check
$ cap local deploy

pm2的操作与rake相同。

附言

因为即使在PM2上,也可以使用Git进行部署(类似于Ruby中的Mina gem格式),所以可能使用这种方式更符合Node.js的理念。

bannerAds