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:ListBucketAction才能在对象不存在时得到响应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 无法删除 Amazon S3 对象

原创aws s3 lambda缩略图生成

使用 AWS Lambda (Python 3) 读取存储在 S3 中的 Parquet 文件

AWS CloudFormation:如何从另一个AWS账户为Lambda代码指定存储桶?

使用 python 从 AWS S3 到 PostgreSQL Amazon RDS 的 CSV 文件

AppSync 中的 S3Object GraphQL 类型与 Lambda 数据源?