之前的一篇文章是为了解决问题而写的,很多东西都没有介绍清楚,这一篇文章就是完整介绍一下 Capistrano,主要的参考来源是 Modern PHP 这本书。

Capistrano 是用于自动部署应用的软件,能让部署变得简单、可预知结果和可逆。Capistrano 运行在本地设备中,通过 SSH 与远程服务器通信。Capistrano 本来是为了部署 Ruby 应用而开发的,不过对任何编程语言开发的应用都有用,包括 PHP。

Capistrano 的工作方式

Capistrano 安装在本地设备中。部署 PHP 应用时,Capistrano 会在本地设备中执行 SSH 命令,与远程服务器通信。Capistrano 会在远程服务器中保存之前部署的应用,而且每次部署的版本放在各自的目录中。Capistrano 会维护五个或更多之前部署的应用,以防需要回滚到早期版本。Capistrano 还会创建一个 current/ 目录,通过符号链接指向当前部署的应用所在的目录。在生产服务器中,Capistrano 管理的目录结构可能像下图这样:

home/
    deploy/
        apps/
            my_app/
                current/
                releases/
                    release1/
                    release2/
                    release3/
                    release4/

把新版应用部署到生产环境时,Capistrano 首先从应用的 Git 仓库获取最新版代码,然后把应用的代码放到 releases/ 目录中的一个新子目录中,最后把 current/ 目录的符号链接指向这个新目录。让 Capistrano 回滚到之前的版本时,Capistrno 会把 current/ 目录的符号链接指向 releases/ 目录中存档之前版本的目录。Capistrano 是一种优雅且简单的部署方案,能让 PHP 应用的部署过程变得简单、可预知和可逆。

安装

Capistrano 应该安装在本地设备中,别在远程服务器中安装。安装时还需要 ruby 和 gem。OS X 系统已经有了。Ubuntu 用户可以直接用下面的命令安装:

$ sudo apt-get install ruby
$ sudo apt-get install gem

安装好之后,执行下面命令安装 Capistrano:

$ gem install capistrano

配置

安装 Capistrano 之后,为了使用 Capistrano,必须初始化项目。打开终端,进入项目的根目录,执行下面的命令:

$ cap install

这个命令会创建 Capfile 文件,config/ 目录和 lib/ 目录。现在,项目目录结构应该如下所示:

Capfile
config/
    deploy/
        production.rb
        staging.rb
    deploy.rb
lib/
    capistrano/
        tasks/

Capfile 是 Capistrano 的中央配置文件,会聚合 config/ 目录中的配置文件。 config/ 目录中存放的是各个远程服务器环境(例如,测试环境、过渡环境或生产环境)的配置文件。

默认情况下,Capistrano 假设你为应用搭建了多个环境。例如,可能有单独的过渡环境和生产环境。Capistrano 在 config/deploy/ 目录中为每个环境都提供了单独的配置文件。Capistrano 还提供了 config/deploy.rb 配置文件,这个文件用于保存所有环境通用的设置。

在每个环境中,Capistrano 会区分服务器的角色。例如,生产环境可能有前置 Web 服务器(web角色),应用服务器(app角色)和数据库服务器(db角色)。只有最大规模的应用才有必要使用这种架构,小型 PHP 应用一般在同一台设备中运行 Web 服务器(nginx)、应用服务器(PHP-FPM)和数据库服务器(MariaDB)。

这篇文章的例子中,我使用的是 web 角色,和生产环境的配置。

config/deploy.rb 文件

这个配置文件包含所有环境通用的配置。打开这个文件,然后更新下列设置:
+ :application 这是 PHP 应用的名称。只能包含字母、数字和下划线。
+ :repo_url 这是Git仓库的URL。这个URL必须指向一个Git仓库,而且远程服务器必须能访问这个仓库。
+ :reploy_to 这是远程服务器中一个目录的绝对路径,我们不熟的PHP应用就存放在这个目录中,此时,该路径为:/home/deploy/apps/my_app
+ :keep_releases 保留多少个旧版,以便于应用的版本回滚。

