使用GitLab CI + Capistrano部署CakePHP应用程序
摘要:本文描述了如使用GitLab CI + Capistrano部署CakePHP应用程序。
目录
1. 问题
2. 解决方法
3. 安装GitLab CI Runner
4. 安装和配置Capistrano
4.1 安装Ruby
4.2 安装Capistrano
4.3 在项目中配置Capistrano
4.4 用户的验证和授权
4.5 其它工具和设置
5. 部署
6. 小结
A. 参考资料
1. 问题
作为软件开发人员,除了写代码,还有很多事情需要我们操心。比如,开发的应用程序总是要部署到某个环境中,在开发的过程中要经常部署到开发(DEV)环境,便于整个团队对代码进行集成和测试,以后又要部署到UAT/Staging环境,通过最后的质量验证步骤,才可以部署到生产(Production)环境。在一个敏捷开发过程中,持续集成通常要求自动化构建和部署,减少人为的介入,同时又要针对不同的环境采用不同的设置,这个过程会面临各种细致的问题,比如,如何保证安全性和保护一些秘密(用户名和密码等),如何协调部署中变化的部分(比如程序代码)和不变的部分(比如用户输入的数据),需要采取什么样的工具,这就是本文要讨论的。
2. 解决方法
在之前的文章中我们介绍过Travis CI,这是一个轻量的持续集成工具。在本文中,我们要介绍另一个持续集成工具GitLab CI,之所以采用它,因为这是和我采用的源码控制GitLab密切集成的。它能够随时监视代码的提交,针对每次提交进行构建和部署。
另外一个我们要介绍的工具Capistrano,是用Ruby开发的远程服务器自动化和部署工具,可以部署Ruby on Rails应用,也可以部署用其它各种语言开发的应用。从这个工具,我们可以看到,在一个自由的环境中,随着需要,可以激发出各种创新的欲望,催生出适用的工具。这个工具对我来说是更为陌生,又可以说是相当复杂,主要是缺乏很好的向导类文章,所以我看了很多介绍文章(参考资料[5]-[11]),才能有个大致的概念。需要注意的是,2013年6月1日发布了Capistrano version 3,这和之前的Capistrano version 2有很大的变化,概念和流程类似,但是写法很不同,因为 Capistrano 3抛弃了Capistrano 2自己的DSL,而采用了Rake (Ruby Make)的DSL。所以在读介绍文章的时候,要特别注意,比如参考资料[5]其实用的就是Capistrano 2。至今Capistrano 3已经发布将近2年了,应该相当成熟了,适用也比较广泛了,而且现在也有很多插件,可以进一步扩展功能。在本文中我们会采用Capistrano 3。
对于文章开始提出的问题,我们要做的,就是用GitLab CI在每次提交代码的时候,启动任务,用Capistrano 3进行应用程序的构建和部署。下面我们就来详细介绍进行配置的各个步骤。
3. 安装GitLab CI Runner
首先要在GitLab创建账号,创建代码库,上传项目代码。然后用相同的账号登录GitLab CI,针对某个代码库启用持续集成。这些步骤因为是写这篇文章之前做的,没有记录和截图,所以无法详述具体的操作步骤了,但应该不是难事。
在GitLab CI中,进入代码库相应的Runners页面,如下图所示:
me@localhost $ cd my-projectme@localhost $ cap install
这会创建如下的目录和文件:
├── Capfile├── config│ ├── deploy│ │ ├── production.rb│ │ └── staging.rb│ └── deploy.rb└── lib └── capistrano └── tasks
Capfile是Capistrano的基本配置,引入了各项任务,包括lib/capistrano/tasks
目录下的任务:
# Load DSL and Setup Up Stagesrequire 'capistrano/setup'# Includes default deployment tasksrequire 'capistrano/deploy'
require 'capistrano/composer'
require 'capistrano/cakephp'# require 'capistrano/cakephp/assets'# require 'capistrano/cakephp/migrations'# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
deploy.rb是共用的设置,比如应用程序的名称,源码库的地址,分支,以及要部署的目录等:
set :application, 'my-project'set :repo_url, 'git@example.com:my_account/my_project.git'set :branch, 'master'
set :deploy_to, '/var/www/my-project'
# set :scm, :git
# set :format, :pretty
# set :log_level, :debug
# set :pty, true
# set :linked_files, %w{config/database.yml}
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
set :linked_dirs, fetch(:linked_dirs) + %w{tmp/logs webroot/files}
namespace :composer do
desc 'Composer update'
task :change_dir do
on roles(:web) do
execute "cd #{release_path}/ && composer update"
end
end
before 'install', 'change_dir'
end
其中,
:deploy_to为要部署的目录,缺省为/var/www/#{fetch(:application)},即/var/www目录下以:application设置的应用程序名称的子目录
;
:scm 是源码控制的工具,Capistrano 本身支持 Git/Mercurial/Subversion。
:linked_dirs是要链接(symlink)到部署的目录下的目录,比如日志目录、上传文件的目录等等,相应的,:linked_dirles是要链接的文件。
更多关于变量的说明可以参考 Configuration文档。
最后的一段代码 composer:change_dir 任务,是为了应对 composer:install 这项任务的一个问题,详情可见参考资料[20]。
而 production.rb (和 staging.rb)是针对具体某个环境的设置:
set :stage, :production# Simple Role Syntax# ==================# Supports bulk-adding hosts to roles, the primary# server in each group is considered to be the first# unless any hosts have the primary property set.# role :app, %w{example.com}# role :web, %w{example.com}# role :db, %w{example.com}# Extended Server Syntax# ======================# This can be used to drop a more detailed server# definition into the server list. The second argument# is something that quacks like a hash and can be used# to set extended properties on the server.server 'example.com', roles: %w{web app}, my_property: :my_value
这里,首先是声明是针对生产环境(:production)的。然后有两种方法设置服务器,前一种是简单的方法(注释掉的三行),后一种是扩展的服务器设置(最后一行),可以设置地址,角色以及其它属性,采用其中一种即可。
4.4 用户的验证和授权
这一节的内容对通常的开发人员最为陌生,也是本文中最复杂的部分,要特别注意。
这里需要在最终要部署的服务器上创建适当的用户,并给予该用户足够的权限,可以参考Capistrano文档中的Authentication & Authorisation。记得,要在每台要进行部署的服务器上都要做下面的步骤。
首先登录服务器,创建进行部署操作的用户deployer:
root@my-server.com $ sudo adduser deployer
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Adding user `deployer' ...
Adding new group `deployer' (1001) ...
Adding new user `deployer' (1001) with group `deployer' ...
Creating home directory `/home/deployer' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: ********
Retype new UNIX password: ********
passwd: password updated successfully
Changing the user information for deployer
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
root@my-server.com $ sudo passwd -l deployer
passwd: password expiry information changed.
第一个命令创建一个标准的用户,拥有用户目录(home directory),可以使用shell,因此可以登录服务器。第二个命令锁住了用户的密码,这样就使得用户无法输入密码,从而无法用密码进行登录,要登录可以使用秘钥(SSH key)。
下面要为用户验证(Authentication)设置秘钥,这主要在两个环节需要用到,一个是要从开发人员的机器(或者GitLab CI Runner所在的机器)登录到要部署的服务器上进行部署,二是从部署的服务器登录到源代码控制服务器上获取最新的项目源代码。
4.4.1 从开发人员的机器到要部署的服务器的用户验证
首先需要创建自己使用的秘钥。如果在执行SSH相关的命令时遇到“Could not open a connection to your authentication agent.”这样的错误提示,请按照参考资料[16][17]进行设置。在本地的开发机器上执行如下命令:
me@localhost $ cd ~/.ssh
me@localhost $ ssh-keygen -t rsa -f me@my_email_address.com -C 'me@my_email_address.com'
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in me@my_email_address.com.
Your public key has been saved in me@my_email_address.com.pub.
The key fingerprint is:
60:e4:f3:f3:76:99:ce:66:d7:8a:4a:db:ec:c7:de:11 me@my_email_address.com
The key's randomart image is:
+--[ RSA 2048]----+
| . |
| o |
| = |
| . + |
| S E |
| o o .|
| + +. o |
| o Bo.+.o|
| o=B+o..|
+-----------------+
上面的 ssh-keygen 命令会创建一个秘钥,-t 选项指定秘钥的类型为RSA,-f 选项指定生成的文件名称,-C 选项指定注释。这样就会生成2个文件:
me@localhost $ ls -l me*-rw------- 1 me staff 1766 May 3 15:54 me@my_email_address.com-rw-r--r-- 1 me staff 405 May 3 15:54 me@my_email_address.com.pub
其中,me@my_email_address.com 这个文件是私钥,而 me@my_email_address.com.pub 这个文件为公钥。如果生成的过程中你输入了密码(passphrase),则私钥有密码保护。
如果你已经有了自己的秘钥,但是私钥没有密码的保护,要加上或者改变密码,可以用如下命令:
me@localhost $ ssh-keygen -f me\@my_email_address.com -pEnter old passphrase:Key has comment 'me@my_email_address.com'Enter new passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved with the new passphrase.
把生成的秘钥加入秘钥列表中:
me@localhost $ ssh-add me\@my_email_address.com
Identity added: me@my_email_address.com (me@my_email_address.com)
下面要把生成的公钥放入要部署的服务器上相应的用户(即上面创建的deployer)的 ~/.ssh/authorized_keys 文件中。在Linux上通常会有 ssh-copy-id 这个命令,就是用来做这件事的:
me@localhost $ ssh-copy-id -i me@my_email_address.com deployer@my-server.com
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'deployer@my-server.com'"
and check to make sure that only the key(s) you wanted were added.
如果你的系统上没有 ssh-copy-id 这个命令,
那么可以这么做:
me@localhost $ ssh-add -L # 列出公钥ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0nGH9BA7EoAIRsCb+xwZkp20muUJdl/EtLffp5E0yOeEDRvlP9d9tpLZKKPSGG6SZo4CQNziv8kc7Bpogozafz2BG8YEk2mwdYm5jcGgKhH3YtVX7AGGga8mwFRgIed/jx4bxUEBP1uxANsL7ZOWrBOUj0YjcFelFU5aJYn822s3e+OXAPk4y02iZneHflf6lMoMP5mMIOReGmemxYzjNSy39JD0l8HULgh/4AlQzz0vDHKjidaOy21zf1YcV/FkOERMxozEXaYIjN0ff5gpSG8GN0gGKcNCy7hYil02bJBePc/AdBWxUSHJl6mpxGtoEn/nI7z6h/3BEr8E9KAGN me@my_email_address.comme@localhost # ssh root@my-server.comroot@my-server.com # su - deployerdeployer@my-server.com $ cd ~deployer@my-server.com $ mkdir .sshdeployer@my-server.com $ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0nGH9BA7EoAIRsCb+xwZkp20muUJdl/EtLffp5E0yOeEDRvlP9d9tpLZKKPSGG6SZo4CQNziv8kc7Bpogozafz2BG8YEk2mwdYm5jcGgKhH3YtVX7AGGga8mwFRgIed/jx4bxUEBP1uxANsL7ZOWrBOUj0YjcFelFU5aJYn822s3e+OXAPk4y02iZneHflf6lMoMP5mMIOReGmemxYzjNSy39JD0l8HULgh/4AlQzz0vDHKjidaOy21zf1YcV/FkOERMxozEXaYIjN0ff5gpSG8GN0gGKcNCy7hYil02bJBePc/AdBWxUSHJl6mpxGtoEn/nI7z6h/3BEr8E9KAGN me@my_email_address.com" >> .ssh/authorized_keysdeployer@my-server.com $ chmod 700 .sshdeployer@my-server.com $ chmod 600 .ssh/authorized_keys
用下面的命令验证上面所做的设置是正确的:
me@localhost $ ssh deployer@my-server.com 'hostname; uptime'my-server.com19:23:32 up 62 days, 44 min, 1 user, load average: 0.00, 0.01, 0.05
这应该不需要你输入密码,就能完成。这需要在每台服务器上(通过改变@my-server.com为其它服务器的主机名或IP地址)证实。
记得把私钥放在安全的地方。如果你负责管理整个团队的部署,那么可能需要收集每个可以做部署的开发人员的公钥,然后进行上面的步骤。
4.4.2 从部署的服务器到源代码控制服务器的用户验证
这里有三种方法,分别为 SSH Agent Forwarding,HTTP Authentication 和 Deploy Keys,详情可以参考Capistrano的Authentication & Authorisation文档中1.2一节。
我采用的是Deploy Keys,这就是要生成一个不同的秘钥,只用于部署,这在Github等源码控制托管商都有相应的管理方法。
先用上面介绍的命令 ssh-keygen 生成用于部署的秘钥 deployer@my_email.address.com。
然后在我使用的源码控制托管商 GitLab 上该项目的设置页面中,进入Deploy Keys页面(https://gitlab.com/my_username/my_project/deploy_keys),添加新的Deploy Key,拷贝公钥deployer@my_email.address.com.pub 的内容到这里,如下图所示:
4.4.4 验证操作权限
我们来验证在这一节中上面所做的用户验证和授权的设置都是正确的,请参考Capistrano的Cold Start文档。
先检查我们在服务器上是否有足够的权限,这也是我们手工写的第一个Capistrano任务。相对于项目根目录,创建文件./lib/capistrano/tasks
/access_check.
cap:
desc "Check that we can access everything"task :check_write_permissions do on roles(:all) do |host| if test("[ -w #{fetch(:deploy_to)} ]") info "#{fetch(:deploy_to)} is writable on #{host}" else error "#{fetch(:deploy_to)} is not writable on #{host}" end endend
这样添加的新任务应该可以用下面的命令看到:
me@localhost $ cap -Tcap cakephp:cake[command_name] # Executes a cake command
cap check_write_permissions # Check that we can access everything# ... 其它 Capistrano 任务 ...
运行该任务,检查我们能够在服务器上进行所需的操作:
me@localhost $ cap production check_write_permissionsDEBUG [96f5c014] Running /usr/bin/env [ -w /var/www/my-project ] as deployer@my-server.comDEBUG [96f5c014] Command: [ -w /var/www/my-project ]DEBUG [96f5c014] Finished in 1.798 seconds with exit status 0 (successful).INFO /var/www/my-project is writable on my-server.com
再来检查是否可以从服务器访问源码控制的代码库。因为检查 Git 稍微复杂一些,而且使用代码操作 Git 并不容易,所以 Capistrano 提供了如下的任务:
$ cap production git:checkINFO [ef4028de] Running /usr/bin/env mkdir -p /tmp/my-project/ as deployer@my-server.comDEBUG [ef4028de] Command: /usr/bin/env mkdir -p /tmp/my-project/INFO [ef4028de] Finished in 0.514 seconds with exit status 0 (successful).DEBUG Uploading /tmp/my-project/git-ssh.sh 0.0%INFO Uploading /tmp/my-project/git-ssh.sh 100.0%INFO [0f73bb2d] Running /usr/bin/env chmod +x /tmp/my-project/git-ssh.sh as deployer@my-server.comDEBUG [0f73bb2d] Command: /usr/bin/env chmod +x /tmp/my-project/git-ssh.shINFO [0f73bb2d] Finished in 0.016 seconds with exit status 0 (successful).DEBUG [42deaf82] Running /usr/bin/env git ls-remote git@example.com:my_account/my_project.git as deployer@my-server.comDEBUG [42deaf82] Command: ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env git ls-remote git@example.com:my_account/my_project.git )DEBUG [42deaf82] 5a54268e8b92c5b6e1e4646dc719a10c5a1226ff HEADDEBUG [42deaf82] 5a54268e8b92c5b6e1e4646dc719a10c5a1226ff refs/heads/masterDEBUG [42deaf82] Finished in 7.391 seconds with exit status 0 (successful).
至此,一切正常,我们可以进行下一步了。
4.5 其它工具和设置
在服务器上进行部署时,还要用到一些额外的工具和设置。
首先是PHP的包管理软件Composer。按照它的Getting Started中的安装步骤:
deployer@my-server.com $ sudo curl -sS https://getcomposer.org/installer | php#!/usr/bin/env phpAll settings correct for using ComposerDownloading...Composer successfully installed to: /home/deployer/composer.pharUse it: php composer.phardeployer@my-server.com $ sudo mv composer.phar /usr/local/bin/composer
另外,CakePHP需要PHP的扩展mcrypt,请参考mcrypt的安装/配置文档中:
deployer@my-server.com $ sudo apt-get install php5-mcrypt
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libmcrypt4
Suggested packages:
libmcrypt-dev mcrypt
The following NEW packages will be installed:
libmcrypt4 php5-mcrypt
0 upgraded, 2 newly installed, 0 to remove and 152 not upgraded.
Need to get 77.3 kB of archives.
After this operation, 324 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://ap-southeast-1.ec2.archive.ubuntu.com/ubuntu/ trusty/universe libmcrypt4 amd64 2.5.8-3.1ubuntu1 [61.9 kB]
Get:2 http://ap-southeast-1.ec2.archive.ubuntu.com/ubuntu/ trusty/universe php5-mcrypt amd64 5.4.6-0ubuntu5 [15.4 kB]
Fetched 77.3 kB in 0s (3838 kB/s)
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
Selecting previously unselected package libmcrypt4.
(Reading database ... 64192 files and directories currently installed.)
Preparing to unpack .../libmcrypt4_2.5.8-3.1ubuntu1_amd64.deb ...
Unpacking libmcrypt4 (2.5.8-3.1ubuntu1) ...
Selecting previously unselected package php5-mcrypt.
Preparing to unpack .../php5-mcrypt_5.4.6-0ubuntu5_amd64.deb ...
Unpacking php5-mcrypt (5.4.6-0ubuntu5) ...
Setting up libmcrypt4 (2.5.8-3.1ubuntu1) ...
Setting up php5-mcrypt (5.4.6-0ubuntu5) ...
Processing triggers for libc-bin (2.19-0ubuntu6.3) ...
deployer@my-server.com $ sudo service apache2 restart
* Restarting web server apache2 [ OK ]
再为命令行启用mcrypt:
deployer@my-server.com $ sudo ln -s /etc/php5/mods-available/mcrypt.ini /etc/php5/cli/conf.d/20-mcrypt.ini
5. 部署
现在准备工作做的差不多了,可以开始真正的部署了,只需要一个命令cap production deploy就可以部署到生产环境了:
deployer@my-server.com $ cap production deployINFO [118bf2b0] Running /usr/bin/env mkdir -p /tmp/my-project/ as deployer@my-server.comDEBUG [118bf2b0] Command: /usr/bin/env mkdir -p /tmp/my-project/INFO [118bf2b0] Finished in 0.367 seconds with exit status 0 (successful).DEBUG Uploading /tmp/my-project/git-ssh.sh 0.0%INFO Uploading /tmp/my-project/git-ssh.sh 100.0%INFO [e72de67d] Running /usr/bin/env chmod +x /tmp/my-project/git-ssh.sh as deployer@my-server.comDEBUG [e72de67d] Command: /usr/bin/env chmod +x /tmp/my-project/git-ssh.shINFO [e72de67d] Finished in 0.016 seconds with exit status 0 (successful).INFO [3ec77d26] Running /usr/bin/env git ls-remote --heads git@gitlab.com:my_account/my_project.git as deployer@my-server.comDEBUG [3ec77d26] Command: ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env git ls-remote --heads git@gitlab.com:my_account/my_project.git )DEBUG [3ec77d26] fbc99fa4c6e11ab56be654a291b8e86b5327526f refs/heads/masterINFO [3ec77d26] Finished in 8.877 seconds with exit status 0 (successful).INFO [7b880538] Running /usr/bin/env mkdir -p /var/www/my-project/shared /var/www/my-project/releases as deployer@my-server.comDEBUG [7b880538] Command: /usr/bin/env mkdir -p /var/www/my-project/shared /var/www/my-project/releasesINFO [7b880538] Finished in 0.016 seconds with exit status 0 (successful).INFO [12fa8621] Running /usr/bin/env mkdir -p /var/www/my-project/shared/logs /var/www/my-project/shared/tmp/cache/models /var/www/my-project/shared/tmp/cache/persistent /var/www/my-project/shared/tmp/cache/views /var/www/my-project/shared/tmp/sessions /var/www/my-project/shared/tmp/tests as deployer@my-server.comDEBUG [12fa8621] Command: /usr/bin/env mkdir -p /var/www/my-project/shared/logs /var/www/my-project/shared/tmp/cache/models /var/www/my-project/shared/tmp/cache/persistent /var/www/my-project/shared/tmp/cache/views /var/www/my-project/shared/tmp/sessions /var/www/my-project/shared/tmp/testsINFO [12fa8621] Finished in 0.017 seconds with exit status 0 (successful).DEBUG [e11395db] Running /usr/bin/env [ -f /var/www/my-project/current/REVISION ] as deployer@my-server.comDEBUG [e11395db] Command: [ -f /var/www/my-project/current/REVISION ]DEBUG [e11395db] Finished in 0.017 seconds with exit status 1 (failed).DEBUG [66d77b61] Running /usr/bin/env [ -f /var/www/my-project/repo/HEAD ] as deployer@my-server.comDEBUG [66d77b61] Command: [ -f /var/www/my-project/repo/HEAD ]DEBUG [66d77b61] Finished in 0.015 seconds with exit status 0 (successful).INFO The repository mirror is at /var/www/my-project/repoDEBUG [b45cdad1] Running /usr/bin/env if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fi as deployer@my-server.comDEBUG [b45cdad1] Command: if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fiDEBUG [b45cdad1] Finished in 0.015 seconds with exit status 0 (successful).INFO [c63f6bd9] Running /usr/bin/env git remote update as deployer@my-server.comDEBUG [c63f6bd9] Command: cd /var/www/my-project/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env git remote update )DEBUG [c63f6bd9] Fetching originINFO [c63f6bd9] Finished in 7.166 seconds with exit status 0 (successful).DEBUG [5e0897b2] Running /usr/bin/env if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fi as deployer@my-server.comDEBUG [5e0897b2] Command: if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fiDEBUG [5e0897b2] Finished in 0.025 seconds with exit status 0 (successful).INFO [1c04aa23] Running /usr/bin/env mkdir -p /var/www/my-project/releases/20150506134335 as deployer@my-server.comDEBUG [1c04aa23] Command: cd /var/www/my-project/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env mkdir -p /var/www/my-project/releases/20150506134335 )INFO [1c04aa23] Finished in 0.019 seconds with exit status 0 (successful).INFO [2c26d74f] Running /usr/bin/env git archive master app | tar -x --strip-components 1 -f - -C /var/www/my-project/releases/20150506134335 as deployer@my-server.comDEBUG [2c26d74f] Command: cd /var/www/my-project/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env git archive master app | tar -x --strip-components 1 -f - -C /var/www/my-project/releases/20150506134335 )INFO [2c26d74f] Finished in 1.176 seconds with exit status 0 (successful).DEBUG [598e7eb8] Running /usr/bin/env if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fi as deployer@my-server.comDEBUG [598e7eb8] Command: if test ! -d /var/www/my-project/repo; then echo "Directory does not exist '/var/www/my-project/repo'" 1>&2; false; fiDEBUG [598e7eb8] Finished in 0.015 seconds with exit status 0 (successful).DEBUG [4a1e3dc5] Running /usr/bin/env git rev-list --max-count=1 --abbrev-commit master as deployer@my-server.comDEBUG [4a1e3dc5] Command: cd /var/www/my-project/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/my-project/git-ssh.sh /usr/bin/env git rev-list --max-count=1 --abbrev-commit master )DEBUG [4a1e3dc5] fbc99faDEBUG [4a1e3dc5] Finished in 0.021 seconds with exit status 0 (successful).DEBUG [927a68db] Running /usr/bin/env if test ! -d /var/www/my-project/releases/20150506134335; then echo "Directory does not exist '/var/www/my-project/releases/20150506134335'" 1>&2; false; fi as deployer@my-server.comDEBUG [927a68db] Command: if test ! -d /var/www/my-project/releases/20150506134335; then echo "Directory does not exist '/var/www/my-project/releases/20150506134335'" 1>&2; false; fiDEBUG [927a68db] Finished in 0.980 seconds with exit status 0 (successful).INFO [b5bfbfbc] Running /usr/bin/env echo "fbc99fa" >> REVISION as deployer@my-server.comDEBUG [b5bfbfbc] Command: cd /var/www/my-project/releases/20150506134335 && /usr/bin/env echo "fbc99fa" >> REVISIONINFO [b5bfbfbc] Finished in 0.016 seconds with exit status 0 (successful).INFO [79f901da] Running /usr/bin/env mkdir -p /var/www/my-project/releases/20150506134335 /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp /var/www/my-project/releases/20150506134335/tmp as deployer@my-server.comDEBUG [79f901da] Command: /usr/bin/env mkdir -p /var/www/my-project/releases/20150506134335 /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp/cache /var/www/my-project/releases/20150506134335/tmp /var/www/my-project/releases/20150506134335/tmpINFO [79f901da] Finished in 0.015 seconds with exit status 0 (successful).DEBUG [05583b4d] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/logs ] as deployer@my-server.comDEBUG [05583b4d] Command: [ -L /var/www/my-project/releases/20150506134335/logs ]DEBUG [05583b4d] Finished in 0.021 seconds with exit status 1 (failed).DEBUG [ad22f1dd] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/logs ] as deployer@my-server.comDEBUG [ad22f1dd] Command: [ -d /var/www/my-project/releases/20150506134335/logs ]DEBUG [ad22f1dd] Finished in 0.016 seconds with exit status 1 (failed).INFO [1b1a9946] Running /usr/bin/env ln -s /var/www/my-project/shared/logs /var/www/my-project/releases/20150506134335/logs as deployer@my-server.comDEBUG [1b1a9946] Command: /usr/bin/env ln -s /var/www/my-project/shared/logs /var/www/my-project/releases/20150506134335/logsINFO [1b1a9946] Finished in 0.844 seconds with exit status 0 (successful).DEBUG [0721af0a] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/tmp/cache/models ] as deployer@my-server.comDEBUG [0721af0a] Command: [ -L /var/www/my-project/releases/20150506134335/tmp/cache/models ]DEBUG [0721af0a] Finished in 0.019 seconds with exit status 1 (failed).DEBUG [67fd2a2a] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/tmp/cache/models ] as deployer@my-server.comDEBUG [67fd2a2a] Command: [ -d /var/www/my-project/releases/20150506134335/tmp/cache/models ]DEBUG [67fd2a2a] Finished in 0.018 seconds with exit status 1 (failed).INFO [592441e0] Running /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/models /var/www/my-project/releases/20150506134335/tmp/cache/models as deployer@my-server.comDEBUG [592441e0] Command: /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/models /var/www/my-project/releases/20150506134335/tmp/cache/modelsINFO [592441e0] Finished in 0.018 seconds with exit status 0 (successful).DEBUG [101259fc] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/tmp/cache/persistent ] as deployer@my-server.comDEBUG [101259fc] Command: [ -L /var/www/my-project/releases/20150506134335/tmp/cache/persistent ]DEBUG [101259fc] Finished in 0.018 seconds with exit status 1 (failed).DEBUG [e83e75e3] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/tmp/cache/persistent ] as deployer@my-server.comDEBUG [e83e75e3] Command: [ -d /var/www/my-project/releases/20150506134335/tmp/cache/persistent ]DEBUG [e83e75e3] Finished in 0.020 seconds with exit status 1 (failed).INFO [72653949] Running /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/persistent /var/www/my-project/releases/20150506134335/tmp/cache/persistent as deployer@my-server.comDEBUG [72653949] Command: /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/persistent /var/www/my-project/releases/20150506134335/tmp/cache/persistentINFO [72653949] Finished in 0.018 seconds with exit status 0 (successful).DEBUG [b0043afb] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/tmp/cache/views ] as deployer@my-server.comDEBUG [b0043afb] Command: [ -L /var/www/my-project/releases/20150506134335/tmp/cache/views ]DEBUG [b0043afb] Finished in 0.018 seconds with exit status 1 (failed).DEBUG [d5535049] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/tmp/cache/views ] as deployer@my-server.comDEBUG [d5535049] Command: [ -d /var/www/my-project/releases/20150506134335/tmp/cache/views ]DEBUG [d5535049] Finished in 0.015 seconds with exit status 1 (failed).INFO [9df85492] Running /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/views /var/www/my-project/releases/20150506134335/tmp/cache/views as deployer@my-server.comDEBUG [9df85492] Command: /usr/bin/env ln -s /var/www/my-project/shared/tmp/cache/views /var/www/my-project/releases/20150506134335/tmp/cache/viewsINFO [9df85492] Finished in 0.017 seconds with exit status 0 (successful).DEBUG [fd776064] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/tmp/sessions ] as deployer@my-server.comDEBUG [fd776064] Command: [ -L /var/www/my-project/releases/20150506134335/tmp/sessions ]DEBUG [fd776064] Finished in 0.016 seconds with exit status 1 (failed).DEBUG [f45e7773] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/tmp/sessions ] as deployer@my-server.comDEBUG [f45e7773] Command: [ -d /var/www/my-project/releases/20150506134335/tmp/sessions ]DEBUG [f45e7773] Finished in 0.016 seconds with exit status 1 (failed).INFO [2cccf11c] Running /usr/bin/env ln -s /var/www/my-project/shared/tmp/sessions /var/www/my-project/releases/20150506134335/tmp/sessions as deployer@my-server.comDEBUG [2cccf11c] Command: /usr/bin/env ln -s /var/www/my-project/shared/tmp/sessions /var/www/my-project/releases/20150506134335/tmp/sessionsINFO [2cccf11c] Finished in 0.017 seconds with exit status 0 (successful).DEBUG [4949e322] Running /usr/bin/env [ -L /var/www/my-project/releases/20150506134335/tmp/tests ] as deployer@my-server.comDEBUG [4949e322] Command: [ -L /var/www/my-project/releases/20150506134335/tmp/tests ]DEBUG [4949e322] Finished in 0.015 seconds with exit status 1 (failed).DEBUG [102183f1] Running /usr/bin/env [ -d /var/www/my-project/releases/20150506134335/tmp/tests ] as deployer@my-server.comDEBUG [102183f1] Command: [ -d /var/www/my-project/releases/20150506134335/tmp/tests ]DEBUG [102183f1] Finished in 0.014 seconds with exit status 1 (failed).INFO [06a03326] Running /usr/bin/env ln -s /var/www/my-project/shared/tmp/tests /var/www/my-project/releases/20150506134335/tmp/tests as deployer@my-server.comDEBUG [06a03326] Command: /usr/bin/env ln -s /var/www/my-project/shared/tmp/tests /var/www/my-project/releases/20150506134335/tmp/testsINFO [06a03326] Finished in 0.017 seconds with exit status 0 (successful).INFO [f028e9f1] Running /usr/bin/env cd /var/www/my-project/releases/20150506134335/ && composer update as deployer@my-server.comDEBUG [f028e9f1] Command: cd /var/www/my-project/releases/20150506134335/ && composer updateDEBUG [f028e9f1] Loading composer repositories with package informationDEBUG [f028e9f1] Updating dependencies (including require-dev)DEBUG [f028e9f1] - Removing cakephp/cakephp (2.6.3)DEBUG [f028e9f1] - Installing cakephp/cakephp (2.6.4)DEBUG [f028e9f1] Downloading: Connecting...DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 0%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 5%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 10%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 15%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 20%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 25%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 30%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 35%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 40%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 45%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 50%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 55%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 60%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 65%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 70%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 75%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 80%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 85%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 90%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 95%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 100%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 100%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] - Removing phpunit/php-file-iterator (1.3.4)DEBUG [f028e9f1] - Installing phpunit/php-file-iterator (1.4.0)DEBUG [f028e9f1] Downloading: Connecting...DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 0%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 100%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] - Removing symfony/yaml (v2.6.5)DEBUG [f028e9f1] - Installing symfony/yaml (v2.6.6)DEBUG [f028e9f1] Downloading: Connecting...DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 0%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 5%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 10%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 15%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 65%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 70%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 75%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 80%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 100%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] - Removing guzzlehttp/ringphp (1.0.6)DEBUG [f028e9f1] - Installing guzzlehttp/ringphp (1.0.7)DEBUG [f028e9f1] Downloading: Connecting...DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 0%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 15%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 25%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 40%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 50%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 55%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 65%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 80%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 90%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 95%DEBUG [f028e9f1]DEBUG [f028e9f1] Downloading: 100%DEBUG [f028e9f1]DEBUG [f028e9f1]DEBUG [f028e9f1] Writing lock fileDEBUG [f028e9f1] Generating autoload filesINFO [f028e9f1] Finished in 21.330 seconds with exit status 0 (successful).DEBUG [15975d75] Running /usr/bin/env if test ! -d /var/www/my-project/releases/20150506134335; then echo "Directory does not exist '/var/www/my-project/releases/20150506134335'" 1>&2; false; fi as deployer@my-server.comDEBUG [15975d75] Command: if test ! -d /var/www/my-project/releases/20150506134335; then echo "Directory does not exist '/var/www/my-project/releases/20150506134335'" 1>&2; false; fiDEBUG [15975d75] Finished in 0.015 seconds with exit status 0 (successful).INFO [6b59d736] Running /usr/bin/env composer install --no-dev --prefer-dist --no-interaction --quiet --optimize-autoloader as deployer@my-server.comDEBUG [6b59d736] Command: cd /var/www/my-project/releases/20150506134335 && /usr/bin/env composer install --no-dev --prefer-dist --no-interaction --quiet --optimize-autoloaderINFO [6b59d736] Finished in 0.212 seconds with exit status 0 (successful).INFO [e565a2f9] Running /usr/bin/env ln -s /var/www/my-project/releases/20150506134335 /var/www/my-project/releases/current as deployer@my-server.comDEBUG [e565a2f9] Command: /usr/bin/env ln -s /var/www/my-project/releases/20150506134335 /var/www/my-project/releases/currentINFO [e565a2f9] Finished in 0.016 seconds with exit status 0 (successful).INFO [80a0ddf0] Running /usr/bin/env mv /var/www/my-project/releases/current /var/www/my-project as deployer@my-server.comDEBUG [80a0ddf0] Command: /usr/bin/env mv /var/www/my-project/releases/current /var/www/my-projectINFO [80a0ddf0] Finished in 0.018 seconds with exit status 0 (successful).DEBUG [1d600961] Running /usr/bin/env ls -xtr /var/www/my-project/releases as deployer@my-server.comDEBUG [1d600961] Command: /usr/bin/env ls -xtr /var/www/my-project/releasesDEBUG [1d600961] 20150506043519 20150506092943 20150506093923 20150506102952 20150506103719DEBUG [1d600961] 20150506122119 20150506130326 20150506130947 20150506134335DEBUG [1d600961] Finished in 0.018 seconds with exit status 0 (successful).INFO Keeping 5 of 9 deployed releases on my-server.comINFO [64fdd5ed] Running /usr/bin/env rm -rf /var/www/my-project/releases/20150506043519 /var/www/my-project/releases/20150506092943 /var/www/my-project/releases/20150506093923 /var/www/my-project/releases/20150506102952 as deployer@my-server.comDEBUG [64fdd5ed] Command: /usr/bin/env rm -rf /var/www/my-project/releases/20150506043519 /var/www/my-project/releases/20150506092943 /var/www/my-project/releases/20150506093923 /var/www/my-project/releases/20150506102952INFO [64fdd5ed] Finished in 0.308 seconds with exit status 0 (successful).DEBUG [300aa3cb] Running /usr/bin/env if test ! -d /var/www/my-project/releases; then echo "Directory does not exist '/var/www/my-project/releases'" 1>&2; false; fi as deployer@my-server.comDEBUG [300aa3cb] Command: if test ! -d /var/www/my-project/releases; then echo "Directory does not exist '/var/www/my-project/releases'" 1>&2; false; fiDEBUG [300aa3cb] Finished in 0.015 seconds with exit status 0 (successful).INFO [66a165f2] Running /usr/bin/env echo "Branch master (at fbc99fa) deployed as release 20150506134335 by vagrant" >> /var/www/my-project/revisions.log as deployer@my-server.comDEBUG [66a165f2] Command: echo "Branch master (at fbc99fa) deployed as release 20150506134335 by vagrant" >> /var/www/my-project/revisions.logINFO [66a165f2] Finished in 0.017 seconds with exit status 0 (successful).
十分冗长的部署输出
从浏览器访问时,由于前面的准备工作不够完善,还有若干问题,下面一一解决。
第一个错误是这样的:
Warning: _cake_core_ cache was unable to write 'cake_dev_en-us' to File cache in /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Cache/Cache.php on line 328Warning: /var/www/my-project/releases/20150506134335/tmp/cache/persistent/ is not writable in /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Cache/Engine/FileEngine.php on line 385Fatal error: Uncaught exception 'CacheException' with message 'Cache engine "_cake_core_" is not properly configured. Ensure required extensions are installed, and credentials/permissions are correct' in /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Cache/Cache.php:186 Stack trace: #0 /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Cache/Cache.php(151): Cache::_buildEngine('_cake_core_') #1 /var/www/my-project/releases/20150506134335/Config/core.php(388): Cache::config('_cake_core_', Array) #2 /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Core/Configure.php(72): include('/var/www/privat...') #3 /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/bootstrap.php(175): Configure::bootstrap(true) #4 /var/www/my-project/releases/20150506134335/webroot/index.php(104): include('/var/www/privat...') #5 {main} thrown in /var/www/my-project/releases/20150506134335/Vendor/cakephp/cakephp/lib/Cake/Cache/Cache.php on line 186
这是因为apache运行的用户www-data对缓存的目录没有写入权限。根据Capistrano的Structure文档,服务器上部署的目录结构是这样的:
├── current -> /var/www/my-project/releases/20150506134335/├── releases│ ├── 20150506103719│ ├── 20150506122119│ ├── 20150506130326│ ├── 20150506130947│ └── 20150506134335├── repo│ └── ├── revisions.log└── shared └── <linked_files and linked_dirs>
shared 目录下的 linked_files 和 linked_dirs 目录就是缓存和日志所在的目录 tmp, logs 等等。要把这些目录给予用户 www-data 完全的权限,我采用的做法是把这些目录的组设置为www-data,再给予该目录的组写入权限,则www-data就有了写入的权限了:
deployer@my-server.com $ cd /var/www/my-project
deployer@my-server.com $ sudo chown :www-data releases
deployer@my-server.com $ sudo chmod g+w releases
deployer@my-server.com $ cd shareddeployer@my-server.com $ sudo chown :www-data *
deployer@my-server.com $ sudo chmod g+w *
写入权限的问题解决之后,遇到的问题是:
Missing Plugin
Error: The application is trying to load a file from the DebugKit plugin
Error: Make sure your plugin DebugKit is in the 20150507044736/Plugin directory and was loaded
<?phpCakePlugin::load('DebugKit');
Loading all plugins: If you wish to load all plugins at once, use the following line in your 20150507044736/Config/bootstrap.php file
CakePlugin::loadAll();
Notice: If you want to customize this error message, create 20150507044736/View/Errors/missing_plugin.ctp
这是因为生产环境的部署中,Composer没有安装require-dev的包,所以就没有安装CakePHP的DebugKit插件。修改代码,在生产环境中不加载DebugKit:
switch (env('HTTP_HOST')) { case '127.0.0.1': // local CakePlugin::load('DebugKit'); break; case 'www.my-project.com': // production default: break;}
这样再次部署,就一切正常了。
当推送了改动到GitLab的代码库之后,可以看到,构建和部署自动启动,并正确执行: