【问题标题】:Can't sync s3 with ec2 folder from aws lambda无法将 s3 与来自 aws lambda 的 ec2 文件夹同步
【发布时间】:2020-05-28 21:17:01
【问题描述】:

我正在尝试使用 AWS 自动化数据处理。我在 python 中设置了一个 AWS lambda 函数:

  1. 由 S3 PUT 事件触发
  2. 使用 paramiko 层通过 SSH 连接到 EC2 实例
  3. 将存储桶中的新对象复制到实例中的某个文件夹中,解压缩实例中的文件并运行清理 csv 文件的 python 脚本。

问题是用于将 s3 存储桶与 ec2 文件夹同步的 aws cli 调用不起作用,但是当我手动 ssh 进入 ec2 实例并运行它的命令时它可以工作。我的 aws-cli 配置了我的 access_keys 并且 ec2 有允许其完全访问的 s3 角色。

    import boto3
    import time
    import paramiko

    def lambda_handler(event, context):
    #create a low level client representing s3
        s3 = boto3.client('s3')
        ec2 = boto3.resource('ec2', region_name='eu-west-a')
        instance_id = 'i-058456c79fjcde676'
        instance = ec2.Instance(instance_id)
    ------------------------------------------------------'''
    #start instance
        instance.start()
    #allow some time for the instance to start
        time.sleep(30)

    # Print few details of the instance
       print("Instance id - ", instance.id)
       print("Instance public IP - ", instance.public_ip_address)
       print("Instance private IP - ", instance.private_ip_address)
       print("Public dns name - ", instance.public_dns_name)
       print("----------------------------------------------------")
       print('Downloading pem file')
       s3.download_file('some_bucket', 'some_pem_file.pem', '/tmp/some_pem_file.pem')

    # Allowing few seconds for the download to complete
       print('waiting for instance to start')
       time.sleep(30)
       print('sshing to instsnce')
       ssh = paramiko.SSHClient()
       ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
       privkey = paramiko.RSAKey.from_private_key_file('/tmp/some_pem_file.pem')
    # username is most likely 'ec2-user' or 'root' or 'ubuntu'
    # depending upon yor ec2 AMI
    #s3_path = "s3://some_bucket/" + object_name
       ssh.connect(
       instance.public_dns_name, username='ubuntu', pkey=privkey)
       print('inside machine...running commands')
       stdin, stdout, stderr = ssh.exec_command('aws s3 sync s3://some_bucket/ ~/ec2_folder;\
       bash ~/ec2_folder/unzip.sh; python3 ~/ec2_folder/process.py;')
       stdin.flush()
       data = stdout.read().splitlines()
       for line in data:
        print(line)
        print('done, closing ssh session')
       ssh.close()

    # Stop the instance
      instance.stop()

    return('Triggered')

【问题讨论】:

  • paramiko 连接时,是否使用与连接时相同的用户名?否则,它可能没有相同的默认目录,因此无法找到您的凭据。
  • 是的,默认用户名是 ubuntu
  • 为什么 Lambda 函数会停止实例?这表明它正在等待 EC2 实例上的脚本完成运行。如果是这样,为什么不让 Lambda 在不使用 EC2 实例的情况下处理文件?或者,如果您需要使用 EC2 实例,您可以让实例在处理完成后自行关闭。见:How to automatically start, execute and stop EC2?
  • 实例需要在处理结束时停止,我不从 Lamba 本身执行此操作的原因是因为我正在处理的文件需要大量计算能力,并且需要我用来处理解压后的 csv 的所有库都有很多层,平均 16GB 以上。
  • 所以这就是为什么它不与我的存储桶同步的原因我只是打印了 stderr 而不是 stdout:aws: command not found 有趣的是机器上安装了 aws-cli,当我手动 ssh 和执行它会运行,但是当我的 lambda 通过 paramiko 执行命令时,它说找不到 aws 命令。

标签: python-3.x amazon-ec2 aws-lambda paramiko


【解决方案1】:

SSH 工具的使用有点不寻常。

这里有一些您可能会考虑的“云友好”选项。

Systems Manager 运行命令

AWS Systems Manager Run Command 允许您在 Amazon EC2 实例(事实上,在任何运行 Systems Manager 代理的计算机上)上执行脚本。它甚至可以同时在许多(数百个!)实例/计算机上运行命令,跟踪每次执行的成功。

这意味着,Lambda 函数可以通过 API 调用调用运行命令,而不是通过 SSH 连接到实例,而 Systems Manager 将在实例上运行代码。

拉,不要推

与其将工作“推”到实例,实例可以“拉”工作:

  • 配置 Amazon S3 事件以将消息推送到 Amazon SQS 队列中
  • 实例上的代码可能会定期轮询 SQS 队列
  • 当它在队列中找到一条消息时,它运行一个下载文件的脚本(桶和密钥在消息中传递)然后运行处理脚本

通过 HTTP 触发

该实例可以运行一个 Web 服务器,监听消息。

  • 配置 Amazon S3 事件以将消息推送到 Amazon SNS 主题
  • 将实例的 URL 添加为 SNS 主题的 HTTP 订阅
  • 当消息发送到 SNS 时,它会将其转发到实例的 URL
  • Web 服务器中的代码然后触发您的脚本

【讨论】:

  • 不需要实例无限期地运行吗?我使用 ssh 的原因是为了减少计费成本,即实例仅在事件触发时才启动,而不是在 SNS 上不断监听消息。
【解决方案2】:

此答案基于您希望在执行之间关闭 EC2 实例的其他信息。

我会推荐:

  • Amazon S3 事件触发 Lambda 函数
  • Lambda 函数启动实例,通过用户数据字段传递文件名信息(它可用于传递数据,而不仅仅是脚本)。然后 Lambda 函数可以立即退出(这比等待作业完成更具成本效益)
  • 将您的处理脚本放在/var/lib/cloud/scripts/per-boot/ 目录中,这将导致它在每次启动实例时运行每次,而不仅仅是第一次)
  • 脚本可以通过检索curl http://169.254.169.254/latest/user-data/提取从 Lambda 函数传递的用户数据,以便它知道来自 S3 的文件名
  • 然后脚本处理文件
  • 脚本然后运行sudo shutdown now -h停止实例

如果有可能当实例已经在处理一个文件时可能会出现另一个文件,那么我会稍微改变这个过程:

  • 不是通过用户数据传递文件名,而是将其放入 Amazon SQS 队列
  • 当实例启动时,它应该从 SQS 队列中获取详细信息
  • 文件处理完毕后,应再次检查队列,看看是否有另一条消息发送
    • 如果是,则处理文件并重复
    • 如果否,自行关闭

顺便说一句,事情有时会出错,因此值得在脚本中放置一个“断路器”,这样它就不会在您想要调试时关闭实例。这可能是传递标志的问题,甚至是向实例添加标签的问题,在调用关闭命令之前会对其进行检查。

【讨论】:

    猜你喜欢
    • 2021-07-12
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    • 2015-03-12
    • 2018-01-26
    • 2017-01-06
    • 2016-12-29
    • 1970-01-01
    相关资源
    最近更新 更多