config/deploy/production.rb 文件

这个文件只包含生产环境的设置。这个文件用于定义生产环境的角色,列出属于各个角色的服务器。我们只使用 web 角色,而且只有一个服务器属于这个角色。所以,这是,把该文件全部内容替换为下列代码即可:

role :web, %w(deploy@123.123.123.123)

认证

使用 Capistrano 部署应用之前,我们必须在本地电脑和远程服务器之间,以及远程服务器和 Git 仓库之间建立认证。在之前的文章中 [[Blog:设置 SSH Key 登录服务器和 Git 服务器]] 已经讨论了如何使用 SSH 密钥登录服务器和 Git 服务,使用之前讨论的方法在每台远程服务器中生成 SSH 公钥和私钥。Git 仓库应该能访问每台远程服务器的公钥。Github 允许在用户账户中添加多个 SSH 公钥。总之,我们必须不使用密码就能把 Git 仓库克隆到远程服务器。

准备远程服务器

就快能部署环境了,但在此之前,还需要准备远程服务器。我们要通过 SSH 登录远程服务器,创建一个目录,存放部署的 PHP 应用。deploy 用户必须有这个目录的读写权限。我喜欢在 deploy 用户的主目录中创建这个目录:

/home
    /deploy
        apps/
            my_app/

虚拟主机

Capistrano 会创建符号链接,把 current/ 目录指向存放当前应用版本的目录。因此,我们要更新 Web 服务器的虚拟主机文档根目录,指向 Capistrano 的 current/ 目录。根据上述文件系统的结构图,要把虚拟主机的文档根目录改为 /home/deploy.apps.my_app/current/public/。这么设置的前提是,假设 PHP 应用中有个 public/ 目录,把它当做文档根目录。然后重启 Web 服务器,加载修改后的虚拟主机配置。

依赖的软件

远程服务器不需要 Capistrano,但是需要 Git。而且还需要运行 PHP 应用所需要的全部软件。

Capistrano 的钩子

Capistrano 允许在部署应用过程中的特定时刻执行我们指定的命令。很多 PHP 开发者都使用 Composer 管理应用的依赖。每次使用 Capistrano 部署应用时,我们可以使用 Capistrano 的钩子安装 Composer 依赖,打开 config/deploy.rb 文件,添加下面的 ruby 代码:

namespace :deploy do
    desc "update composer package"
    after :updated, :build do
      on roles(:web) do
          within release_path do
              execute :composer, "install --no-dev --quiet"
              execute :cp, "-rfv  ../../shared/configs ./application/"
          end
      end
    end
  end

一些不便于放在 Git 中的配置文件此时可以通过在钩子中执行 ln -s source dest 命令,建立符号链接,引用外部的配置文件。

部署应用

一切准备就绪以后,切换到本地项目根目录,执行下列命令部署你的应用!

$ cap production deploy

回滚应用

要回滚到上个版本,只需执行下列命令:

$ cap production deploy:rollback

FAQs

fatal: Not a valid object name: ‘dev’

原因:set branch ‘dev’,中 dev 分支并不存在。

rm stderr: rm: cannot remove ‘/home/deploy/apps//releases/20180930034435/storage/framework/cache/data/7e/e2/7ee26eb04819797da018521ae355096eb5af5eb1’: Permission denied

原因:这是一个 Laravel 项目,storage 目录下面是 Laravel 的运行过程中产生的日志文件和缓存文件,权限属于 www-data,而当前操作 Capistrano 发布新版的用户是 deploy,发布新版时,就要删除多余的旧版项目,而在删除时遇到这种没有权限删除的文件和目录,就会报这个错误。
解决:将 storage 链接到项目外的一个固定目录,比如 shared/storage,然后,给予 deploy chmod 的权限,每次在发布前,设置一个自动执行的操作:chmod 777 storage/。即可。
参考: https://stackoverflow.com/questions/35724285/capistrano-remove-cache-permission-denied