Capistrano 部署

这几天要部署自己做的一个小板凳,是用 sinatra 写的一个简单的 blog 程序。这个程序很简单,代码非常少,可以像 php 那样直接使用 async 将代码。但我想熟悉下 Ruby 关于部署方面的技术,因此找到了 Capistrano 这个东西。Rails 的 web 后台对环境非常挑剔,Capistrano 是 Ruby 写的, 虽然它可以部署各种语言的代码,但可以部署 Rails 的程序不会太简单。

配置项含义

Capistrano 这个东西产生的一大堆配置文件有点让人迷糊。Capfile 相当于一个 makefile 文件,里面设置了一些任务是如何运行的。config目录下有三个配置文件,其中 deploy 子目录是用于配置部署不同的环境。默认的是 production 和 staging 环境配置项分别对应两个同名的文件。deploy.rb 这个文件相当于一个总配置文件,里面的是一些公共的配置项。
Capistrano 非常依赖版本控制,在 deploy.rb 文件中 repo_url 是用于设置代码仓库的地址的,配置好这个参数后需要确认服务器有访问代码仓库的权限。 scm 是设置具体的版本控制软件的。Capistrano 支持最好的是 git 和 svn。 deploy_to 是一个非常重要的参数,它指示了代码上传到服务器的具体位置,另外需要确保这个目录与配置好的用户名有足够的访问权限。这几个参数设置好后,其他的参数可以使用默认的了。然后再配置对应环境的具体配置项。 在环境配置项中,role 参数指示的三项分别是 app,web 和 db。这个三项分开的好处是可以让他们部署到不同的服务器上做负载均衡。单台服务器不用太关心,三项指向同一个地址即可。然后配置服务器的访问权限,配置非常简单,设置好用户名和 ssh public key就可以了。到这里基本的配置已经完成了,运行cap deploy:check 可以检测配置是否成功。这个时候如果 check 成功的话使用 cap deploy 命令就可以成功上传代码了。

让代码运行

代码上传后部署的任务已经完成了一小部分了,接下来就是具体的程序的启动与 http request的转发了。我使用的是 nginx + unicorn 组合来干这件事情的。首先要确保代码在本地能正常的运行起来,然后再去服务器上做进一步的测试。实际上更好的方式是所有的软件在本地的机器上能完全正常运行后,再将同样的配置文件上传至服务器。能避免不少因为不同机器,不同环境而引起的问题。这样服务器只负责运行软件,不做任何测试。这也是我最近所理解的生产环境。 首先来配置 unicorn 这个东西,一般的 web framework 都会自带 http 服务器用来做测试。自带的 http 服务器是不推荐直接跑在生产环境下的。nginx 更适合干这种事情,但 nginx 干的事情更加单一,它只负责将请求接收并转发。并不负责应用层的事情。unicorn就是这个应用层(php对应的是fastcgi),他负责将代码运行起来并与nginx进行通信。unicorn配置起来也非常的简单,在config.ru中配置好程序的入口点和unicorn.rb中配置好程序的pid,sock 和 log 文件路径。其中 pid 文件是用于启动与停止后台运行的 unicorn。sock 文件用于和 nginx 进行通信。 现在可以配置 nginx 了,nginx 的配置非常简单。只要设置好 nginx 的 upstream 项,在此项里面设置好 unicorn sock 的具体路径,就可以顺利通过 nginx 访问到unicorn了。

Capistrano 的 task

nginx 与 unicorn 部署好后,就可以借助 Capistrano 的 task 来进行一些必要的代码部署步奏。比如重启 http 服务器,更新数据库等一系列事情。这些事情使用 Capistrano 来完成的好处是,task写好好每次部署只需要一条命令就可以自动化完成一些繁琐的事情。这个才是 Capistrano 最核心要解决的问题。 这里拿 unicorn 的重启为例来展示如何在远端服务器执行一些 shell 命令。unicorn 网上有一个控制它停止与重新启动的 shell 脚本。下面是一个 task 的具体代码。

%w[start stop restart].each do |command|
  desc "#{command} unicorn server"
  task command do
    on roles(:app, :web) do
      execute "sh #{fetch(:current_path)}/config/unicorn_init.sh #{command}"
    end
  end
end
before :deploy, "unicorn:stop"
after  :deploy, "unicorn:start"

其中 on roles 指定的是要执行命令具体的服务器,exexcute 是执行具体命令的函数。其中(:current_path)指定的是当前release 代码的路径。因为每次使用 cap deploy 命令后,都会产生一个新的 release,然后远端会切换版本到新的 release 目录。before 和 after 是 hook,它指示了每次某一项任务开始前和完成后所做的一些工作。

Capistrano 2 与 Capistrano 3

Capistrano 2 与 Capistrano 3 差别非常大,一些配置项完全是需要重写的。现在从网上的资料和 github 的 issue 来看 2 比 3 要多很多。可参考的教程2比3也要多出很多,我在使用的是Capistrano 3,在部署时遇到不少的问题。因此如果没有太高的要求,建议继续使用2。以下是我在部署的过程中碰到的一些差异。

Capistrano 2 可以使用 sudo 执行命令,Capistrano 3 却不行。据他们自己说是为了安全考量。 Capistrano 3 的 task 必须要指定具体的 roles。 Capistrano 2 可以直接调用 set 设置的属性,而 Capistrano 必须使用 fetch 函数去获取。 Capistrano 3 去掉了 current_path, shared_path 等一些公有变量,据说是为了防止部署失败而设计的。