部署 利用 Mina 自动部署 Rails + Sidekiq + Unicorn

luolinae86 · 发布于 2015年7月27日 · 最后由 luolinae86 回复于 2015年8月05日 · 5246 次阅读
10603
本帖已被设为精华帖!

社区上面关于自动部署的文章比较完整和齐全了,在此,我仅将自己最近用mina部署Rails+sidekiq+unicorn的整个过程,做一个总结,希望对新手做一个指引,也抛砖引玉,希望更多高手发表自己的部署经验,共勉。

部署方案选择

Rails自动部署,主流的方案有:

  • Capistrano
  • Mina

刚开始选择了capistrano,感觉比较折腾,最终选择了mina。最后发现,选择mina是值的,特别是配合mina-sidekiq,和mina-unicorn,让整个部署更加简洁,高效。

部署步骤

Mina 地址https://github.com/mina-deploy/mina

引入mina gem

在Gemfile中增加:

gem 'mina'

初始化(mina init)

在相应的Rails工作目录,执行

$ mina init

mina init命令会在config目录生成一个默认的配置文件deploy.rb

引入mina-sidekiq

项目中,运用到了sidekiq做异步队列:

https://github.com/Mic92/mina-sidekiq/blob/master/README.md

Gemfile中增加

gem 'mina-sidekiq',:require => false 

引入 mina-unicorn

应用服务器用的unicorn,使用mina-unicorn,可以非常方便完成进程的启,停,重启

https://github.com/scarfacedeb/mina-unicorn/blob/master/lib/mina/unicorn/tasks.rb

Gemfile中增加:

gem 'mina-unicorn', :require => false  

注:以下配置都是修改config/deploy.rb文件

配置目标主机

set :domain, '*.*.*.*'
set :deploy_to, '/data/project/science_read'
set :user, 'deployer'    
set :port, '3118'    
set :forward_agent, false

其中:domain为目标主机IP,deploy_to为目标主机的部署路径,user为用户名,port为ssh的端口号。 注:请将自动部署机器的~/.ssh/id_rsa.pub,放置到目标机器的~/.ssh/authorized_keys列表里面,从而完成自动登陆

设置git地址

set :repository, 'git@git.aliyun.tata.cn.com:hesheng/science_read.git'
set :branch, 'master'

设置sidekiq和unicorn进程pid保存地址

set :sidekiq_pid, "#{deploy_to}/tmp/pids/sidekiq.pid"
set :unicorn_pid, "#{deploy_to}/tmp/pids/unicorn.pid"

设置rvm的安装路径及ruby版本

set :rvm_path, '/usr/local/rvm/bin/rvm'
task :environment do
  invoke :'rvm:use[ruby-2.0.0@default]'
end

设置共享文件或目录

set :shared_paths, ['config/database.yml', 'config/yetting.yml','config/symmetric-encryption.yml','log']

以上设置了log目录,以及database.yml,yetting.yml,symmetric-encryption.yml为所有版本共享

设置目标主机目录及文件 (mina setup)

以下是执行mina setup命令时,在远程目录机执行的创建目录或者文件的操作

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/tmp/sockets/"]
  queue! %[mkdir -p "#{deploy_to}/tmp/pids/"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]

  queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"]
  queue! %[touch "#{deploy_to}/#{shared_path}/config/yetting.yml"]
  queue! %[touch "#{deploy_to}/#{shared_path}/config/symmetric-encryption.yml"]
  queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml','yetting.yml', 'symmetric-encryption.yml'."]

增加部署命令(mina deploy)

以下是执行mina deploy命令时,执行的任务操作,是整个远程部署的核心

desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do
    # Put things to run locally before ssh
  end
  deploy do
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      # sidekiq stop accepting new workers
      invoke :'sidekiq:quiet'
      invoke :'sidekiq:restart'
      invoke :'unicorn:restart'
    end
  end
end             

task:deploy,首先从repository的branch中clone一份代码到deploy_to目录,然后生成连接目录及文件,执行bundle, db_migrate,assets_precompile命令 ,最后会执行launch,里面的sidekiq 异步任务的重启,以及unicorn进程重启。

