AWS Lambda 中的 Amazon S3 waitFor()
Posted
技术标签:
【中文标题】AWS Lambda 中的 Amazon S3 waitFor()【英文标题】:Amazon S3 waitFor() inside AWS Lambda 【发布时间】:2019-01-11 00:37:01 【问题描述】:从 Lambda 函数(无服务器 nodejs)内部调用 S3.waitFor()
函数时遇到问题。我正在尝试使用 S3.putObject()
从一个休息 api 将文件异步写入 Amazon S3,并使用 S3.waitFor()
从另一个休息 api 轮询结果文件以查看写入是否准备好/完成。
请看下面的sn-p:
...
S3.waitFor('objectExists',
Bucket: bucketName,
Key: fileName,
$waiter:
maxAttempts: 5,
delay: 3
, (error, data) =>
if (error)
console.log("error:" + JSON.stringify(error))
else
console.log("Success")
);
...
给定 valid bucketName 和 invalid fileName,当代码在我的本地测试脚本中运行时,它会在 15 秒(3 秒 x 5 次重试)后返回错误并生成如下结果:
error:
"message": "Resource is not in the state objectExists",
"code": "ResourceNotReady",
"region": null,
"time": "2018-08-03T06:08:12.276Z",
"requestId": "AD621033DCEA7670",
"extendedRequestId": "JNkxddWX3IZfauJJ63SgVwyv5nShQ+Mworb8pgCmb1f/cQbTu3+52aFuEi8XGro72mJ4ik6ZMGA=",
"retryable": true,
"statusCode": 404,
"retryDelay": 3000
同时,当它在 AWS lambda 函数内部运行时,它直接返回结果如下:
error:
"message": "Resource is not in the state objectExists",
"code": "ResourceNotReady",
"region": null,
"time": "2018-08-03T05:49:43.178Z",
"requestId": "E34D731777472663",
"extendedRequestId": "ONMGnQkd14gvCfE/FWk54uYRG6Uas/hvV6OYeiax5BTOCVwbxGGvmHxMlOHuHPzxL5gZOahPUGM=",
"retryable": false,
"statusCode": 403,
"retryDelay": 3000
如您所见,retryable 和 statusCode 值在两者之间是不同的。
在lamba上,当文件不存在时,它似乎总是得到statusCode 403。在我本地时,一切正常(每 3 秒重试 5 次并收到 statusCode 404)。
我想知道这是否与 IAM 角色有关。这是我的 serverless.yml 中的 IAM 角色声明设置:
iamRoleStatements:
- Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "ec2:CreateNetworkInterface"
- "ec2:DescribeNetworkInterfaces"
- "ec2:DeleteNetworkInterface"
- "sns:Publish"
- "sns:Subscribe"
- "s3:*"
Resource: "*"
如何通过 lambda 函数使其工作? 提前谢谢!
【问题讨论】:
你的bucket和lambda函数在同一个区域吗? @MikePatrick 是的,它在同一个地区。顺便说一句,我找到了解决这个问题的方法。它与 S3.headObject() 函数有关。我很快就会发布解决方案。感谢您的努力。 【参考方案1】:事实证明,关键在于如何为存储桶及其下的所有对象设置 IAM 角色。
根据 AWS 文档 here,它指出 S3.waitFor()
依赖于底层 S3.headObject()
。
通过每 5 秒(最多 20 次)定期调用底层 S3.headObject() 操作来等待 objectExists 状态。
同时,S3.headObject()
本身依赖于 HEAD Object API,该 API 具有 AWS Docs here 中所述的以下规则:
您需要 s3:GetObject 权限才能执行此操作。有关更多信息,请参阅 Amazon Simple Storage Service 开发人员指南中的在策略中指定权限。 如果您请求的对象不存在,Amazon S3 返回的错误取决于您是否还拥有 s3:ListBucket 权限。
如果您对存储桶拥有 s3:ListBucket 权限,Amazon S3 将返回 HTTP 状态代码 404 ("no such key") 错误。 如果您没有 s3:ListBucket 权限,Amazon S3 将返回 HTTP 状态代码 403(“拒绝访问”) 错误。
这意味着我需要在包含对象的Bucket资源中添加s3:ListBucket
Action才能在对象不存在时得到响应404。
因此,我配置了 cloudformation AWS::IAM::Policy 资源,如下所示,我在存储桶本身(即:S3FileStorageBucket)上专门添加了s3:Get*
和s3:List*
操作。
"IamPolicyLambdaExecution":
"Type": "AWS::IAM::Policy",
"DependsOn": [
"IamRoleLambdaExecution",
"S3FileStorageBucket"
],
"Properties":
"PolicyName": "Fn::Join": ["-", ["Live-RolePolicy", "Ref": "environment"]],
"PolicyDocument":
"Version": "2012-10-17",
"Statement": [
"Effect":"Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource":
"Fn::Join": [
"",
[
"arn:aws:s3:::",
"Ref": "S3FileStorageBucket"
]
]
,
"Effect":"Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource":
"Fn::Join": [
"",
[
"arn:aws:s3:::",
"Ref": "S3FileStorageBucket"
,
"/*"
]
]
,
...
现在我已经能够通过 S3.waitFor()
仅通过一个 API 调用轮询存储桶下的文件/对象,并仅在它准备好时获取结果,或者在特定资源未准备好时抛出错误超时。
这样,客户端实现会简单得多。因为它不必自己实现民意调查。
希望有人觉得它有用。谢谢。
【讨论】:
另外,您可以通过在上面S3.waitFor()
的第二个参数上传递未记录的$waiter
属性来玩弄轮询周期。以上是关于AWS Lambda 中的 Amazon S3 waitFor()的主要内容,如果未能解决你的问题,请参考以下文章
使用 AWS Lambda (Python 3) 读取存储在 S3 中的 Parquet 文件
AWS CloudFormation:如何从另一个AWS账户为Lambda代码指定存储桶?