部署 Capstrano 经验分享

piginzoo · 2012年03月21日 · 最后由 bhuztez 回复于 2012年08月11日 · 6620 次阅读

最近鼓捣 capstrano 并被之折磨,终于有了一些心得,不敢独享,share 给大家。

1.我们的的 capstrona 配置

# encoding: utf-8
require 'bundler/capistrano'                #添加之后部署时会调用bundle install, 如果不需要就可以注释掉
set   :keep_releases, 10                    #只保留10个备份
set   :application, "myapp"     #应用名称
set   :repository,  "git@myapp.com:/data/gitrepo/myapp"
set   :deploy_to,   "/data/www"
set   :current_public,   "/data/www/current/public"
set   :shared_path,      "/data/www/shared"
set   :user, "deploy"                       #登录部署机器(myapp.com)的用户名
set   :password, "123456"                   #登录部署机器的密码, 如果不设部署时需要输入密码
set   :use_sudo, false                      #执行的命令中含有sudo, 如果设为false, 用户所有操作都有权限
set   :scm, :git
set   :bundle_flags, '--quiet'
set   :copy_exclude, [".git", "spec"]
set   :group_writable, false
role  :web, "192.168.0.106"                     #Your HTTP server, Apache/etc
role  :app, "192.168.0.106"                     #This may be the same as your `Web` server
role  :db,  "192.168.0.106", :primary => true   #This is where Rails migrations will run
default_run_options[:pty] = true            #pty: 伪登录设备
#after, before 表示在特定操作之后或之前执行其他任务
after "deploy:update", "deploy:migrate"
after "deploy:migrate", "deploy:symbol_resource"
# If you are using Passenger mod_rails uncomment this:
namespace :deploy do
  desc "restart the paozhoumo application"
  task :restart, :roles => :app, :except => { :no_release => true } do
   run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
   # rails 3.1首次访问时间较长,发布成功后先访问一下,进行初始化
   run "curl -s -o /dev/null 127.0.0.1"
  end
  #用新的拉下来的public里面的内容替换旧的shared目录的,然后删除掉public,用shared链接到public
  desc "copy everyting to overwrite the shared public folder"
  task :symbol_resource do
    #拷贝新拉下来的images/css/jslib,以及目录下的文件
    run "rm -rf #{shared_path}/images/*"
    run "cp -r #{current_public}/images/* #{shared_path}/images/"
    run "rm -rf #{shared_path}/css/*"
    run "cp -r #{current_public}/css/* #{shared_path}/css/"
    run "rm -rf #{shared_path}/jslib/*"
    run "cp -r #{current_public}/jslib/* #{shared_path}/jslib/"
    run "rm -f #{shared_path}/*.*"
    run "cp #{current_public}/*.* #{shared_path}"
    #然后把拉下来的public目录删除掉
    run "rm -rf #{current_public}"
    #把共享的share文件夹中
    run "ln -sf #{shared_path} #{current_public}"
  end
end




2.几点说明:

这里有两个帐号,一个是 “git@myapp.com:/data/gitrepo/myapp”,也就是 git 服务器上的 git 帐号,还有一个是要部署到某台机器上的时候登录用的帐号:deploy。因此,如果你做本地的 ssh 的密码配置,在 cap deploy 过程中,需要输入 git 用户的密码,和 deploy 用户的密码。

这里有 3 台机器将卷入这个部署

  1. 你的开发机
  2. 你的 git 服务器(放着 team 的 merge 后的代码滴最新版)
  3. 你要去部署的服务器

过程就是你在你的开发机器上运行 cap deploy,然后远程登录到被部署的机器上跑 cap 的脚本,期间会自动到 git 服务器上拉代码,过程大抵如此。

后面我 trace 了一个整个过程的 shell 脚本,并加上注释,可以细看。

在第一次运行 cap deploy:setup 的时候,会报权限错误,毕竟 deploy 用户权限很低,不能随便创建目录,那么就用 root 或者其他有权限的帐号,先建好 my app 的根目录,然后 chown deploy:deploy_group /my_app_root 即可。

由于脚本中有了这行 require 'bundler/capistrano' ,cap 会在部署的时候,重新拉一份 gem 下来,放到 shared/bunld 目录下, 即使你生产机器上已经有 gem 了,小哥们还是会不知疲倦地下载一份,原因是,这个脚本触发了这条命令:bundle install --gemfile /data/www/releases/20120321020234/Gemfile --path /data/www/shared/bundle --quiet --without development test.我曾经担心过 rails 启动的时候,是去找这里面的 gem 还是去找系统目录下的 gem 呢,后来得知在 Myapp 目录下会生成一个 .bundld/config,告诉 rails 用哪个 gem 目录,就释然了。

