编程

使用 bref 在 AWS Lambda 中安装部署无服务 Laravel 应用(下篇)

1285 2022-11-03 10:51:38

上篇看这里:使用 bref 安装部署 无服务 Laravel 应用(上篇)

资源

要部署 Laravel 网站,资源文件需存储在 AWS S3 。最简单的方式是,使用 Lift 插件组成的服务端网站。

这将会部署一个 Cloundfront 作为代理:它会直接从 S3 中获取静态文件,并转发给 Lambda。这一行为非常像传统的网站服务器如 Apache/Nginx,这就意味着你的应用不需要做改变!

首先安装插件

serverless plugin install -n serverless-lift

然后添加这些配置到 serverless.yml 文件。

...
service: laravel

provider:
  ...

plugins:
  - ./vendor/bref/bref
  - serverless-lift

functions:
  ...

constructs:
  website:
    type: server-side-website
    assets:
      '/js/*': public/js
      '/css/*': public/css
      '/favicon.ico': public/favicon.ico
      '/robots.txt': public/robots.txt
      # add here any file or directory that needs to be served from S3
    # Laravel uses some headers that are not in CloudFront's default whitelist.
    # To add any, we need to list all accepted headers to pass through.
    # https://github.com/getlift/lift/blob/master/docs/server-side-website.md#forwarded-headers
    forwardedHeaders:
      - Accept
      - Accept-Language
      - Content-Type
      - Origin
      - Referer
      - User-Agent
      - X-Forwarded-Host
      - X-Requested-With
      # Laravel Framework Headers
      - X-Csrf-Token
      # Other Headers (e.g. Livewire, Laravel Nova), uncomment based on your needs
      # - X-Livewire
      # - X-Inertia

注意:AWS 的 forwardedHeader 限制被设置成 10

部署之前,使用 Laravel Mix 编译资源。

npm run prod

现在使用 serverless 部署网站。Lift 将会创建所有必须的资源,然后自动帮你上传资源到 S3 中。

模板中的资源

模板中引用的资源,应该通过 asset() 辅助函数来实现:

<script src="{{ asset('js/app.js') }}"></script>

如果你的模板通过直接路径引用了一些资源,你应该修改并使用 asset() 帮助函数:

- <img src="/images/logo.png">
+ <img src="{{ asset('images/logo.png') }}">

存储于 S3 上的文件

Laravel 有一个文件分发系统,让我们可以轻松修改文件存储位置。在 Lambda 运行时,需要使用 S3 适配器,以便在 AWS S3 上存储文件。配置生产环境的 .env 文件:

# .env
FILESYSTEM_DRIVER=s3

接下来,我们要通过 serverless.yml 创建 bucket:

...

provider:
    ...
    environment:
        # environment variable for Laravel
        AWS_BUCKET: !Ref Storage
    iam:
        role:
            statements:
                # Allow Lambda to read and write files in the S3 buckets
                -   Effect: Allow
                    Action: s3:*
                    Resource:
                        - !Sub '${Storage.Arn}' # the storage bucket
                        - !Sub '${Storage.Arn}/*' # and everything inside

resources:
    Resources:
        Storage:
            Type: AWS::S3::Bucket

Laravel 默认并没有相关的配置项。你需要在 config/filesystems.php 文件中添加这些配置项:

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+           'token' => env('AWS_SESSION_TOKEN'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
        ],

就是这样。 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY' 和 'AWS_SESSION_TOKEN' 变量在 AWS Lambda 自动定义了,你无需重新去定义它们。

public 文件

Laravel 有一个 public 磁盘:用来存储需要公开的文件,比如上传的图像,生产的 PDF 文件等。

这些文件无法在 Lambda 上存储, 比如,它们不能存入到默认的 storage/app/public 目录中,你需要将它们存到 S3 上。

不要在 AWS Lambda 上运行 php artisan storage:link — 这是无用的并且会失败,因为 Lambda 中的文件系统是只读的

要将公共文件存入到 S3, 可以在代码中修改 disk:

- Storage::disk('public')->put('avatars/1', $fileContents);
+ Storage::disk('s3')->put('avatars/1', $fileContents);

不过这种做法会让你的本地应用不能正常工作。更好的方案是,让你的 pulic 磁盘变得可配置。修改 config/filesystems.php 中的配置文件:

    /*
    |--------------------------------------------------------------------------
    | Default Public Filesystem Disk
    |--------------------------------------------------------------------------
    */

+   'public' => env('FILESYSTEM_DRIVER_PUBLIC', 'public_local'),

    ...

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

-        'public' => [
+        'public_local' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'token' => env('AWS_SESSION_TOKEN'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
        ],

+        's3_public' => [
+            'driver' => 's3',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'token' => env('AWS_SESSION_TOKEN'),
+            'region' => env('AWS_DEFAULT_REGION'),
+            'bucket' => env('AWS_PUBLIC_BUCKET'),
+            'url' => env('AWS_URL'),
+        ],

    ],

现在可以修改生产环境的 .env 配置文件,将 public 磁盘改为 S3:

FILESYSTEM_DRIVER=s3
FILESYSTEM_DRIVER_PUBLIC=s3

Laravel 队列

使用 Amazaon SQS 可以在 AWS Lambda 上运行 Laravel 队列。

有一个专用的 Bref 包:bref/laravel-bridge.

Laravel Passport

Laravel Passport 有一个 passport:install 命令。不过,该命令无法在 Lambda 上运行,因为它会在 storage/ 目录中写入文件。

因此,我们应该这样做:

在本地运行 php artisan passport:keys , 生成 key 文件。

这个命令会生成 storage/oauth-private.keystorage/oauth-public.key 文件,我们需要将它们部署上去.

取决于你如何部署应用(从自己的机器部署, 或者通过 CI), 你可能需要在 serverless.yml 中加入白名单:

 package:
      patterns:
          - ...
          # Exclude the 'storage' directory
          - '!storage/**'
          # Except the public and private keys required by Laravel Passport
          - 'storage/oauth-private.key'
          - 'storage/oauth-public.key'

现在你可以部署应用了:

serverless deploy

最后, 你可以创建 token (这是 passport:install 命令的第二个动作):

vendor/bin/bref cli <artisan-function-name> -- passport:client --personal --name 'Laravel Personal Access Client'
vendor/bin/bref cli <artisan-function-name> -- passport:client --password --name 'Laravel Personal Access Client'

以上步骤用来代替运行 passport:install 命令。