修改共享文件

编辑,database.yml,yetting.yml,以及symmetric-encryption.yml文件,以使其符合正式部署的需求。

修改config/unicron配置文件

listen "/data/project/science_read/tmp/sockets/unicorn.sock", :backlog => 64
pid "/data/project/science_read/tmp/pids/unicorn.pid"

之所以这样修改,是因为,自动部署后,APP_HOME = Rails.root会从未部署时候的/data/project/science_read/变成/data/project/science_read/current

执行mina setup远程部署命令

$ mina deploy

设置crontab 定时远程部署

在实际的项目中,可能会定时完成自动部署,比如每小时的0分钟时部署一次,crontab 任务

0 * * * * cd /data/project/mina_do_not_remove/science_read && mina deploy

整个config/deploy.rb部署脚本

require 'mina/bundler'
require 'mina/rails'
require 'mina/git'
require 'mina/rvm'    
require 'mina/unicorn'
require "mina_sidekiq/tasks"

set :domain, '*.*.*.*'
set :deploy_to, '/data/project/science_read'
set :user, 'deployer'         
set :port, '3118'            
set :forward_agent, false

#设置git地址及分支
set :repository, 'git@git.aliyun.zeta.cn.com:hesheng/science_read.git'
set :branch, 'master'

set :rvm_path, '/usr/local/rvm/bin/rvm'

set :shared_paths, ['config/database.yml', 'config/yetting.yml','config/symmetric-encryption.yml','log']

#设置sidekiq的进程保存地址
set :sidekiq_pid, "#{deploy_to}/tmp/pids/sidekiq.pid"


#设置unicorn pid 及 进程启动环境
set :unicorn_pid, "#{deploy_to}/tmp/pids/unicorn.pid"
set :unicorn_env, 'production'

task :environment do
  invoke :'rvm:use[ruby-2.0.0@default]'
end

task :setup => :environment do
  # unicorn and sidekiq needs a place to store its pid file
  queue! %[mkdir -p "#{deploy_to}/tmp/sockets/"]
  queue! %[mkdir -p "#{deploy_to}/tmp/pids/"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]

  queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"]
  queue! %[touch "#{deploy_to}/#{shared_path}/config/yetting.yml"]
  queue! %[touch "#{deploy_to}/#{shared_path}/config/symmetric-encryption.yml"]
  queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml','yetting.yml', 'symmetric-encryption.yml'."]

  queue %[
    repo_host=`echo $repo | sed -e 's/.*@//g' -e 's/:.*//g'` &&
    repo_port=`echo $repo | grep -o ':[0-9]*' | sed -e 's/://g'` &&
    if [ -z "${repo_port}" ]; then repo_port=22; fi ]
end

desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do
    # Put things to run locally before ssh
  end
  deploy do
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      # sidekiq stop accepting new workers
      invoke :'sidekiq:quiet'
      invoke :'sidekiq:restart'
      invoke :'unicorn:restart'
    end
  end
end
共收到 21 条回复
4215

主标题和二级标题不清晰。二级要用四个#号,才有区别。

10603

@chenge 二级标题已经修改为head 4

1638

请问mina到底比cap快在哪些地方?

一些比较慢的操作是不可避免的呀。

比如每次部署最耗时间的部分 assets compile 是逃不过去的呀?

10603

@xiaoronglv 我尝试过用cap部署,但比较麻烦,我就直接改用mina了,所以没有cap和mina的直接对比。 mina配合mina-sidekiq, mina-unicorn,整个部署起来的确非常简单,容易

-----> DB migrations unchanged; skipping DB migration
-----> Skipping asset precompilation
-----> Cleaning up old releases (keeping 5)
-----> Deploy finished
-----> Building
-----> Moving build to releases/2
-----> Build finished
-----> Launching
-----> Updating the current symlink
-----> Launching
-----> Stop sidekiq
       Sidekiq shut down gracefully.
-----> Start sidekiq
-----> Duplicating Unicorn...
-----> Done. Deployed v2
       Elapsed time: 7.26 seconds

从部署日志来看,如资源文件不变,那么会跳过预编译,整个部署时间在10s内。

