曾经有一段时间,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
目录。该工具会在每个版本中为您配置的版本创建符号链接。
事件驱动工具,所有策略和任务在运行时都会在事件之前和之后触发。当任务失败时,它们还提供特殊的暂停事件。例如,对于一般失败,这可能是 dependency.halt
或 deploy.halt
。这使我们能够在需要的地方挂钩流程。
部署期间发生的默认事件是:
deploy.before
:在任何事情发生之前。create-release.before
:在创建新的发布目录之前。create-release.after
:创建新的发布目录后。dependency.before
:在安装或更新依赖项之前。dependency.after
:安装或更新依赖项后。也许请确保供应商文件夹中的二进制文件是可执行的。test.before
:运行测试之前。test.after
:运行测试后。migrate.before
:运行数据库迁移之前。也许您想备份数据库?migrate.after
:运行数据库迁移后。deploy.before-symlink
:在将版本符号链接为当前版本之前。deploy.after
:已完成。您可以通知人们一切顺利或其他情况。我们还可以创建自己的事件,我们可以触发和监听这些事件。目前,我们将坚持为我们提供的这些事件。现在它们对我们来说就足够了。
在 Rocketeer 的核心,我们发现了一个名为“任务”的概念。幕后发生的大部分事情都是核心任务。任务的定义可以是作为部署中的步骤执行的一组指令。如果我们查看该工具提供的一些类,我们可以大致了解什么是任务:诸如 Deploy
、Setup
、迁移
、 class="inline">回滚 和 class="inline">依赖项。当您部署时,部署命令本身就是一个带有子任务的任务。
在这里,您将开始了解该工具与 PHP 的集成程度,因为您将用该语言编写任务。您可以通过三种不同的方式创建自己的任务:
任意终端命令。这些是您想要在服务器上运行的单行代码。对于很多事情都很有用,例如运行 gulp build ---development
。
关闭。如果您需要更多的灵活性或复杂性,您可以将任务编写为闭包(匿名函数)。假设您想在部署期间为 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' => [], ],
您可能已经知道所提供的任务非常通用。以 依赖项
为例。我们谈论的是哪种依赖关系以及哪个包管理器?
这就是策略发挥作用的地方。策略是任务的具体实现,例如使用 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 | +--------------+----------------+-----------------------------------------------------------------------+
假设您正在为您的应用程序使用 BDD With Behat,而不是 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.
中设置应用程序名称并配置生产服务器。
'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.
中进行更改。
// Deploys to /var/www/my-deployable-app/ 'root_directory' => '/var/www/',
在同一文件中,您可以覆盖应用程序名称并为应用程序指定目录。
// Deploys to /var/www/tutsplus-tutorial 'app_directory' => 'tutsplus-tutorial',
现在您已经有了部署的接收端,但您还需要设置代码的位置,以便可以提取它。您可以通过在 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
键下,找到要更改的文件中的相应键。在本例中,它是 branch
,位于 scm.php
。
现在您已完成成功部署所需的一切设置。您尚未满足完整部署的要求,但足以将应用程序克隆到服务器并提供给最终用户。首先,您可以执行检查策略来查看您的服务器是否满足要求。
$ 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
的符号链接更改为以前的版本。
设置共享目录很简单 - 只需将它们添加到 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 的依赖项。 依赖项
的默认策略设置为 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
中执行此操作,如果该文件尚不存在,则可以创建该文件。
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 应用程序集成得非常好。
对于那些刚刚开始使用 PHP 或希望通过扩展来扩展您的知识、站点或应用程序的人,我们在 Envato Market 中提供了您可以学习的各种内容。
为 Rocketeer 编写入门教程是一项艰巨的任务。该工具非常灵活,以至于绘制停止位置并不容易。我希望我了解了使用此工具的可能性以及它如何使您和您的应用程序受益。如果您想深入了解,我建议您阅读完整的文档。该工具的功能远不止我在本文中所能介绍的内容。
以上是Rocketeer:部署 PHP 应用程序的终极工具的详细内容。更多信息请关注PHP中文网其他相关文章!