stechstudio / laravel-bref-bridge
Bref,Laravel的方式。
Requires
- php: ^7.2
- ext-curl: *
- ext-fileinfo: *
- ext-iconv: *
- ext-json: *
- ext-mbstring: *
- ext-openssl: *
- ext-parallel: *
- ext-pcre: *
- ext-simplexml: *
- ext-tokenizer: *
- bref/bref: ^0.3.4
- gisostallenberg/file-permission-calculator: ^1.0
- illuminate/http: ^6.0|^7.0
- illuminate/support: ^6.0|^7.0
- laravel/framework: ^6.0|^7.0
- psr/http-server-handler: ^1.0
- stechstudio/aws-events: ^1.0
- symfony/psr-http-message-bridge: ^1.1
- symfony/yaml: ^4.2
Requires (Dev)
- phpstan/phpstan: ^0.11.2
- phpstan/phpstan-doctrine: ^0.11.1
This package is auto-updated.
Last update: 2020-09-10 13:19:02 UTC
README
如果您正在寻找一种轻松部署Laravel项目到AWS Lambda的方法,那么您就找到了正确的位置!
基于出色的bref项目,我们提供了一种简单便捷的方式进入AWS的Serverless Laravel世界。
安装
假设您已经在现有的Laravel项目中,让我们通过Composer安装桥接器。
$ composer require stechstudio/laravel-bref-bridge
需要发布一个路由、配置文件和AWS SAM模板。
$ artisan vendor:publish --tag=bref-routes --tag=bref-sam-template --tag=bref-configuration
配置
TL;DR
编辑.env
BREF_NAME="<my-lambdas-name>" BREF_S3_BUCKET="<bucket-name>"
$ artisan vendor:publish --tag=bref-sam-template --tag=bref-configuration $ artisan bref:config-sam $ artisan vendor:publish --tag=bref-routes $ mv routes/lambda.example.php routes/lambda.php $ artisan bref:package $ artisan bref:deploy
AWS
您需要一个S3存储桶来发送函数包,以便Cloudformation可以消费它。您可以使用现有的存储桶或创建一个新的存储桶。您可以使用AWS CLI轻松创建一个新的存储桶,如下所示。
$ aws s3 mb s3://<bucket-name>
.env
修改您的.env
文件并添加以下内容:
BREF_NAME="<my-lambdas-name>" BREF_S3_BUCKET="<bucket-name>"
区域层
虽然已经为您配置了默认的us-east-1层,但最好参考https://bref.sh/docs/runtimes/,并找到您打算部署Lambda函数的区域中最新的bref层的ARN。
请注意,当您选择基本层时,我们需要一个php-??-fpm
层。此桥接器不兼容php-??
或console
层类型。
确保区域和层匹配,如下所示
BREF_DEFAULT_REGION=ap-southeast-1 BREF_FUNCTION_LAYER_1=arn:aws:lambda:ap-southeast-1:209497400698:layer:php-73-fpm:6
SQS作业队列
我们将报告创建的默认作业队列。函数将配置为接收来自它的事件以及写入它。这意味着当您向默认队列发送作业时,它将触发相同的Lambda函数来处理作业。
SAM模板
$ artisan vendor:publish --tag=bref-sam-template
您现在可以在您的根目录中找到template.yml
文件,您可以打开它,查看它,编辑它,或者现在先忽略它。完成之后,让我们运行配置命令。这将根据您的.env
文件生成最终的模板。如果您在.env
中进行了任何修改,您应该再次运行此命令以更新模板。
$ artisan bref:config-sam
Lambda路由
什么是Lambda路由?很高兴您问!许多人只关注来自API Gateway和/或AWS SQS的事件来触发他们的Lambda作业。然而,还有一系列可能配置为触发您的Lambda函数的事件。
我们为Laravel实现了一个路由器,它使得您的应用程序能够轻松消费和响应多个触发器的事件,全部在单个Lambda函数中完成。我们使用AWS Events Package将传入的事件转换为适当的PHP对象,然后确定将事件发送到哪个控制器。
API网关
所有API网关代理请求事件都被硬编码为任何正常网络请求的处理方式。事件将被转换为HTTP请求,并像nginx或apache一样传递给PHP-FPM。然后,结果将转换回适当的API网关代理响应,并送回网关。对于这种情况,您只需像为任何传统的Laravel应用程序一样编写HTTP路由和控制器即可,如果我们的工作做得正确,它应该会顺利运行!
其他事件
除了API网关之外,我们目前支持路由所有(十六个)其他可能的事件。如果您没有使用其他事件,您可以简单地忽略这一部分。然而,对于那些超越API网关的人来说,让我们发布示例路由文件。
$ artisan vendor:publish --tag=bref-routes
这将导致在您的项目中放置一个routes/lambda.example.php
文件。在使用之前,您需要将其手动重命名为routes/lambda.php
。当您查看它时,您会注意到它遵循与HTTP路由相同的范式。您可以映射一个回调或映射一个Lambda控制器。
然后,路由器将确保当出现您正在路由的事件类型时,它会被传递到适当的callable
以处理该事件。您只需在完成后返回一个array
即可。为了帮助您测试各种事件的路线,这里有一些示例。
AWS CloudFormation创建请求示例事件
{ "StackId": "arn:aws:cloudformation:us-west-2:EXAMPLE/stack-name/guid", "ResponseURL": "http://pre-signed-S3-url-for-response", "ResourceProperties": { "StackName": "stack-name", "List": [ "1", "2", "3" ] }, "RequestType": "Create", "ResourceType": "Custom::TestResource", "RequestId": "unique id for this create request", "LogicalResourceId": "MyTestResource" }
Amazon SES接收电子邮件示例事件
{ "Records": [ { "eventVersion": "1.0", "ses": { "mail": { "commonHeaders": { "from": [ "Jane Doe <janedoe@example.com>" ], "to": [ "johndoe@example.com" ], "returnPath": "janedoe@example.com", "messageId": "<0123456789example.com>", "date": "Wed, 7 Oct 2015 12:34:56 -0700", "subject": "Test Subject" }, "source": "janedoe@example.com", "timestamp": "1970-01-01T00:00:00.000Z", "destination": [ "johndoe@example.com" ], "headers": [ { "name": "Return-Path", "value": "<janedoe@example.com>" }, { "name": "Received", "value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.us-west-2.amazonaws.com with SMTP id o3vrnil0e2ic for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)" }, { "name": "DKIM-Signature", "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=example; h=mime-version:from:date:message-id:subject:to:content-type; bh=jX3F0bCAI7sIbkHyy3mLYO28ieDQz2R0P8HwQkklFj4=; b=sQwJ+LMe9RjkesGu+vqU56asvMhrLRRYrWCbV" }, { "name": "MIME-Version", "value": "1.0" }, { "name": "From", "value": "Jane Doe <janedoe@example.com>" }, { "name": "Date", "value": "Wed, 7 Oct 2015 12:34:56 -0700" }, { "name": "Message-ID", "value": "<0123456789example.com>" }, { "name": "Subject", "value": "Test Subject" }, { "name": "To", "value": "johndoe@example.com" }, { "name": "Content-Type", "value": "text/plain; charset=UTF-8" } ], "headersTruncated": false, "messageId": "o3vrnil0e2ic28tr" }, "receipt": { "recipients": [ "johndoe@example.com" ], "timestamp": "1970-01-01T00:00:00.000Z", "spamVerdict": { "status": "PASS" }, "dkimVerdict": { "status": "PASS" }, "processingTimeMillis": 574, "action": { "type": "Lambda", "invocationType": "Event", "functionArn": "arn:aws:lambda:us-west-2:012345678912:function:Example" }, "spfVerdict": { "status": "PASS" }, "virusVerdict": { "status": "PASS" } } }, "eventSource": "aws:ses" } ] }
计划事件示例事件
{ "account": "123456789012", "region": "us-east-1", "detail": {}, "detail-type": "Scheduled Event", "source": "aws.events", "time": "1970-01-01T00:00:00Z", "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", "resources": [ "arn:aws:events:us-east-1:123456789012:rule/my-schedule" ] }
Amazon CloudWatch日志示例事件
{ "awslogs": { "data": "H4sIAAAAAAAAAHWPwQqCQBCGX0Xm7EFtK+smZBEUgXoLCdMhFtKV3akI8d0bLYmibvPPN3wz00CJxmQnTO41whwWQRIctmEcB6sQbFC3CjW3XW8kxpOpP+OC22d1Wml1qZkQGtoMsScxaczKN3plG8zlaHIta5KqWsozoTYw3/djzwhpLwivWFGHGpAFe7DL68JlBUk+l7KSN7tCOEJ4M3/qOI49vMHj+zCKdlFqLaU2ZHV2a4Ct/an0/ivdX8oYc1UVX860fQDQiMdxRQEAAA==" } }
Amazon SNS示例事件
{ "Records": [ { "EventVersion": "1.0", "EventSubscriptionArn": eventsubscriptionarn, "EventSource": "aws:sns", "Sns": { "SignatureVersion": "1", "Timestamp": "1970-01-01T00:00:00.000Z", "Signature": "EXAMPLE", "SigningCertUrl": "EXAMPLE", "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", "Message": "Hello from SNS!", "MessageAttributes": { "Test": { "Type": "String", "Value": "TestString" }, "TestBinary": { "Type": "Binary", "Value": "TestBinary" } }, "Type": "Notification", "UnsubscribeUrl": "EXAMPLE", "TopicArn": topicarn, "Subject": "TestInvoke" } } ] }
Amazon DynamoDB更新示例事件
{ "Records": [ { "eventID": "1", "eventVersion": "1.0", "dynamodb": { "Keys": { "Id": { "N": "101" } }, "NewImage": { "Message": { "S": "New item!" }, "Id": { "N": "101" } }, "StreamViewType": "NEW_AND_OLD_IMAGES", "SequenceNumber": "111", "SizeBytes": 26 }, "awsRegion": "us-west-2", "eventName": "INSERT", "eventSourceARN": eventsourcearn, "eventSource": "aws:dynamodb" }, { "eventID": "2", "eventVersion": "1.0", "dynamodb": { "OldImage": { "Message": { "S": "New item!" }, "Id": { "N": "101" } }, "SequenceNumber": "222", "Keys": { "Id": { "N": "101" } }, "SizeBytes": 59, "NewImage": { "Message": { "S": "This item has changed" }, "Id": { "N": "101" } }, "StreamViewType": "NEW_AND_OLD_IMAGES" }, "awsRegion": "us-west-2", "eventName": "MODIFY", "eventSourceARN": sourcearn, "eventSource": "aws:dynamodb" }, { "eventID": "3", "eventVersion": "1.0", "dynamodb": { "Keys": { "Id": { "N": "101" } }, "SizeBytes": 38, "SequenceNumber": "333", "OldImage": { "Message": { "S": "This item has changed" }, "Id": { "N": "101" } }, "StreamViewType": "NEW_AND_OLD_IMAGES" }, "awsRegion": "us-west-2", "eventName": "REMOVE", "eventSourceARN": sourcearn, "eventSource": "aws:dynamodb" } ] }
Amazon Cognito Sync触发器示例事件
{ "datasetName": "datasetName", "eventType": "SyncTrigger", "region": "us-east-1", "identityId": "identityId", "datasetRecords": { "SampleKey2": { "newValue": "newValue2", "oldValue": "oldValue2", "op": "replace" }, "SampleKey1": { "newValue": "newValue1", "oldValue": "oldValue1", "op": "replace" } }, "identityPoolId": "identityPoolId", "version": 2 }
Amazon Kinesis数据流示例事件
{ "Records": [ { "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", "eventVersion": "1.0", "kinesis": { "partitionKey": "partitionKey-3", "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", "kinesisSchemaVersion": "1.0", "sequenceNumber": "49545115243490985018280067714973144582180062593244200961" }, "invokeIdentityArn": identityarn, "eventName": "aws:kinesis:record", "eventSourceARN": eventsourcearn, "eventSource": "aws:kinesis", "awsRegion": "us-east-1" } ] }
Amazon S3放置示例事件
{ "Records": [ { "eventVersion": "2.0", "eventTime": "1970-01-01T00:00:00.000Z", "requestParameters": { "sourceIPAddress": "127.0.0.1" }, "s3": { "configurationId": "testConfigRule", "object": { "eTag": "0123456789abcdef0123456789abcdef", "sequencer": "0A1B2C3D4E5F678901", "key": "HappyFace.jpg", "size": 1024 }, "bucket": { "arn": bucketarn, "name": "sourcebucket", "ownerIdentity": { "principalId": "EXAMPLE" } }, "s3SchemaVersion": "1.0" }, "responseElements": { "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", "x-amz-request-id": "EXAMPLE123456789" }, "awsRegion": "us-east-1", "eventName": "ObjectCreated:Put", "userIdentity": { "principalId": "EXAMPLE" }, "eventSource": "aws:s3" } ] }
Amazon S3删除示例事件
{ "Records": [ { "eventVersion": "2.0", "eventTime": "1970-01-01T00:00:00.000Z", "requestParameters": { "sourceIPAddress": "127.0.0.1" }, "s3": { "configurationId": "testConfigRule", "object": { "sequencer": "0A1B2C3D4E5F678901", "key": "HappyFace.jpg" }, "bucket": { "arn": bucketarn, "name": "sourcebucket", "ownerIdentity": { "principalId": "EXAMPLE" } }, "s3SchemaVersion": "1.0" }, "responseElements": { "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", "x-amz-request-id": "EXAMPLE123456789" }, "awsRegion": "us-east-1", "eventName": "ObjectRemoved:Delete", "userIdentity": { "principalId": "EXAMPLE" }, "eventSource": "aws:s3" } ] }
Amazon Lex示例事件
{ "messageVersion": "1.0", "invocationSource": "FulfillmentCodeHook or DialogCodeHook", "userId": "user-id specified in the POST request to Amazon Lex.", "sessionAttributes": { "key1": "value1", "key2": "value2", }, "bot": { "name": "bot-name", "alias": "bot-alias", "version": "bot-version" }, "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request", "currentIntent": { "name": "intent-name", "slots": { "slot-name": "value", "slot-name": "value", "slot-name": "value" }, "confirmationStatus": "None, Confirmed, or Denied (intent confirmation, if configured)" } }
Amazon SQS事件
{ "Records": [ { "messageId": "c80e8021-a70a-42c7-a470-796e1186f753", "receiptHandle": "AQEBJQ+/u6NsnT5t8Q/VbVxgdUl4TMKZ5FqhksRdIQvLBhwNvADoBxYSOVeCBXdnS9P+erlTtwEALHsnBXynkfPLH3BOUqmgzP25U8kl8eHzq6RAlzrSOfTO8ox9dcp6GLmW33YjO3zkq5VRYyQlJgLCiAZUpY2D4UQcE5D1Vm8RoKfbE+xtVaOctYeINjaQJ1u3mWx9T7tork3uAlOe1uyFjCWU5aPX/1OHhWCGi2EPPZj6vchNqDOJC/Y2k1gkivqCjz1CZl6FlZ7UVPOx3AMoszPuOYZ+Nuqpx2uCE2MHTtMHD8PVjlsWirt56oUr6JPp9aRGo6bitPIOmi4dX0FmuMKD6u/JnuZCp+AXtJVTmSHS8IXt/twsKU7A+fiMK01NtD5msNgVPoe9JbFtlGwvTQ==", "body": "{\"foo\":\"bar\"}", "attributes": { "ApproximateReceiveCount": "3", "SentTimestamp": "1529104986221", "SenderId": "594035263019", "ApproximateFirstReceiveTimestamp": "1529104986230" }, "messageAttributes": {}, "md5OfBody": "9bb58f26192e4ba00f01e2e7b136bbd8", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-west-2:594035263019:NOTFIFOQUEUE", "awsRegion": "us-west-2" } ] }
CloudFront事件
{ "Records": [ { "cf": { "config": { "distributionId": "EDFDVBD6EXAMPLE" }, "request": { "clientIp": "2001:0db8:85a3:0:0:8a2e:0370:7334", "method": "GET", "uri": "/picture.jpg", "headers": { "host": [ { "key": "Host", "value": "d111111abcdef8.cloudfront.net" } ], "user-agent": [ { "key": "User-Agent", "value": "curl/7.51.0" } ] } } } } ] }
AWS Config事件
{ "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-02-17T01:36:34.043Z\",\"awsAccountId\":\"000000000000\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"ARN\":\"arn:aws:ec2:us-east-1:000000000000:instance/i-00000000\",\"awsRegion\":\"us-east-1\",\"availabilityZone\":\"us-east-1a\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"Foo\":\"Bar\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"foo\":\"bar\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}", "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", "resultToken": "myResultToken", "eventLeftScope": false, "executionRoleArn": "arn:aws:iam::012345678912:role/config-role", "configRuleArn": "arn:aws:config:us-east-1:012345678912:config-rule/config-rule-0123456", "configRuleName": "change-triggered-config-rule", "configRuleId": "config-rule-0123456", "accountId": "012345678912", "version": "1.0" }
AWS IoT按钮事件
{ "serialNumber": "ABCDEFG12345", "clickType": "SINGLE", "batteryVoltage": "2000 mV" }
Kinesis数据Firehose事件
{ "invocationId": "invoked123", "deliveryStreamArn": "aws:lambda:events", "region": "us-west-2", "records": [ { "data": "SGVsbG8gV29ybGQ=", "recordId": "record1", "approximateArrivalTimestamp": 1510772160000, "kinesisRecordMetadata": { "shardId": "shardId-000000000000", "partitionKey": "4d1ad2b9-24f8-4b9d-a088-76e9947c317a", "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z", "sequenceNumber": "49546986683135544286507457936321625675700192471156785154", "subsequenceNumber": "" } }, { "data": "SGVsbG8gV29ybGQ=", "recordId": "record2", "approximateArrivalTimestamp": 151077216000, "kinesisRecordMetadata": { "shardId": "shardId-000000000001", "partitionKey": "4d1ad2b9-24f8-4b9d-a088-76e9947c318a", "approximateArrivalTimestamp": "2012-04-23T19:25:43.511Z", "sequenceNumber": "49546986683135544286507457936321625675700192471156785155", "subsequenceNumber": "" } } ] }
在您将包发布到lambda之后,您可以前往AWS控制台,将各种示例复制粘贴到测试中并手动运行它们。如果您想从AWS CLI中进行测试,也可以这样做。
打包与部署
我们使这一过程尽可能简单。
$ artisan bref:package
这将生成一个storage/latest.zip
包,包含您的当前代码。这里不会有来自composer的开发包,所以如果您需要这些包,您需要将它们移动到所需的段落。
$ artisan bref:deploy
几分钟后,任务将完成,您可以前往AWS控制台查看您的新Lambda作业。
就这样,恭喜您!
配置选项
config/bref.php
文件包含我们的配置选项。这是可用设置的概述。有关更多详细信息和默认值,请参阅文件中的注释。
- 名称 - 此值是您的 Lambda 名称。当框架需要生成 Lambda 函数名称时使用此值。
- 描述 - 此值是您的 Lambda 描述。当框架需要生成 Lambda 函数描述时使用此值。
- 区域 - 此值是您的 Lambda 区域。当框架需要生成 Lambda 函数区域时使用此值。
- 超时时间 - 此值是配置 Lambda 函数的超时时间(以秒为单位)。API Gateway 超时时间为 30 秒,因此这是默认值。最大超时时间为 900 秒(15 分钟)。
- 内存大小 - 函数可访问的内存量。增加函数的内存也会增加其 CPU 配额。默认值为 128 MB,最大值为 3,008 MB。此值必须是 64 MB 的整数倍。
- 层 - 要添加到函数执行环境的函数层列表。指定每个层时,请按它们应堆叠的顺序,使用 ARN(包括版本),最多五层。
- 保留 - 在文件系统中保留的最新(zip)包的数量。
- SQS - Lambda 消耗作业队列在此配置。向队列发布作业仍然按正常工作,所以这里没有变化。只需更新 .env 即可。
- 打包 - 此数组配置在打包您的应用程序时应忽略的文件,以及标识可执行文件。
- 环境变量 - 此数组配置传递给函数代码的环境变量,并列出那些被忽略的变量。
.env 变量
以下 .env
变量可供使用。有关更多详细信息,请参考 config/bref.php
。
- BREF_NAME - 此值是您的 Lambda 名称。当框架需要生成 Lambda 函数名称时使用此值。
- BREF_DESCRIPTION - 此值是您的 Lambda 描述。当框架需要生成 Lambda 函数描述时使用此值。
- BREF_DEFAULT_REGION - 此值是您的 Lambda 区域。当框架需要生成 Lambda 函数区域时使用此值。
- BREF_FUNCTION_TIMEOUT - 此值是配置 Lambda 函数的超时时间(以秒为单位)。API Gateway 超时时间为 30 秒,因此这是默认值。最大超时时间为 900 秒(15 分钟)。
- BREF_FUNCTION_MEMORY_SIZE - 函数可访问的内存量。增加函数的内存也会增加其 CPU 配额。默认值为 128 MB,最大值为 3,008 MB。此值必须是 64 MB 的整数倍。
- BREF_FUNCTION_LAYER_1 - 第一个层的 ARN(包括版本)。这将覆盖默认层。
- BREF_FUNCTION_LAYER_2 - 第二个层的 ARN(包括版本),如果使用的话。
- BREF_FUNCTION_LAYER_3 - 第三个层的 ARN(包括版本),如果使用的话。
- BREF_FUNCTION_LAYER_4 - 第四个层的 ARN(包括版本),如果使用的话。
- BREF_FUNCTION_LAYER_5 - 第五个层的 ARN(包括版本),如果使用的话。
- BREF_PACKAGE_KEEP - 在文件系统中保留的最新(zip)包的数量。
- BREF_APP_STORAGE - Laravel 应用存储的位置。默认为
/tmp/storage
。 - BREF_LOG_CHANNEL - 在 Lambda 中处理日志的方式。默认为
stderr
。 - BREF_CACHE_DRIVER - 在 Lambda 中使用的缓存驱动程序。默认为
file
。 - BREF_SESSION_DRIVER - 在 Lambda 中使用的 Session 驱动程序。默认为
array
。 - BREF_QUEUE_CONNECTION - 在 Lambda 中使用的队列连接。默认为
sqs
。
启用扩展
如果您想启用扩展或修改php.ini指令,可以通过创建一个./storage/php/conf.d
目录来实现。您放入该目录中的任何内容都将被打包到/var/task/php/config.d
,这是bref的默认设置。
例如,要启用来自基础bref层的pdo_mysql
,只需创建一个类似的./storage/php/conf.d/mysql.ini
文件
extension=pdo_mysql
参见:https://bref.sh/docs/environment/php.html#customizing-phpini https://bref.sh/docs/environment/php.html#extensions-installed-but-disabled-by-default