rails3项目解析之6——自动部署
上一篇:rails3项目解析之5——rails on windows
在那山的这边海的那边有一群部署员
他们辛苦又没钱
他们忙碌又危险
他们一天到晚坐在那里不停地在上传
有了问题就赶紧查网线~~
哦 苦逼的部署员
哦 苦逼的部署员
只要一改代码他们就要重新发一遍
出点问题打击一大片
由于年代太过久远,我确实已经忘记了以前在java下面是肿么部署的了。隐约记得好象不是个轻松活。貌似要打各种包,上传各种文件,重启各种服务,对付各种问题。后来ant好象可以做自动部署,但得写一大坨xml文件,也忘了是不是好用了。
无数的实践已经证明并将继续证明,手动部署是一项十分坑爹的运动,仅次于跨个110米的栏就被人牵手两次。因为根据墨菲定律(Murphy's Law)最广为人知的变体表述,“会出错的终将出错”。人是一种很奇妙的生物,模糊计算的能力无比强大,精确执行的能力却又惨不忍睹。周岁的婴儿能够辨认出各种挤眉弄眼表情的妈妈,再严谨的成人却也无法保证连续100次做同一件事情而不出错。
那好,就把精确而繁琐的任务交给计算机来执行吧。
1、选型
在rails下自动部署工具一般会有两个选择:capistrano和vlad。前者貌似出得比较早,名声也大一点,后者据说做了一些改进,使用起来更为简单。忘了当时为什么没选vlad了,可能是capistrano印象比较深刻,各种插件相对还算齐全,所以在这个问题上也并没有太多的犹豫。使用vlad的同学,你们的选择也是正确的,不必过于纠结。
2、原理
从capistrano的gemspec可以看到,它使用了net-ssh、net-scp、net-sftp等各种net工具。因此,它的原理就是使用ssh依次登录各个服务器,按照事先规定的流程,拷贝文件,执行shell命令,等等。
3、需求
3.1 首先,安全性是第一位的。经验告诉我们,安全措施不到位会搞出人命,无论是人命++还是人命--。要求执行自动部署命令的用户名是普通用户,不在admin组,而且不被授予任何的sudo权限。避免别人通过网站漏洞取得权限之后啥事都能干了。
3.2 可以在多个环境进行部署。可以简单地根据命令参数,选择是在测试环境中发布,还是在产品环境中发布。
3.3 和bundle整合良好。rails越来越多地依赖bundle来进行gem管理,因此要融入bundle来做一些工作。
3.4 部署命令尽量简单易记,用最简单的20%的命令完成80%的部署任务。
3.5 配置文件中不出现任何的明文和密文密码。
4、具体配置
大体按照配置文件的自然顺序,逐一简单描述。
4.1 多环境部署
多环境部署需要用到capistrano的一个插件capistrano-ext,里面有一个ext叫multistage,可以符合我们的需求。
capistrano的主配置文件是config/deploy.rb,在该文件中添加
require 'capistrano/ext/multistage'set :stages, %w(alpha production)set :default_stage, 'alpha'
cap deploy
cap production deploy
# 设置执行相关命令时的RAILS_ENV。set :rails_env, 'production'# 设置该环境在scm中的代码分支名。set :branch, 'production'# 设置服务器角色,no_release代表不再重复发布代码。role :app, '192.168.1.30', '192.168.1.40', '192.168.1.50', '192.168.1.60', '192.168.1.70', '192.168.1.80'role :web, '192.168.1.30', '192.168.1.40', :no_release => truerole :bg, '192.168.1.50', '192.168.1.60', :no_release => truerole :db, '192.168.1.90', :primary => true, :no_release => true
# 应用名set :application, 'xxx'# 发布根路径set :root_path, '/xxx'# 代码发布路径set :deploy_to, "#{root_path}/web"# 存储代码中共用的文件,例如database.yml、payment.yml等。set :project, "#{deploy_to}/shared/project"# 通过远程缓存发布set :deploy_via, :remote_cache# 执行发布的用户名set :user, 'www'# 不使用sudoset :use_sudo, false
set :scm, :gitset :repository, 'ssh://git@192.168.1.188/xxx/xxx.git'
require 'bundler/capistrano'set :bundle_flags, '--quiet'
namespace :bundle do task :prepare, :roles => :app do # 链接文件,而不必每次都重新生成 run "ln -sfT #{project}/.bundle #{latest_release}/.bundle" run "ln -sf #{project}/Gemfile.lock #{latest_release}/Gemfile.lock" # 指定执行update run "cd #{latest_release} && bundle update" if exists?(:update) endendnamespace :deploy do task :start do ; end task :stop do ; end # 重启服务,适用于passenger task :restart, :roles => :web do run "touch #{current_path}/tmp/restart.txt" # rails 3.1首次访问时间较长,发布成功后先访问一下,进行初始化 run "curl -s -o /dev/null 127.0.0.1" end task :links, :roles => :app do # 建立配置文件链接 # 因为这两个文件中的信息比较敏感,因此代码库中是不保存的,真实的文件只保存在服务器上 run "ln -sf #{project}/config/database.yml #{latest_release}/config/database.yml" run "ln -sf #{project}/config/payment.yml #{latest_release}/config/payment.yml" # 建立文件目录链接,可以按照自己的需求添加 run "ln -sfT #{root_path}/file/ckeditor_assets #{latest_release}/public/ckeditor_assets" end task :bg, :roles => :bg do # 因为使用了resque作为后台任务,所以每次代码发布后,有可能会更改了后台任务的执行代码,因此需要杀掉resque相关的进程,再由monitor自动重启,让resque使用最新的代码。 run "ruby #{current_path}/script/resque.rb kill" endend task :precompile, :roles => :web do run "cd #{current_path} && #{rake} RAILS_ENV=#{rails_env} assets:precompile" endafter 'deploy:finalize_update', 'bundle:prepare'before 'deploy:symlink', 'deploy:links'after 'deploy:update', 'deploy:migrate', 'deploy:bg', 'deploy:precompile'
# 发布cap deploy# 数据迁移cap deploy:migrate# 发布后发现有重大问题,需要立刻回滚至上一个正确版本。cap deploy:rollback# 上面的命令都是发布至默认的alpha测试环境的,如果要发布至产品环境,则在cap后加入production即可cap production deploycap production deploy:rollback
namespace :apt do task :install, :roles => :app do set :user, 'xxx' default_run_options[:pty] = true sudo "apt-get -y install #{fetch(:name)}" end task :upgrade, :roles => :app do set :user, 'xxx' default_run_options[:pty] = true sudo "apt-get update" sudo "apt-get -y upgrade" sudo "apt-get -y dist-upgrade" endendcap apt:install -s name=mongodb-10gen