部署 Capistrano 3 实现 Rails 自动化部署

lanvige · 2014年04月15日 · 最后由 rocLv 回复于 2015年09月12日 · 30402 次阅读
本帖已被管理员设置为精华贴

原文始发:http://lanvige.github.io/2014/02/21/using-capistrano-3-to-deploy/

Offical Site: http://capistranorb.com/
Github: https://github.com/capistrano/capistrano

Capistrano 3.1 相对于之前的 2.x 有着很大不同,本想用自动化发布,应该是分分种搞定的事情,没想到找文档,看说明,花了一天也没看出个什么,于是晚上花时间把 2.x 和 3.1 的代码过了下,做了个对比,总算搞明白了里面的一些机制。

使用版本
unicorn 4.8.2
rails 4.1.0
ruby 2.1.1
capistrano 3.1

安装::

Gemfile中添加 Capistrano 和其它用到的插件

group :development do
  gem 'capistrano'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  # Add this if you're using rvm
  # gem 'capistrano-rvm'
end
项目中初始化 Capistrano
$ cap install

会生成如下目录文件,Capfile用来配置 Capistrano,deploy.rb是一些共用 task 的定义,而production.rb/staging.rb用来定义具体的 stage 的 tasks。

├── Capfile   
├── config   
│   ├── deploy   
│   │   ├── production.rb   
│   │   └── staging.rb   
│   └── deploy.rb   
└── lib   
    └── capistrano   
        └── tasks

安装完成之后,通过 cap -vT 来查看当前项目的可执行任务列表。

cap bundler:install                # Install the current Bundler environment
cap deploy                         # Deploy a new release
cap deploy:check                   # Check required files and directories exist
cap deploy:check:directories       # Check shared and release directories exist
cap deploy:check:linked_dirs       # Check directories to be linked exist in shared
cap deploy:check:linked_files      # Check files to be linked exist in shared
cap deploy:check:make_linked_dirs  # Check directories of files to be linked exist in shared
...
cap install                        # Install Capistrano, cap install STAGES=staging,production

这些命令其中就包括 刚刚执行的cap install,其它用到的,后面再讲。

配置 Capistrano

- 在Capfile里开启要用到的一些插件

require 'capistrano/setup'
require 'capistrano/deploy'

require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }

###

本文件中用来配置共用变量。

# config valid only for Capistrano 3.1
lock '3.1.0'

set :application, 'appname'
set :deploy_user, 'ares'

set :scm, :git
set :repo_url, '[email protected]:lanvige/railsapp.git'

# rbenv
set :rbenv_type, :user
set :rbenv_ruby, '2.1.1'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}

# how many old releases do we want to keep, not much
set :keep_releases, 5

# files we want symlinking to specific entries in shared
set :linked_files, %w{config/database.yml config/application.yml config/secrets.yml}

# dirs we want symlinking to shared
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

namespace :deploy do
  after :finishing, 'deploy:cleanup'
end
  • rbenv 的配置见这里
  • linked_files & linked_dirs 见这里

- 配置Stage

关于 Stage,详见:

一个很重要的配置是 Role、Server(User)以及其对应关系,为了方便,Cap3 中提供了多种配置形式,各有不同的侧重,但用途都是一样的,见下面。

# 以role为中心的写法
role :app, %w{[email protected], [email protected]}
role :web, %w{[email protected]}
role :db,  %w{[email protected]}

# 以server为中心的写法,上面的写法可以用以下写法代替:
server 'example.com', user: 'deploy', roles: %w{web app db}
server 'example.local', user: 'deploy', roles: %w{app}

# 如果要对某服务器配置SSH等更多时,也可以用这种写法:
server 'example.com',
  user: 'user_name',
  roles: %w{web app},
  ssh_options: {
    user: 'user_name', # overrides user setting above
    keys: %w(/home/user_name/.ssh/id_rsa),
    forward_agent: false,
    auth_methods: %w(publickey password)
    # password: 'please use keys'
  }

Stage 示例

set :stage, :test
set :branch, 'develop'

server '192.168.1.1', user: 'ares', roles: %w{web app db}

set :deploy_to, "/home/#{fetch(:deploy_user)}/apps/appname"

# dont try and infer something as important as environment from
# stage name.
set :rails_env, :test

# number of unicorn workers, this will be reflected in
# the unicorn.rb and the monit configs
set :unicorn_worker_count, 5

