曾经有一段时间,PHP开发人员不得不使用针对通用Web应用程序的部署工具。 例如,您可以在Johannes Schickling的有关使用Capistrano部署Laravel应用程序的教程中看到这一点。 这是一个Ruby工具,您需要编写Ruby代码。 这些工具可以完成工作,但与PHP应用程序的集成有限。 在某些情况下,这可能会导致破解解决方案。
但是如今,我们很幸运地获得了一些用我们的语言编写的部署工具,这些工具可以实现更深入的集成。 这些工具之一是Rocketeer,该工具从Capistrano和Laravel框架中汲取了灵感。
Rocketeer是一种现代工具,可为您的部署需求提供一种出色的方法。 那就是在不同的环境和服务器上运行任务并管理您的应用程序。 最重要的是,它还具有一些魔力,例如,如果检测到composer.json
文件,则安装Composer依赖项。 您可以获得现代PHP应用程序合理的默认设置和常见任务的自动化功能。 您可以自定义和扩展所有内容。
您可以将其描述为运行客户端的SSH任务运行程序。 这些命令通过SSH连接在服务器上执行。 不幸的是,如果您使用仅通过FTP访问的共享主机提供程序,那么您将很不走运。 您还需要一个远程存储库,该工具可以从中获取代码。 默认情况下支持Git和SVN。 需要其他版本控制系统的支持吗? 使用提供的接口编写您自己的实现。
安装
您可以通过两种不同的方式安装Rocketeer。 您可以下载phar文件并使其可执行,也可以通过Composer安装它。 我是后者的支持者。 将其作为依赖项可简化克隆存储库时的安装。 任何克隆存储库以使其启动并运行的人都将从中受益。
使用Composer安装:
$ composer require anahkiasen/rocketeer --dev
我不建议您全局安装它。 将其保持在存储库级别将确保部署的每个人都运行相同的版本。 我建议您将vendor/bin
添加到PATH。 然后,您可以通过在项目根目录中键入rocketeer
来使用二进制文件。
点火
让我们开始吧! 首先,您要引导目录和文件以进行配置。 您可以通过在项目的根目录中运行rocketeer ignite
来实现。
应用程序启动时,该工具将在项目中创建一个.rocketeer
文件夹。 目录的内容将如下所示:
| .rocketeer
| -- config.php
| -- hooks.php
| -- paths.php
| -- remote.php
| -- scm.php
| -- stages.php
| -- strategies.php
这些是开始设置部署所需的所有配置文件。 每当我从这里开始引用配置文件时,它都位于.rocketeer/
目录中。
远程文件夹结构
了解Rocketeer如何在服务器端管理其文件夹结构非常重要,因为它与常规设置有所不同。 它使用一些目录来管理部署的某些方面,因此可以有效地完成其工作。 您指定要在服务器上部署应用程序的路径,其余部分将由该工具完成。 如果您将/var/www/app
作为应用程序目录,则该文件夹将如下所示。
| /var/www/app
| | -- current => /var/www/app/releases/20160103183754
| | -- releases
| | | -- 20160101161243
| | | |-- uploads => /var/www/app/shared/uploads
| | | -- 20160103183754
| | | |-- uploads => /var/www/app/shared/uploads
| | -- shared
| | | -- uploads
最重要的文件夹是current
,它指向您的最新版本。 这是Web服务器的文档根目录应设置为的位置。 那么,部署时会发生什么?
- 该工具在
releases
目录中创建一个带时间戳的文件夹。 - 完成所有任务以准备发布。
- 将
current
的符号链接更新为新版本。
此过程使您的部署对用户透明。 版本之间的切换几乎是即时的,通常称为原子部署 。
在部署之间某些数据应该是持久的。 例如,这可以是文件上传,用户会话和日志。 这些文件或文件夹将进入shared
目录。 该工具会在每个版本中为您配置的版本创建符号链接。
大事记
事件驱动该工具,并且所有策略和任务在运行时都会在事件发生之前和之后触发。 当任务失败时,它们还提供特殊的停止事件。 例如,对于一般故障,可以是dependencies.halt
或deploy.halt
。 这使我们能够进入需要的过程。
部署期间发生的默认事件是:
-
deploy.before
:在任何事情发生之前。 -
create-release.before
:创建新的发行目录之前。 -
create-release.after
:创建新的发行目录之后。 -
dependencies.before
:在安装或更新依赖项之前。 -
dependencies.after
:安装或更新依赖项之后。 也许要确保供应商文件夹中的二进制文件是可执行的。 -
test.before
:运行测试之前。 -
test.after
:运行测试后。 -
migrate.before
:在运行数据库迁移之前。 也许您想备份数据库? -
migrate.after
:运行数据库迁移后。 -
deploy.before-symlink
:在将该版本deploy.before-symlink
链接为当前版本之前。 -
deploy.after
:已完成。 您可以通知人们一切顺利。
我们还可以创建自己的事件,以触发并收听。 目前,我们将继续提供这些活动。 它们对我们现在已经足够。
任务
在Rocketeer的核心,我们发现了一个名为task的概念。 幕后发生的大部分事情都是核心任务。 任务的定义可以是一组指令,以作为部署中的步骤执行。 如果我们看一下该工具提供的一些类,我们可以大致了解哪些任务是: Deploy
, Setup
, Migrate
, Rollback
和Dependencies
。 部署时,deploy命令本身就是带有子任务的任务。
任务类型
在这里,您将开始了解该工具与PHP的集成程度,因为您将使用该语言编写任务。 您可以通过三种不同的方式创建自己的任务:
任意终端命令 。 这些是您要在服务器上运行的单行代码。 在很多事情上可能很有用,例如运行gulp build ---production
。
关闭 。 如果您需要更多的灵活性或复杂性,可以将任务编写为闭包(匿名函数)。 假设您要在部署期间为API生成文档。
function($task) {
return $task->runForCurrentRelease('apigen generate source src destination api');
}
类 。 对于更复杂的任务,应利用该选项为任务创建类。 您创建一个类并扩展Rocketeer\Abstracts\AbstractTask
。 然后,您必须至少提供一个描述和execute()
方法。 这是一个完全无用的示例,仅用于显示任务类的结构:
namespace MyDeployableApp\Deploy\Tasks;
class HelloWorld extends \Rocketeer\Abstracts\AbstractTask
{
/**
* Description of the Task
*
* @var string
*/
protected $description = 'Says hello to the world';
/**
* Executes the Task
*
* @return void
*/
public function execute() {
$this->explainer->line('Hello world!');
return true;
}
}
请注意,您必须自己注册任务类。 您可以通过hooks.php
文件执行此操作,然后将其添加到custom
数组中...
'custom' => array('MyDeployableApp\Deploy\Tasks\HelloWorld',),
...或者您可以通过外观进行此操作:
Rocketeer::add('MyDeployableApp\Deploy\Tasks\HelloWorld');
一旦注册,就可以执行它:
$ rocketeer hello:world
staging/0 | HelloWorld (Says hello to the world)
staging/0 |=> Hello world!
Execution time: 0.004s
定义任务
我们首先讨论事件,因为我们将任务挂在过程中需要它们的地方。 您可以通过几种方式来执行此操作。 随心所欲,满足您的复杂性要求。
定义任务的最简单方法是在hooks.php
文件中。 为此,它提供了两个数组,用于指定在某些事件之前或之后执行任务。
'before' => [
'setup' => [],
'deploy' => ['hello:world'],
'cleanup' => [],
],
策略
您可能已经可以知道所提供的任务是非常通用的。 以Dependencies
为例。 我们在谈论什么样的依赖关系以及哪个程序包管理器?
这就是战略发挥作用的地方。 策略是任务的特定实现,例如使用Behat运行测试或使用Gulp构建前端。 任务具有默认策略,可以选择通过CLI运行其他策略。 我们可以列出可用的策略,如下所示:
$ rocketeer strategies
+--------------+----------------+-----------------------------------------------------------------------+
| Strategy | Implementation | Description |
+--------------+----------------+-----------------------------------------------------------------------+
| check | Php | Checks if the server is ready to receive a PHP application |
| check | Ruby | Checks if the server is ready to receive a Ruby application |
| check | Node | Checks if the server is ready to receive a Node application |
| deploy | Clone | Clones a fresh instance of the repository by SCM |
| deploy | Copy | Copies the previously cloned instance of the repository and update it |
| deploy | Sync | Uses rsync to create or update a release from the local files |
| test | Phpunit | Run the tests with PHPUnit |
| migrate | Artisan | Migrates your database with Laravel's Artisan CLI |
| dependencies | Composer | Installs dependencies with Composer |
| dependencies | Bundler | Installs dependencies with Bundler |
| dependencies | Npm | Installs dependencies with NPM |
| dependencies | Bower | Installs dependencies with Bower |
| dependencies | Polyglot | Runs all of the above package managers if necessary |
+--------------+----------------+-----------------------------------------------------------------------+
制定自己的策略
假设您正在为应用程序使用Behat做BDD ,而不是TDD。 然后,您想使用Behat而不是PHPUnit运行测试。 由于它是测试运行程序 ,因此已经有一个策略名称空间,但是没有实现。 创建目录.rocketeer/strategies/
,并将新的BehatStrategy.php
放在其中。
namespace MyDeployableApp\Deploy\Strategies;
use Rocketeer\Abstracts\Strategies\AbstractStrategy;use Rocketeer\Interfaces\Strategies\TestStrategyInterface;
class BehatStrategy extends AbstractStrategy implements TestStrategyInterface
{
public function test() {
return $this->binary('vendor/bin/behat')->runForCurrentRelease();
}
}
现在,您可以将您的测试策略切换出在新的实施strategies.php
。
'test' => 'Behat',
连接和阶段
无论您拥有适当的基础架构还是构想一个基础架构都没关系。 您的应用程序是否跨多个服务器部署到多个环境都没有关系。 火箭手将在那里为您服务。 您甚至可以在同一服务器上有许多不同的位置。 这是连接和阶段输入的术语。
连接是将应用程序部署到的服务器。 这通常称为环境 ,生产和暂存就是这种情况的示例。 在工具中轻而易举地配置这些连接。 您可以通过嵌套数组来实现,也可以通过为每个连接保留单独的文件来实现。 每个连接中也可以有多个服务器。
阶段就像连接内部的连接一样,是一种“连接接受”。 您可以使用阶段在单个服务器上设置暂存和生产环境。 因此,您不必拥有两个单独的连接,而可以拥有一个包含两个阶段的连接。
外挂程式
一个很棒的功能是我们可以使用插件来扩展我们的过程。 有一些官方可以与Laravel , Slack , HipChat和Campfire集成。 然后在Packagist上有一些,但不是很多。 通过CLI安装插件是一项简单的任务:
$ rocketeer plugin:install rocketeers/rocketeer-slack
即使插件数量有限,也为将来的插件开发留出了空间。 它说明了一个好的哲学。 而为什么不发展自己的呢?
设置部署
为了使您的应用程序起步,您需要一些基本配置。 您需要告诉Rocketeer在哪里可以找到您的应用程序以及将它部署到哪里。 让我们从设置应用程序名称并在config.php
配置生产服务器开始。
'application_name' => 'my-deployable-app',
// [...]
'connections' => [
'staging' => [
'host' => 'staging.my-deployable-app.com',
'username' => '',
'password' => '',
'key' => '/Users/niklas/.ssh/id_rsa',
'keyphrase' => '',
'agent' => '',
'db_role' => true,
],
'production' => [
'host' =>
'www.my-deployable-app.com',
'username' => '',
'password' => '',
'key' => '/Users/niklas/.ssh/id_rsa',
'keyphrase' => '',
'agent' => '',
'db_role' => true,
],
],
现在,您有了一个应用程序名称和一个服务器,可以将应用程序部署到该服务器上。 此设置使用SSH密钥身份验证,但是您也可以使用用户名和密码进行连接。 要提示输入用户名和密码,请设置'key' => ''
。 该工具会将凭据存储在本地计算机上,并在以后每次使用。 我不建议在配置文件中设置用户名和密码,因为您永远不希望将凭据提交到存储库。
现在,您应该做的就是更改部署到的默认连接。 将默认值设置为生产并不理想。 您不想意外地部署到生产中。 因此,在同一文件中,查找default
密钥,然后将值更改为staging
。
'default' => ['staging'],
应用程序名称本身并不那么重要。 但是,如果您没有指定要部署到的文件夹,它将使用它作为根目录中的文件夹名称。 默认情况下,根目录设置为/home/www
。 使用此应用程序名称,它将部署到/home/www/my-deployable-app
。 如果要更改根目录,可以在remote.php
进行更改。
// Deploys to /var/www/my-deployable-app/
'root_directory' => '/var/www/',
在同一文件中,您可以覆盖应用程序名称并为应用程序指定目录。
// Deploys to /var/www/tutsplus-tutorial
'app_directory' => 'tutsplus-tutorial',
现在您已经有了部署的接收端,但是您还需要设置代码的位置以便可以将其提取。 您可以通过在scm.php
设置远程存储库来scm.php
。 默认情况下,它使用Git,但也支持SVN。 您告诉它我们存储库的地址,并在需要时提供凭据。 我建议您也在此处使用SSH密钥身份验证,并将用户名和密码留空。
'repository' => 'git@github.com:owner/name.git',
'username' => '',
'password' => '',
'branch' => 'master',
由于您已将连接添加到生产服务器,因此您想要部署master分支。
连接和阶段特定配置
在大多数情况下,您不希望所有连接或阶段都使用相同的配置选项。 例如,假设您要将另一个分支部署到登台环境。 Rocketeer允许您使用config.php
覆盖连接和阶段的配置值。 要在暂存连接上部署名为暂存的分支,请执行以下操作:
'on' => [
'connections' => [
'staging' => [
'scm' => [
'branch' => 'staging',
]
]
],
],
它使用嵌套数组覆盖配置值。 在staging
密钥下,在要更改的文件中找到相应的密钥。 在这种情况下,它是scm.php
的branch
。
部署,升空!
现在,您已完成一切,可以成功进行部署。 您尚未满足完整部署的要求,但足以将您的应用程序克隆到服务器并提供给最终用户。 首先,您可以执行检查策略,以查看服务器是否满足要求。
$ rocketeer check
如果一切正常,则可以运行以下命令进行部署:
$ rocketeer deploy
由于这是您的第一个部署,因此Rocketeer将确保一切正常。 该工具将创建它所需的目录,我们的应用程序将位于其中。如果一切顺利,则应在服务器上完整构建应用程序。
如果您在上一节中将默认连接更改为暂存,它将始终部署为暂存。 当然,除非您告诉它部署到其他地方。 如果要部署在其他连接或多个连接上,请使用--on
开关。
# Deploy to production
$ rocketeer deploy --on="production"
# Deploy to staging and production
$ rocketeer deploy --on="staging,production"
想看看一旦按下按钮,服务器上会发生什么? 使用--pretend
标志可以使该工具告诉您它将在服务器上执行的操作。
$ rocketeer deploy --pretend
我们需要回滚。
不幸的是,我们需要照顾那些破坏功能或对基础架构造成严重破坏的部署。 然后,您需要快速回滚到最新版本。 幸运的是,这是一个简单的操作,只需运行以下命令:
$ rocketeer rollback
由于它存储许多内部版本,因此回滚速度很快。 它将current
的符号链接更改为以前的版本。
共享目录
设置共享目录很简单-只需将它们添加到remote.php
找到的shared
数组remote.php
。 之后,Rocketeer将为您创建并链接这些文件夹。 指定的路径应相对于您的根文件夹。
'shared' => [
'storage/logs',
'storage/sessions',
'storage/uploads',
'.env',
],
可写目录
大多数共享目录也将需要Web服务器能够对其进行写入。 编写日志,会话或文件上传通常是任何应用程序执行的任务。 这些将添加到remote.php
的permissions.files
数组中。
'permissions' => [
'files' => [
'storage/sessions',
'storage/logs',
'storage/uploads',
],
// [...]
],
安装或更新依赖项
如果应用程序依赖于任何种类的依赖项,则需要安装或更新依赖项。 该工具附带了对最受欢迎的软件包管理器的支持。 如果您具有默认设置,则无需配置任何内容。 它将检测并安装或更新Composer , Npm , Bower和Bundler的依赖项。 dependencies
的默认策略设置为Polyglot
。 这是该工具检测和安装不同程序包管理器依赖性的方法。
但是,假设您要在staging上安装所有依赖项,并且该工具默认使用--no-dev
标志。 也许您想安装PHPUnit来运行测试,这是开发的依赖。 在strategies.php
,您可以找到composer
密钥,该密钥告诉工具如何执行Composer。 然后,您可以在config.php
覆盖它:
use Rocketeer\Binaries\PackageManagers\Composer;
// [...]
'on' => [
// [...]
'connections' => [
'staging' => [
'strategies' => [
'composer' => [
'install' => function (Composer $composer, $task) {
return $composer->install([], ['--no-interaction' => null, '--prefer-dist' => null]);
}
],
],
]
],
],
数据库迁移
在拥有完整的发行版之前,通常要迁移数据库,这只是在符号链接到当前版本之前。 无论使用哪种工具,都可以告诉它在deploy.before-symlink
之前运行。 该钩子不是常规钩子,而是内部钩子。 然后,您需要在hooks.php
其他地方进行注册。 您可以在events.php
执行此events.php
,如果尚不存在,可以创建它。
use Rocketeer\Facades\Rocketeer;
// Laravel
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
$task->runForCurrentRelease('php artisan migrate');
});
// Symfony2
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
$task->runForCurrentRelease('php app/console doctrine:migrations:migrate');
});
// Stand-alone Doctrine
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
$task->runForCurrentRelease('doctrine migrations:migrate --no-interaction');
});
运行测试
在部署过程中运行测试是确保没有破损的代码或测试通过裂缝的一种好方法。 默认情况下,该工具使用PHPUnit,您可以在安装或更新依赖项后挂接测试运行器以运行。
'after' => [
'setup' => [],
'deploy' => [],
'dependencies' => ['test'],
'cleanup' => [],
],
现在,我们应该看到它在每个部署上都在执行PHPUnit,并且如果测试失败,它将中止。 确保从中看到输出,否则可能会在查找PHPUnit二进制文件或测试套件时出现问题。
staging/0 |---- Test (Run the tests on the server and displays the output) fired by dependencies.after
staging/0 |------ Test/Phpunit (Run the tests with PHPUnit)
$ cd /var/www/my-deployable-app/releases/20160129220251$ /var/www/my-deployable-app/releases/20160129220251/vendor/bin/phpunit --stop-on-failure
[deploy@staging.mydeployableapp.com] (staging) PHPUnit 4.8.21 by Sebastian Bergmann and contributors.
[deploy@staging.mydeployableapp.com] (staging)
[deploy@staging.mydeployableapp.com] (staging) .
[deploy@staging.mydeployableapp.com] (staging) Time: 4.79 seconds, Memory: 6.00Mb
[deploy@staging.mydeployableapp.com] (staging) OK (1 test, 1 assertion)
[deploy@staging.mydeployableapp.com] (staging)staging/0 |=====> Tests passed successfully
前端构建工具
通常,我们的应用程序不仅是后端,除非它们是REST API。 使用诸如Grunt , Gulp或Webpack之类的工具来为前端运行构建工具是一项常见的任务。 将这一部分纳入部署过程并不比使用钩子来运行以下命令更简单:
'after' => [
'setup' => [],
'deploy' => [],
'dependencies' => ['gulp build'],
'cleanup' => [],
],
前端通常也依赖于依赖项,因此在安装或更新它们后运行工具。
提示与技巧
运行更新
如果不想在部署时创建新版本,则可以选择运行更新。 执行此操作时请小心,因为您将无法回滚到以前的版本,只能回滚到以前的版本。 但这是使用以下最新更新来更新应用程序的快速简便方法:
$ rocketeer update
本地任务
有时在本地环境中运行任务会很好。 假设您要运行PHPCS检查或构建您的静态资产,然后将其上传到服务器,而无需服务器上的某些二进制文件。 如果创建任务类,则可以将受保护的变量$local
为true
。
class MyTask extends Rocketeer\Abstracts\AbstractTask
{
protected $local = true;
// [...]
}
结论
部署过程是应用程序生命周期的重要组成部分。 诸如Rocketeer之类的工具可让您轻松地做到这一点。 当将它与PHP应用程序集成得很好时,尤其如此。
对于那些刚开始使用PHP或希望通过扩展来扩展您的知识,网站或应用程序的人,我们可以在Envato Market中进行很多研究。
向Rocketeer编写入门教程是一项艰巨的任务。 该工具非常灵活,以至于很难在哪里停下来画线。 我希望我了解使用此工具的可能性以及它如何使您和您的应用程序受益。 如果您想深入研究,建议阅读完整的文档 。 除了我在本文中介绍的内容之外,该工具还有更多功能。
翻译自: https://code.tutsplus.com/articles/deploy-your-php-application-with-rocketeer--cms-25838