我无法使用 Lambda 将文件从 S3 下载到 EFS

asm*_*dkj 3 amazon-s3 amazon-web-services boto3 aws-lambda amazon-efs

我将 Lambda 连接到 EFS,我想使用 Lambda 将小文件从 S3 下载到 EFS。

我将 Lambda 函数连接到文件系统,并添加了权限为 777 的访问点。

我在 Lambda 中有这个小的 python 函数可以下载到挂载:

import json
import boto3
import time
import os

s3 = boto3.client('s3')

def lambda_handler(event, context):
    s3.download_file('my_bucket', 'img.jpg', '/mnt/my-efs/img.jpg')
    return{
        "statusCode": 200
    }
Run Code Online (Sandbox Code Playgroud)

我在 1 分钟后超时(这应该不到一秒钟)。

如果我为 下载/tmp/img.jpg,这有效。即使我将文件从复制/tmp//mnt/my-efs/该工程。

我为 Lambda 用户提供了这个 IAM:

AmazonEC2FullAccess
AmazonS3FullAccess
AmazonVPCFullAccess
AWSLambdaVPCAccessExecutionRole
AmazonElasticFileSysstemClientReadWriteAccess
AmazonElasticFileSysstemClientFullAccess
Run Code Online (Sandbox Code Playgroud)

为什么我不能使用 Lambda 从 S3 下载文件到 EFS?

Tob*_*Rex 5

这是一个很大的棘手问题,所以让我们慢慢来,确保我们涵盖了所有的运动部件。如果其中一些是多余的,我深表歉意,但我想确保外行人拥有所有正确的部分。如果您认为我遗漏了什么,请告诉我。

设置:

  1. serverless.yaml
  2. AWS 接入点设置
  3. Lambda 添加 EFS
  4. 将项目下载到 EFS 驱动器
  5. 验证您的文件在 EFS 中

1.serverless.yaml 我使用的是无服务器,所以这就是我的serverless.yaml文件的样子。

iamRoleStatements:
    - Effect: Allow
          Action:
          - elasticfilesystem:ClientMount
          - elasticfilesystem:ClientWrite
          - elasticfilesystem:ClientRootAccess
          Resource:
          - arn:aws:elasticfilesystem:us-west-2:123456789:file-system/fs-123456789
Run Code Online (Sandbox Code Playgroud)

这些是我将文件从 S3 下载到 EFS 驱动器位置所需的最低权限。同样,该项目使用无服务器,因此如果您不使用服务器,所有这些都可以转换为 cloudFormation 模板。

在处理程序函数下......

functions:
  myFunc:
    handler: handler.handle
    package:
      exclude:
        - env/**
        - node_modules/**
    description: Lambda to download S3 file to SFTP EFS folder.
    events:
        - s3:
          bucket: some-bucket
          event: s3:ObjectCreated:*
          rules:
            - prefix: some-folder/
            - suffix: .jpg
          existing: true
    fileSystemConfig:
      localMountPath: /mnt/my-efs
      arn: arn:aws:elasticfilesystem:us-west-2:123456789:access-point/fsap-12345678910
Run Code Online (Sandbox Code Playgroud)

注意:我从我的serverless.yaml文件中遗漏了很多,只包含了与 EFS 设置有关的部分。

2. AWS EFS 接入点设置

    1. 转到 EFS 服务。
    1. 单击接入点。
    1. 创建新接入点
    1. 根据需要填写字段。以下是我如何配置我的
    • 细节
      • File System: 选择一个下拉选项
      • Name - optional 取一个名字
      • Root directory path - optional:这将映射到 EC2 或 SFTP 服务器上的底层文件系统。我使用的是 sftp 服务器,所以我只需在 sftp 服务器上输入地址。sftp.companyX.com/images. 此位置将映射到Local Mount PathEFS 配置窗口中您的 Lambda 设置中的设置(我们将在步骤 3 中详细介绍)。
    • POSIX用户
      • User ID: 1054| 找到您的/etc/passwd/文件并验证这是否适用于托管 EFS 驱动器的服务器。我认为这1054amazon linux的默认设置,但如果您在 EFS 主机上运行不同的操作系统,则应验证此值。
      • Group ID: 1054 (见最后一行)
    • 根目录创建权限
      • Owner User ID: 1054 (见最后一行)
      • Owner Group ID: 1054 (见最后一行)
      • POSIX permissions to apply to the root directory path777| 了解这些权限会转化为您的iamRoleStatements.Action价值观;如果它们不同,777则应适当设置。

3. EFS 的 Lambdas 添加

  • EFS File System: (从下拉选择中选择)
  • Local Mount Path: | 超级重要,例如/mnt/my-efs。这部分是非常不直观的 IMO。无论您想要什么位置,此位置都将映射到Root directory path您在构建接入点时配置的(上面的步骤 2.4)。意思是,如果您下载一个调用它的 s3 文件img.jpg/mnt/my-efs/.它将将该文件放入sftp.company.com/images/img.jpg.

4. 将项目下载到 EFS 驱动器

  • 显然,这可以通过多种方式完成。您的原始代码看起来非常好。作为示例,我只是将其扩展为从 S3 事件对象中提取文件位置。如果不明显,则每当 S3 存储桶获取新上传(添加到存储桶的新对象)时,都会触发 S3 事件。
def download_files_to_efs(self, event):
        """
        Locates the S3 file name (i.e. S3 object "key" value) the initiated the Lambda call, then downloads the file
        into the locally attached EFS drive at the target location.
        :param: event | S3 event record
        :return: dict
        """
        key = event.get('Records')[0].get('s3').get('object').get('key') # some-bucket/some-folder/img.jpg
        efs_loci = f"/mnt/my-efs/images/{key.split('/')[-1]}" # '/mnt/my-efs/images/img.jpg
        result = self.s3.download('some-bucket', key, efs_loci)
        if result:
            print('Download Success...')
        else:
            print('Ask Stack Overflow :/')
        return { 'status_code': 200 }
Run Code Online (Sandbox Code Playgroud)

5. 验证您的文件是否在 EFS 中

  • 要验证下载是否有效,您只需从 Lambda 中的 EFS 位置打印文件,然后检查内置 CloudWatch 日志以查看结果是否符合预期。
def get_files_from_path(file_path):
    """
    Returns the files found at the file_path
    :param file_path: string
    :return list:
    """
    # NOTE: file_path = '/my-efs/

    found_files = []
    for _, _, f in os.walk(file_path):
        found_files.append(f)
    if found_files:
       print('efs files: ', found_files)
    return found_files
Run Code Online (Sandbox Code Playgroud)

这里要特别注意!请注意,我没有从/mnt/my-efs/i read from 中读取/my-efs,这是因为 EFS/目录映射到/mntLambda 环境中的目录。

完成的

注意:如果我遗漏了某些内容,请随时发表评论,如果可以,我也会添加。不幸的是,我无法共享 repo,因为它是一个与工作相关的项目(受保护)。