使用 bref 在 AWS Lambda 中安装部署无服务 Laravel 应用(下篇)
上篇看这里:使用 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.key
和 storage/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
命令。