几个月前,我写了一篇文章关于如何使用Gitlab的免费CI功能设置零停机持续部署。现在GitHub Actions已经退出测试版,我已经将大部分CI/CD管道迁移过去了。
据我所知,GitHub Actions的速度略慢,但在构建管道方面不太友好。对我来说,最大的优势是它是GitHub的一部分,意味着我只需要使用一个服务。免费版也非常慷慨。
只想看代码和配置?这个文章的示例仓库在这里。我已经尽量使其通用,所以你应该能够将工作流和部署.php复制粘贴到自己的项目中,只需要做最小的修改。
设置Gitlab Actions
为了撰写这篇文章,我将从全新的Laravel安装开始,并指导您设置我们将用于测试、构建和部署代码到生产环境的操作。
Github Actions使用yaml文件进行配置,放置在.github/workflows
中。他们确实提供了一个编辑器,但在我的经历中,手动创建和编辑文件要容易得多。要创建一个新的工作流,只需在前述文件夹中创建一个新的yaml文件(例如,deploy.yml
)。
在这个文件中,您可以为构建阶段、它们的依赖项、缓存等功能进行定义。对于我们的应用程序,我们只需使用简单的三个步骤构建(GitHub称这些为作业)。
设置工作流程
在.github/workflows
下,创建一个新的工作流程deploy.yml
。我们将需要添加一些基本配置来指示操作在仓库的提交上运行工作流程,并定义一个名称。在jobs
部分,我们需要定义构建/部署过程中的每个job
。目前,我们只需建立基本支架,并在设置三个作业时填充它。
name: CI-CD # Give your workflow a name
on: push # Run the workflow for each push event (i.e commit)
jobs:
build-js:
name: Build Js/Css
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# Build JS
test-php:
name: Test/Lint PHP
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# Test and Lint PHP code
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
# Note that this needs both of the other steps to have completed successfully
needs: [build-js, test-php]
steps:
- uses: actions/checkout@v1
# Deploy Code
构建JavaScript和CSS资产
Laravel自带Mix,它提供了一个简单的接口来使用webpack构建和编译前端资源(JavaScript和CSS)。
这里值得一提的是,我们正在使用upload-artifact操作。这是必需的,以便我们的部署作业可以部署已编译的文件,因为每个作业都是在源代码的新版本上运行的。
jobs:
build-js:
name: Build Js/Css
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1 # Download the source code
- name: Yarn Build
run: |
yarn install
yarn prod
cat public/mix-manifest.json # See asset versions in log
- name: Upload build files
uses: actions/upload-artifact@v1
with:
name: assets
path: public
运行PHP linting和测试
大多数应用程序都会有一些自动的linting和(希望)测试。确保代码有效并在部署到生产之前工作是一个好主意,很明显。如果CI构建发现问题,如语法错误或失败的测试,它将不会进行部署。
在我们的操作中,我们将定义一个test-php
作业。Laravel有内置的一些示例测试,我们将运行这些测试。您也可以在这个阶段运行linting和静态分析工具。您可以使用setup-php操作使用特定的PHP版本,或添加其他扩展。
test-php:
name: Test/Lint PHP
runs-on: ubuntu-latest
needs: build-js
steps:
- uses: actions/checkout@v1
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: 7.3 # Use your PHP version
extension-csv: mbstring, bcmath # Setup any required extensions for tests
- name: Composer install
run: composer install
- name: Run Tests
run: ./vendor/bin/phpunit
部署代码
确保我们的代码工作并构建了资产后,我们可以进入实际的部署。
前往您的仓库设置,然后在侧边栏中选择Secrets
选项。
我对GitHub操作的最大抱怨是秘密管理。由于某种原因,您无法编辑秘密,因此要更新任何内容,您都需要删除并重新创建整个秘密。希望他们能在某个时候修复这个问题。
我们需要添加一个私钥,以便部署者可以通过SSH登录服务器。最好为部署创建一个新密钥(最好是在具备最小权限的特定部署用户上创建)。您需要将其添加到变量SSH_PRIVATE_KEY
下。
您还需要将您的Laravel.env
文件添加为DOT_ENV
,以便与代码一起部署(您绝对不应该在git中存储秘密)。
由于每个CI构建/部署都是从全新状态开始,因此容器中的~/.ssh/known_hosts
文件将不会被填充。为了确保没有任何中间人攻击,我们需要将服务器的SSH指纹作为变量SSH_KNOWN_HOSTS
。
您可以通过运行ssh-keyscan rsa -t <服务器IP>
来找到您服务器的主机指纹。
注意到在needs
部分中定义了其他两个作业。此build-js
和test-php
应该异步运行(这在我不一致,通常它们一次运行一个),一旦两者完成,部署将开始。
我们还添加了一个条件if
规则,以确保只部署主分支(我们希望所有分支都运行测试/linting)。
作业首先从build-js
下载编译好的javascript/css,并将其应用于当前工作树。
在我们能够部署之前,我们需要设置SSH(使用提供的私钥启动ssh-agent
,更新known_hosts
)并安装部署者。为了使其简单易用,我已创建一个操作来为您处理所有这些,但您也可以手动运行所有shell命令。
任务最终运行 dep deploy
,它使用 Deployer 来执行零停机部署(我们将在下一节中设置它)。
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [build-js, test-php]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v1
- name: Download build assets
uses: actions/download-artifact@v1
with:
name: assets
path: public
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: 7.3
extension-csv: mbstring, bcmath
- name: Composer install
run: composer install
- name: Setup Deployer
uses: atymic/deployer-php-action@master
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
- name: Deploy to Prod
env:
DOT_ENV: ${{ secrets.DOT_ENV }}
run: dep deploy production --tag=${{ env.GITHUB_REF }} -vvv
整合一切
现在我们已经配置好了所有任务,您的工流程应该类似于下面所示。您可以将此内容复制并粘贴到自己的项目中。
name: CI-CD
on:
push:
branches: master
jobs:
build-js:
name: Build Js/Css
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Yarn Build
run: |
yarn install
yarn prod
cat public/mix-manifest.json # See asset versions in log
- name: Upload build files
uses: actions/upload-artifact@v1
with:
name: assets
path: public
test-php:
name: Test/Lint PHP
runs-on: ubuntu-latest
needs: build-js
steps:
- uses: actions/checkout@v1
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: 7.3 # Use your PHP version
extension-csv: mbstring, bcmath # Setup any required extensions for tests
- name: Composer install
run: composer install
- name: Run Tests
run: ./vendor/bin/phpunit
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [build-js, test-php]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v1
- name: Download build assets
uses: actions/download-artifact@v1
with:
name: assets
path: public
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: 7.3
extension-csv: mbstring, bcmath
- name: Composer install
run: composer install
- name: Setup Deployer
uses: atymic/deployer-php-action@master
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
- name: Deploy to Prod
env:
DOT_ENV: ${{ secrets.DOT_ENV }}
run: dep deploy production --tag=${{ env.GITHUB_REF }} -vvv
设置 Deployer
现在我们已经设置了 CI,是时候在我们的项目中安装 Deployer 并配置它了。
composer require deployer/deployer deployer/recipes
安装完成后,运行 ./vendor/bin/dep init
并按照提示操作,选择 Laravel 作为您的框架。将生成一个 deploy.php
配置文件并将其放置在您项目的根目录中。
默认情况下,Deployer 使用 GIT 进行部署(通过 SSH 连接到服务器,运行 git pull
并执行您的构建步骤),但由于我们将其作为 CI/CD 管道的一部分运行(我们的项目已构建并准备好部署),我们将使用 rsync
将文件直接复制到服务器上。
在代码编辑器中打开您的 deploy.php
文件。将以下代码复制并粘贴到您的 deploy.php
中,位于 hosts 部分之上。
<?php
namespace Deployer;
// Include the Laravel & rsync recipes
require 'recipe/laravel.php';
require 'recipe/rsync.php';
set('application', 'dep-demo');
set('ssh_multiplexing', true); // Speed up deployment
set('rsync_src', function () {
return __DIR__; // If your project isn't in the root, you'll need to change this.
});
// Configuring the rsync exclusions.
// You'll want to exclude anything that you don't want on the production server.
add('rsync', [
'exclude' => [
'.git',
'/.env',
'/storage/',
'/vendor/',
'/node_modules/',
'.github',
'deploy.php',
],
]);
// Set up a deployer task to copy secrets to the server.
// Grabs the dotenv file from the github secret
task('deploy:secrets', function () {
file_put_contents(__DIR__ . '/.env', getenv('DOT_ENV'));
upload('.env', get('deploy_path') . '/shared');
});
接下来,我们需要设置我们的主机。在这个例子中,我们只会使用一个主机,但 Deployer 支持所需的任何数量。将以下代码复制到您的 deploy.php
中,替换现有的 hosts 部分。您需要根据特定的服务器配置自定义它。
// Hosts
host('production.app.com') // Name of the server
->hostname('178.128.84.15') // Hostname or IP address
->stage('production') // Deployment stage (production, staging, etc)
->user('deploy') // SSH user
->set('deploy_path', '/var/www'); // Deploy path
接下来,我们将设置 Deployer 作为我们部署的一部分要执行的步骤。这些可以根据您的需要进行自定义,例如,您可以使用 artisan:horizon:terminate
任务来重启您的 horizon 队列。将以下代码块复制到您的 deploy.php
中,替换 tasks 部分。
after('deploy:failed', 'deploy:unlock'); // Unlock after failed deploy
desc('Deploy the application');
task('deploy', [
'deploy:info',
'deploy:prepare',
'deploy:lock',
'deploy:release',
'rsync', // Deploy code & built assets
'deploy:secrets', // Deploy secrets
'deploy:shared',
'deploy:vendors',
'deploy:writable',
'artisan:storage:link', // |
'artisan:view:cache', // |
'artisan:config:cache', // | Laravel specific steps
'artisan:optimize', // |
'artisan:migrate', // |
'deploy:symlink',
'deploy:unlock',
'cleanup',
]);
配置您的服务器
我们还需要在服务器上修改一些 nginx 或 apache 配置文件。
您希望将您的 web 服务器 root
设置为 deploy_path
(在您的 deploy.php
中设置)+ /current/public
。例如,在我们的情况下,这是 /var/www/current/public
。
您还需要确保部署用户有权写入部署路径。Deployer 的 deploy:writable
任务将确保文件夹具有正确的权限,以便您的 web 服务器用户可以写入它们。
首次部署 😎
一切设置完成后,是时候测试我们的管道了!
提交您的工流程以及 deploy.php
、您的 composer json/lock 文件,并推送!如果一切顺利,您可以去您的 Github 仓库的 Actions
选项卡和观察构建/部署进展。
如果出现问题,请检查 CI 日志。GitHub Actions 的日志有时可能有些晦涩,但只要您的 yaml 结构正确,通常可以很清楚地了解出了什么问题(缺少 SSH 密钥/指纹、无效服务器配置等)。
如果一切顺利,您将部署您的项目的新版本。任何新的提交都将构建和部署,而不会中断您的用户的体验。
总结
本帖概述了一个基本的 CI/CD 管道,但还有很多改进和添加的地方,例如添加预生产环境、发布通知、多服务器部署(用于负载均衡的服务器组)。
希望您喜欢这篇帖子,并且它帮助您改进了构建和部署,或者将现有的迁移到 Github Actions。
如果您有任何问题,请在下面评论,我会尽力回答。
进一步阅读
atymic、rufflesaurus、aharen、hermescortesm、dk009dk、alt、astroboy喜欢这篇文章