# whether we're using ssl or not, used for building nginx
# config file
set :enable_ssl, false

开始部署::

- 部署

$ cap production deploy --dry-run
$ cap production deploy

输入服务器用户密码后就 deploy 就进行了。不想每次都输密码,ssh-copy-id可能是你想要的。

- 服务器生成目录详解

运行多次 deploy 之后会生成这样的目录结构:

.
├── current -> /home/ares/apps/appname/releases/20140325071623
├── releases
│   ├── 20140325065734
│   ├── 20140325071310
│   ├── 20140325071623
│   └── 20140325074922
├── repo
│   ├── branches
│   ├── config
│   ├── description
│   ├── FETCH_HEAD
│   ├── HEAD
│   ├── hooks
│   ├── info
│   ├── objects
│   ├── packed-refs
│   └── refs
├── revisions.log
└── shared
    ├── bin
    ├── bundle
    ├── config
    ├── log
    ├── public
    ├── tmp
    └── vendor
  • release 每次发布都会产成一个目录,该目录下存放着 Rails 项目源码,多个目录是为了 rollback 而设。
  • current 是指当前版本,软链接到 release 下的某个版本目录。
  • repo 存的是项目的.git 目录
  • shared 是项目中共享的内容,如 config 文件,不随每次发布而改动。

- Rollback

$ cap production deploy:rollback

Rollback 其实就是把 current 目录指向到 releases 里上次发布的目录。

关键词解析::

- Cap Flow

系统默认包含下面的这些 Task,Task 是有顺序的,在每个 Task 之前和之后可以通过before, after添加自定义的 Task。更多 Flow 介绍,见这里

  1. deploy:starting
  2. deploy:started
  3. deploy:reverting - revert server(s) to previous release
  4. deploy:reverted - reverted hook
  5. deploy:publishing
  6. deploy:published
  7. deploy:finishing_rollback - finish the rollback, clean up everything
  8. deploy:finished

在这些 Flow 之外也有一些常用的 Task:

  • ##### deploy:check 对应 Cap 2.x 中的 deploy:setup。

check 不属于 deploy flow,它的目的主要是在服务器上创建所需要的目录(主要是 shared, release),然后就是对应的 linked_dir & linked_files 操作。

  • deploy:cold in 2.x

    新的系统中,核心的 deploy task 是幂等 (idempotent) 的,所以像cap deploy:cold这样的预执行命令(创建目录结构)就不再被需要了。

  • dry run

    dry-run 主要是保证每一个命令都能被执行到,但不会在服务器上产生任何改动。

$ cap production deploy —dry-run

- Stage

Cap 中有和 Rails 一样的运行环境(Environment)的概念 (Cap 中叫作Stage,默认建了 2 个 Stage,staging, production),就是不同环境下对应不同的服务器。

deploy.rb中定义着公用的变量,对应到不同的 Stage,可以定义一些专有的变量,同时也可以覆写 deploy.rb 中的公用变量。

注:Stage 名字默认对应 Rails 的 environment 名字(development, test, production)。可通过设置进行映射。

set :rails_env, :test

- linked_files & linked_dirs

Capistrano 使用Shared目录来管理那些在不同Release中共用的文件,最主要的一个shared/config中包含每个发布所需要的配置文件

  • ##### linked_dirs 是将项目的指定目录链接到 shared 目录中。这个操作会在从 repo 取下代码之后进行。
  • ##### linked_files 和 linked_dirs 相反,它是将 shared 中的文件链接到项目中,文件要首先存在于 shared 目录中,不然 deploy 时会报错。 Rails 项目中,主要就是database.ymlsecret.yml这样的敏感文件。对于这些文件最好的做法就是从 Git 中过滤掉,然后每个开发者和服务器都单独配置。

- MySQL

在尝试使用 SQLite3 进行 Cap 测试通过后,将 DB 换成 MySQL,然后又出错了。错误是数据库不存在,一直以为是在创建数据库时未指定RAILS_EVN,后来发现 migrations 在执行前也没有运行db:create来创建数据库。

在运行 deploy 前,自己手工创建数据库,然后 deploy 就能正常运行了。猜想:create 属于一次性操作,所以没有将其作为 task 放入 deploy 中。

- Ask

在 deploy.rb 和 stage 文件中,我们可以设置变量的值。但有时,值会在运行时才被确定,这样就可以通过 ask 来设定。