-----> Skipping asset precompilatio
15420

10秒内。。。相比,cap花的时间比较长

10603

@pathbox 每次部署时间和工程大小,改动的多少,执行任务的个数强相关,所以光看某一次的部署时间,无法反应更无法横向比较mina和cap的部署速度。

该文章,主要是利用mina并接合mina-sidekiq和mina-unicorn两个gem包,将整个部署变得简单明了一些,不用自己再去写一些重启服务的脚本。

社区用cap部署的用户也不在少数,做为开发人员,我们选择适合自己的部署方式就行了 .^

16325

请问你们是部署在国内哪个托管服务器上?

10603

@george911 目前我们用的阿里云服务器,带宽的选择可以选100M的按量付费

16325

@luolinae86 好的,谢谢。 请问数据库不用特别设置吗?我们用的postgresql.

10603

@george911 自动部署会执行db:migrations相关操作,但自动部署不会去动你环境中的数据库配置的,所以不用特别设置。

5984

问下楼主,mina有办法使用sudo吗, 支持输入密码进行sudo操作,cap2.0版本是支持的。

96

用 iOS 上的 Prompt 软件,配合 mina (将 mina 的 config 文件,在服务器上也放一份),在手机上也可以启动部署,很是方便。

10603

@Tim_Lang

在mina中 queue! 命令是执行shell相关操作的,你可以修改deploy脚本,加上sudo命令,但如果sudo需求输入密码,我觉得破坏了自动部署的过程,你可以对指定用户设置sudo不输入密码,从而保证部署时不用人为参与。

595

mina 是相比cap 要简洁的多,不过多个domain 部署,cap是可以的,不知道mina是否也行? 有大神解惑一下

10603

@elele,mina也是支持部署多个server的,定义一个domains的数组,然后遍历数组,分别执行deploy即可。

set :domains, %w[host1 host2 host3]

desc "Deploy to all servers"
task :deploy_all do
  isolate do
    domains.each do |domain|
      set :domain, domain
      invoke :deploy
      run!
    end
  end
end
583

感觉 database.yml setting.yml 之类的配置文件没必要共享。默认RAILS的方式把 production 的 value 放到 ENV 省事一些

10603

@suxu

配置文件共享的原因

这几个配置文件,涉及到帐号的敏感信息,没有上代码库,所以设置了共享,这样只需要编辑一次,后面部署的时候就不用再去动了。

默认RAILS的方式把 production 的 value 放到 ENV 省事一些

这一句,我没能够理解到你的意思,@suxu,能否以以代码配置的形式指点下,多谢。

583

@luolinae86 你好啊,先谢谢你的分享。我不知道你用的RAILS版本,4.0以后(具体哪个版本忘记了),默认的 database.yml 如下:

#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: metro_production
  username: metro
  password: <%= ENV['DATABASE_PASSWORD'] %> #配置好ENV 直接读取就可以,而database.yml可以加入版本库的

敏感信息配置到服务器环境变量里,然后所有的yml 都用 ENV 读取环境变量就可以了 ,

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  weixin_token: <%= ENV["WEIXIN_TOKEN"] %>
  weixin_appid: <%= ENV["WEIXIN_APPID"] %>
  weixin_secret: <%= ENV["WEIXIN_SECRET"] %>
  weixin_mchid: <%= ENV["WEIXIN_MCHID"] %>
  weixin_pay_secret: <%= ENV["WEIXIN_PAY_SECRET"] %>
10603

@suxu 非常感谢你的回复,你提供的这种方式值得参考。 Rails版本,我用的3.2

7614

bin rvm 可以吗? 确定是 set :rvm_path, '/usr/local/rvm/bin/rvm' 而不是: set :rvm_path, '/usr/local/rvm/scripts/rvm'

10603

@michael_roshen

这里的rmv_path,根据目标服务器rvm的安装路径而设定:

deployer@*******:~$ which rvm
/usr/local/rvm/bin/rvm

所以,不同用户的机器,which rvm 输出可能不同,从你的回复来看,你机器上面输出是: /usr/local/rvm/scripts/rvm

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册