支持 Laravel.io 的持续开发 →

00 高效 Laravel 部署与 Github Actions

26 Jun, 2020 11 min read

几个月前,我写了一篇文章关于如何使用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>来找到您服务器的主机指纹。

Configuring github actions secrets

注意到在needs部分中定义了其他两个作业。此build-jstest-php应该异步运行(这在我不一致,通常它们一次运行一个),一旦两者完成,部署将开始。

我们还添加了一个条件if规则,以确保只部署主分支(我们希望所有分支都运行测试/linting)。

作业首先从build-js下载编译好的javascript/css,并将其应用于当前工作树。

在我们能够部署之前,我们需要设置SSH(使用提供的私钥启动ssh-agent,更新known_hosts)并安装部署者。为了使其简单易用,我已创建一个操作来为您处理所有这些,但您也可以手动运行所有shell命令。

atymic/deployer-php-action

任务最终运行 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 密钥/指纹、无效服务器配置等)。

如果一切顺利,您将部署您的项目的新版本。任何新的提交都将构建和部署,而不会中断您的用户的体验。

以下是我的测试仓库的首次部署链接,以及包含迁移的链接

Deployment

总结

本帖概述了一个基本的 CI/CD 管道,但还有很多改进和添加的地方,例如添加预生产环境、发布通知、多服务器部署(用于负载均衡的服务器组)。

希望您喜欢这篇帖子,并且它帮助您改进了构建和部署,或者将现有的迁移到 Github Actions。

如果您有任何问题,请在下面评论,我会尽力回答。

进一步阅读

示例仓库 (代码 + 工作流程)

Deployer 文档

Github Actions 文档

上次更新1年前。

atymic、rufflesaurus、aharen、hermescortesm、dk009dk、alt、astroboy喜欢这篇文章

7
喜欢这篇文章吗? 告诉作者并给他们点赞!

你可能还喜欢以下文章

2024年3月11日

如何使用Larastan将Laravel应用从0到9

在应用程序执行之前发现Laravel应用程序中的错误是可能的,得益于Larastan,它是...

阅读文章
2024年7月19日

不使用特质标准化API响应

我注意到大多数用于API响应的库都是使用特质实现的,并且...

阅读文章
2024年7月17日

在您的Laravel项目中通过Discord通知收集反馈

如何在Laravel项目中选择一个反馈模块,并在接收到信息时收到Discord通...

阅读文章

我们感谢这些 令人敬畏的公司 对我们的支持

您的标志在哪里?

Laravel.io

Laravel的问题解决、知识共享和社区建设门户网站。

© 2024 Laravel.io - 版权所有。