ask :branch, ‘my_default_branch’

这样在 branch 变量第一次被使用时,会有输入提示,就可以动态设置该值了。

- Role

角色的目的是让 Task 可以运行在不同的机器上。更多

一个线上程序中有着多种服务器,像 DB Server, APP server, Web Server,在不同的 Server 上要运行不同的部署方案。Cap 也把这些也考虑到了,可以通过创建不同的 Role 来对这些服务器进行归类,为 Task 指定 Role 来运行。

- Tasks

如何自定义 taskdoc

- Cold Start

可以在发布进行之前做一些准备工作,比如,创建 linked_files、Unicorn、Nginx 的配置及创建 Service 的工作。

现行的作法是,定制一个 task,在 Deploy 之前进行,在服务器创建目录,然后从本地将文件预先upload!) 到服务器的相应目录(主要是shared/config)。

- sudo

Capistrano 3 推荐使用 passwordless sudo。这样非 root 用户也可以直接使用 sudo 命令,而不必通过 PTY 来输入密码。Guide:

其实就是在系统里,给指定用户赋上某些指令的 sudo 权限。在 Ubuntu 下,修改 /etc/sudoers来添加要使用的命令。

ares ALL=NOPASSWD:/usr/sbin/service, /bin/ln
#也可以将所有程序都设置为不要密码,不过太不安全,不建议
#ares ALL=(ALL) NOPASSWD: ALL

- PTYs

PTY 就是让用户在当前 Terminal 中执行任务时,可以进行交互。比如说执行 sudo 任务时,可以远程输入密码。 更多

ps:试着启用 pts,但输入密码后无反应。

- ssh-copy-id

每次运行 cap 都要输入密码,可以将本地的 ssh 公钥存到 server 上,就可以省下很多时间。

ssh-copy-id 就是这么一个将本机的公钥复制到远程机器的 authorized_keys 文件的工具,其也能让你拥有远程机器的 home, ~./ssh , 和 ~/.ssh/authorized_keys 的权利。

首先本地机器上要创建 ssh key

$ ssh-keygen
$ ssh-copy-id [email protected]

Mac OSX 上 ssh-copy-id 不是默认安装的,可通过 Homebrew 进行安装

$ brew install ssh-copy-id

必须加精呀!

#1 楼 @huacnlee 不需加精你给人加什么精

@seamon 这种东西没什么技术含量的,就是他们官方文档代码几乎没用,读代码花点时间。

#3 楼 @lanvige 没什么技术含量的东西也是好东西,对提高生产力帮助很大。希望通过你这篇文章有越来越多的人了解自动部署。我知道很多公司都是自己写脚本或者手动发布而不用自动部署,问他为什么,说配置自动部署好麻烦。。。。。。

顶一个,求 mina 篇:)

@imlcl Cap 3 其实真的蛮清晰的,而且很多理念也蛮好的,Mina 没用过,工具吗,不用太纠结,都差不哪去,关健是能 Auto Deploy。:)

#8 楼 @lanvige 因为喜欢 mina 多一点,更加直接

终于明白了。原来只知道老大配好了直接使用。

虽然一直用 Capistrano 3,但是没有这么详细的总结过!必须顶!

顶一个。。

@imlcl Mina 好久没更新了,所以现在还是 Cap 3 靠谱点。打算过段时间换到 Cap 3 的。

不爽的一点就是,在 current 目录,它把版本管理给移除掉了。

绝对的好东西!!!

@hz_qiuyuanxin repo 目录是你要找的地方,cap 把每次 release 都放到一个单独的目录下,方便 rollback,rollback 就是把 current 指向到一个旧的 release 目录。

V3 部署到多台服务器的配置文件怎么写的 和 V2 一样?

@limkurn 定义 Role 啊,它会自动在定义好的机器上执行,无论多少台。

终于等到了。。。

#19 楼 @lanvige 先前公司有人部署没成功 后面把 sever 那一句设置了多句

#13 楼 @darkbaby123 快 1 年了,我认为可以考虑像 cancancan 那样搞一个新的 mmina 之类的。

我是 hr,给 global 的外资公司和国内 top 互联网公司招聘 ruby 方向的人才,level 不限,越牛越好,呵呵,location 北京上海都可以,我的邮箱是 [email protected],欢迎投递简历和咨询!

