浏览 7150 次
锁定老帖子 主题:rails3项目解析之6——自动部署
精华帖 (9) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-09-05
最后修改:2011-09-07
上一篇:rails3项目解析之5——rails on windows
在那山的这边海的那边有一群部署员 他们辛苦又没钱 他们忙碌又危险 他们一天到晚坐在那里不停地在上传 有了问题就赶紧查网线~~ 哦 苦逼的部署员 哦 苦逼的部署员 只要一改代码他们就要重新发一遍 出点问题打击一大片 由于年代太过久远,我确实已经忘记了以前在java下面是肿么部署的了。隐约记得好象不是个轻松活。貌似要打各种包,上传各种文件,重启各种服务,对付各种问题。后来ant好象可以做自动部署,但得写一大坨xml文件,也忘了是不是好用了。 无数的实践已经证明并将继续证明,手动部署是一项十分坑爹的运动,仅次于跨个110米的栏就被人牵手两次。因为根据墨菲定律(Murphy's Law)最广为人知的变体表述,“会出错的终将出错”。人是一种很奇妙的生物,模糊计算的能力无比强大,精确执行的能力却又惨不忍睹。周岁的婴儿能够辨认出各种挤眉弄眼表情的妈妈,再严谨的成人却也无法保证连续100次做同一件事情而不出错。 那么,为了看起来能更加地fashion,有什么问题能更好地hold住,就把精确而繁琐的发布任务交给计算机来执行吧。 1、选型 在rails下自动部署工具一般会有两个选择:capistrano和vlad。前者貌似出得比较早,名声也大一点,后者据说做了一些改进,使用起来更为简单。忘了当时为什么没选vlad了,可能是capistrano印象比较深刻,各种插件相对还算齐全,所以在这个问题上也并没有太多的犹豫。使用vlad的同学,你们的选择也是正确的,不必过于纠结。 2、原理 从capistrano的gemspec可以看到,它使用了net-ssh、net-scp、net-sftp等各种net工具。因此,它的原理就是使用ssh依次登录各个服务器,按照事先规定的流程,拷贝文件,执行shell命令,等等。 capistrano自带的核心功能,主要就是ssh登录到服务器,然后从scm(如git、svn)上下载最新代码至服务器本地,拷贝至指定路径,这样就完成了代码的更新。 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' 然后在config/deploy目录下添加两个文件:alpha.rb和production.rb,针对不同的环境作相应配置。 这样,就形成了两个发布环境,alpha用于内部测试,production是产品环境,是正式运营服务器。 可以使用如下命令进行发布: cap deploy 因为已经设置default_stage为alpha,所以当省略环境名称的时候,即是发布至alpha服务器组。 如果要发布至产品环境,可加入一个参数显式指定环境: cap production deploy 4.2 服务器组配置 以production.rb为例: # 设置执行相关命令时的RAILS_ENV。 set :rails_env, 'production' # 设置该环境在git中的代码分支名。 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 => true role :bg, '192.168.1.50', '192.168.1.60', :no_release => true role :db, '192.168.1.90', :primary => true, :no_release => true 角色app:最新代码将发布至这个角色组中的所有服务器。 角色web:web服务器组,与此相关的命令,例如重启passenger,将在这个组中的服务器中执行。 角色bg:resque后台任务服务器组,发布时在这些服务器中重启所有resque任务。 角色db:数据库组。192.168.1.90是keepalived的虚拟地址(VIP),视主备运行状态绑定至物理服务器。 4.3 服务器路径 # 应用名 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 deploy_via :remote_cache可以在服务端保持一个缓冲池,当每次发布的时候,先git pull到服务端缓冲池中,然后再cp至发布路径,这样不必每次都git clone,可以节省很多时间。 4.4 用户 # 执行发布的用户名 set :user, 'www' # 不使用sudo set :use_sudo, false 4.5 代码库 set :scm, :git set :repository, 'ssh://git@192.168.1.188/xxx/xxx.git' 设置git路径,并指定使用ssh协议来进行传输。ssh权限使用公钥系统进行管理。有发布权限的开发人员,只需在本地配置好公钥,便可进行发布,而且不必输入密码,也不用在capistrano的配置文件中设置密码。 4.6 扩展bundle require 'bundler/capistrano' set :bundle_flags, '--quiet' bundle自带capistrano的task,引用一下就可以了,倒也省事。quiet是安静模式,避免看见满屏的信息。 引用bundle支持后,每次update_code更新完代码之后,都自动执行bundle:install安装新增和改变的gem。 为了节省bundle:install的时间,提高发布效率,需要做一个小小的改进: 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) end end 如果Gemfile中rails版本更改,自动bundle:install会产生依赖失败,因此,如果rails版本更改,则需要传入-s update=true参数,触发bundle update语句,先升级rails再执行后续流程。 扩展bundle后,项目所依赖的gem就不再安装到ruby的系统路径下,而是安装到发布路径,而且owner是用户www。以后如果需要登录到服务器上执行诸如rake命令,都需要在前面加bundle exec,以把当前命令置入bundle环境执行。 4.7 自定义任务 namespace :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" end end 另外,rails 3.1需要先预编译一下,提高asset pipeline的效率。因此可以设置 task :precompile, :roles => :web do run "cd #{current_path} && #{rake} RAILS_ENV=#{rails_env} assets:precompile" end capistrano刚出的2.8.0的版本,貌似已经加入了自动precompile的任务。不过因为我已经被离职,已不方便再拿人家的服务器做小白鼠了。有兴趣的同学可以自行体验。 4.8 回调 可以设置各种回调,指定各项task执行的顺序: after 'deploy:finalize_update', 'bundle:prepare' before 'deploy:symlink', 'deploy:links' after 'deploy:update', 'deploy:migrate', 'deploy:bg', 'deploy:precompile' 5、命令 # 发布 cap deploy # 数据迁移 cap deploy:migrate # 发布后发现有重大问题,需要立刻回滚至上一个正确版本。 cap deploy:rollback # 上面的命令都是发布至默认的alpha测试环境的,如果要发布至产品环境,则在cap后加入production即可 cap production deploy cap production deploy:rollback 在本项目中,当执行 cap deploy 时,capistrano执行如下操作: 5.1 执行update_code,从指定的git服务器pull最新代码,以当前时间戳为目录名拷贝至发布目录。 5.2 执行finalize_update,建立log、tmp等项目中的各个目录连接。 5.3 执行自定义bundle:prepare,建立.bundle和Gemfile.lock等bundle相关文件和目录的链接。 5.4 执行自定义deploy:links,链接database.yml等配置文件,链接项目中指定的文件目录。 5.5 执行symlink,链接最新代码目录至current。从此之后,所有命令均在最新代码上执行。 5.6 执行migrate,完成数据库迁移。 5.7 执行自定义deploy:bg,调用脚本kill掉所有resque后台任务进程,监控脚本将会全部自动重启。 5.8 执行自定义deploy:precomile,预编译rails 3.1的assets。 注:如果使用capistrano最新发布的2.8.0版,则内置的assets:precompile指令在update_code之后自动执行。 发布的时候,如果成功,则会报告全部发布任务执行完毕。如果出错,将会回滚至发布前状态。所有服务器上的输出信息都会回馈到本地控制台中,如果服务器数量比较多的话,看起来还是很壮观的。满屏的消息一直翻滚,一般情况是看不清具体内容的,比较适合于用来拍科幻片。就算是逐格慢放,能够看懂这些内容的人也不会很多,不至于象某部片子那样,用一个excel文件当成黑客程序来糊弄广大人民。 6、常用维护任务 capistrano可以做一些常用的服务器维护任务,例如apt-get install等。因为如果在服务器数量太多的情况下,在本地敲一个命令就能够完成全部服务器的升级,也能节省不少的时间。 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" end end 需要注意的是,因为是系统维护,所以这里的执行用户,就得是admin组里的管理用户了。pty选项是为了开启交互模式,以输入sudo密码。 fetch(:name)是接收命令行传入的apt包名称。完整命令如下: cap apt:install -s name=mongodb-10gen 不过在执行服务器批量维护命令时一定要慎重,必须在测试服务器上全面测试以确保一切顺利,避免新版本的apt包引起可能的潜在问题。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-09-06
LZ 辛苦了 一直跟着您的文章读,收获很多,可惜公司不打算采用rails开发新的项目~- -#
|
|
返回顶楼 | |
发表时间:2011-09-06
强烈建议lz出书!
|
|
返回顶楼 | |
发表时间:2011-09-06
继续学习
|
|
返回顶楼 | |
发表时间:2011-09-09
嗯,我们已经采用自动部署了,现在3台服务器;不过现在最头疼的还是在性能优化上,每台服务器日访问量在300万以上,现在架构是lighttpd + fastcgi,8G内存+8G swap,经常吃光而且不释放,最近在找问题所在
|
|
返回顶楼 | |
发表时间:2011-09-13
虽然不能达到一劳永逸的功效,但是至少不用去专门招人来做部署的事了, 不管公司省下来的钱花到哪里去了, 我们还是可以把省下来的时间做些其他有意义的事去
|
|
返回顶楼 | |
发表时间:2011-09-14
zeeler 写道 嗯,我们已经采用自动部署了,现在3台服务器;不过现在最头疼的还是在性能优化上,每台服务器日访问量在300万以上,现在架构是lighttpd + fastcgi,8G内存+8G swap,经常吃光而且不释放,最近在找问题所在
你们不用nginx+ree+passenger是什么原因呢,是效率问题吗,还是说内存管理更加不好。 |
|
返回顶楼 | |
发表时间:2011-09-14
bluebu 写道 虽然不能达到一劳永逸的功效,但是至少不用去专门招人来做部署的事了, 不管公司省下来的钱花到哪里去了, 我们还是可以把省下来的时间做些其他有意义的事去
嗯,你可以省下大把的时间,去和法国浪漫主义风格的女同事们暧昧调笑一番。。。。。。 |
|
返回顶楼 | |