3. cap 运行过程中的脚本:

#列出remote上最新的head
git ls-remote deploy@paozhoumo.com:/data/gitrepo/paozhoumo HEAD
#做一些目录清场和准备
rm -rf /data/www/releases/20120321020234/public/assets 
mkdir -p /data/www/releases/20120321020234/public        
mkdir -p /data/www/shared/assets
#做1个软连接,后面还有3个
ln -s /data/www/shared/assets /data/www/releases/20120321020234/public/assets
#改变目录为组可写
chmod -R g+w /data/www/releases/20120321020234
rm -rf /data/www/releases/20120321020234/log /data/www/releases/20120321020234/public/system /data/www/releases/20120321020234/tmp/pids 
mkdir -p /data/www/releases/20120321020234/public
mkdir -p /data/www/releases/20120321020234/tmp
#做了3个软连接
ln -s /data/www/shared/log /data/www/releases/20120321020234/log
ln -s /data/www/shared/system /data/www/releases/20120321020234/public/system
ln -s /data/www/shared/pids /data/www/releases/20120321020234/tmp/pids
#重新touch一下这些目录中的所有的文件
find /data/www/releases/20120321020234/public/images /data/www/releases/20120321020234/public/stylesheets /data/www/releases/20120321020234/public/javascripts -exec touch -t 201203210202.50 {} ';'; true
#列出文件来,-x是在一行的意思,不知道要干嘛?
ls -x /data/www/releases
cd /data/www/releases/20120321020234
#安装bundle到/shared/bundle里面,但是用的Gemfile是新拉下来的gemfile
bundle install --gemfile /data/www/releases/20120321020234/Gemfile --path /data/www/shared/bundle --quiet --without development test
cd /data/www/releases/20120321030939
#做资源预编译,地球人都知道
bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile
#改链接之类的后续活
rm -f /data/www/current && ln -s /data/www/releases/20120321030939 /data/www/current
cd /data/www/releases/20120321030939
#执行migration
bundle exec rake RAILS_ENV=production  db:migrate
。。。。
# 后面就没啥了



4.参考:

官方给的流程图:

@piginzoo 用过 keep_releases 选项了么?使用貌似不会默认保留指定的版本数,需要手动执行 cap deploy:cleanup

@comme ,还真没有,这个选项是照着文档上拷的,回头我试试。

在一台服务器,本来已经设置好,git 用户,和 deploy 用户权限之类了。所以写 deploy.rb 很简单。之前的服务器 cap deploy 都很顺利。然后自己弄个 VPS 的时候,发现好像权限之类的问题很复杂。实在不太理解,我 deploy 是存储站点,nginx 读取 deploy 用户,git 用户只是电脑代码 push 上去 XXX.GIT。git 和 deploy 之间的权限应该是怎么样才对? 然后请问有出现过这样的错误吗?

servers: ["74.207.243.XXX"]
connection failed for: 74.207.243.XXX (Net::SSH::Disconnect: connection closed by remote host)

疯狂了。弄了好久都没有搞清楚问题究竟是在哪里产生的。

楼主 是 Capistrano

passenger 的初始化加载可以在 nginx.conf 里面设置

passenger_pre_start http://foo.com/;

http://www.modrails.com/documentation/Users%20guide%20Nginx.html#PassengerPreStart

#2 楼 @piginzoo 可手动加after "deploy", "deploy:cleanup",默认会保留 5 个 release 版本

@fresh_fish @Rei @Zernel 可否知道 Net::SSH::Disconnect: connection closed by remote host 这个错误是因为什么呢?

#7 楼 @JeskTop

确定配置好 ssh-key? 试试在服务器手动连接一次 SSH,因为在第一次访问时,系统要询问你要不要将对方设为可信,这会导致 cap 出错……我部署第一次失败就是因为这个原因

其实最希望有个 Linux 高手讲解一下权限的问题

nginx 和 thin 是以 nginx 用户权限运行的 而布置 ssh 用户肯定不是 nginx,因为它一般设置为无法登陆 难道通过 sudo -u nginx git 这样来指定,这样 git 命令就是以 nginx 用户运行了,再设置 sudo 配置文件,只允许 ssh 用户 sudo 指定命令,那么这个 sudo 配置文件应该怎么写?

#8 楼 @HungYuHei 在 git 用户和 deploy 两个用户上都把自己的公钥加进.ssh/authorized_keys 里了,一开始只有 git 里有,后面提交不到,就往 deploy 里也加了。 在服务器手动连连接一次?只是我从 终端 ssh git@IP,到服务器吗?

#9 楼 @cxh116 就第一次需要改 nginx 配置吧,那就直接 root 去改了

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