赞! @martin91 这就是你一直想写的啊,被抢了吧 :)

#24 楼 @allenfantasy 同赞!说抢就不对了吧:D。当时过后觉得就是看文档的事,就没动力写(自捅一刀)。但是不管内容本身有没有意义,写了就还是要佩服的!

"猜想:create 属于一次性操作,所以没有将其作为 task 放入 deploy 中。"

这个问题我之前也有困惑过,Cap 团队回答过,是因为安全问题,它们不想将这个操作放在 Cap 的任务中。

第一次部署不会自动建 current 目录很蛋疼

#13 楼 @darkbaby123 起码现在用着还算靠谱,如果再出现大更新就换了。

@MaFai 我试着把 db:create 放到了 deploy flow 中了,before db:migrate,跑了十来天了,没出现问题,倒是发现另一个 bug,等待他们确认中。

这几天一直想找自动部署的教程,感谢楼主的热情分享

我现在蛮喜欢 Docker。Discourse 的 Docker 部署,自动搞定了配置、Redis、pg,非常轻松,更新放在一个/admin/docker 中,也还方便。

cap3 把-S 给取消了,这个改动太蛋疼了...

最近项目刚好也用到这个,但是一直想知其所以然,今天看到这篇文章眼前一亮,留名收走

匿名 #37 2014年04月26日

👍

ssh-copy-id 阿,一直 scp 了 n 年

把代码私有托管到 bitbucket, 在服务器上拉取代码不就可以了。。或者直接写好 ssh,或者其他的脚本。为什么要还要这么重的东西。。

赞!支持给 cap 正名哈哈

#35 楼 @suupic 什么-S,是命令行参数吗。

#39 楼 @moliliang 看见了吧,这就是我所说的嫌麻烦不用自动部署而自己写脚本的生动例子。。。。。。

#42 楼 @seamon 真心的, ……一个拉取,一个重启,一个回滚。加一起就 3 行代码。。。

#44 楼 @moliliang 这么简单啊。不用多台服务器同时部署吗,不用 bundle install 吗,不用 bundle update 吗,不用建立某些敏感文件的符号链接吗,不用建立网络共享文件的链接吗,不用 migrate 数据库吗,不用 assets:precompile 吗,不用重启异步任务和定时任务吗,不用更新 mongodb 的索引吗,不用考虑缓存管理和清理吗,不用考虑给 newrelic 打发布标记吗。。。

#46 楼 @seamon 项目很大了才需要。嘿嘿。

@moliliang 有些事天生就属于计算机做的,手工做一次不烦,做十次还行,做 100 次总有一次忘了是不是少做了个 migration 什么的,而且这个时间加起来绝对超过了 auto 的学习时间,有这个时间早早配置下,做个精量的工程师。

我表示受益,我要点赞.....

一直都是用 auto deploy,最近升级到 cap3.1 时 ask 不能获得输入的字符,在 stackoverflow 上有类似问题的解答,可惜对本机环境不适用。

不错!冒个泡。

今天就参照这个教程完成了工作,尤其是最后的 ssh-copy-id,非常有效。 感谢!

写的真不错,受益良多。

hi,问一下楼主,这个 cap 3 执行 sudo 命令时,提示输入密码,然后就卡住不动了,这个找到解决方法咩?

Is the topic still work??

给个晚来的赞先

cap -vT来查看当前项目的可执行任务列表。 应该是 cap -Tv

cap -vT返回的是版本信息

$ capistrano % cap -vT
$ Capistrano Version: 3.4.0 (Rake Version: 10.4.2)

cap -Tv返回任务列表

capistrano % cap -Tv
cap deploy:log_revision          # Log details of the deploy
cap deploy:revert_release        # Revert to previous release timestamp
cap deploy:reverted              # Reverted
cap deploy:reverting             # Revert server(s) to previous release
cap deploy:set_current_revision  # Place a REVISION file with the current revision SHA in the current release path

xdoc [该话题已被删除] 提及了此话题。 03月06日 16:15
xdoc 长文慎入:将 Rails 程序部署到 Docker 容器中 提及了此话题。 03月06日 16:25
64 楼 已删除
dccmmtop Passenger + Nginx + Capistrano 部署 提及了此话题。 07月20日 10:40
需要 登录 后方可回复, 如果你还没有账号请 注册新账号