无法将 s3 与来自 aws lambda 的 ec2 文件夹同步
Posted
技术标签:
【中文标题】无法将 s3 与来自 aws lambda 的 ec2 文件夹同步【英文标题】:Can't sync s3 with ec2 folder from aws lambda 【发布时间】:2020-05-28 21:17:01 【问题描述】:我正在尝试使用 AWS 自动化数据处理。我在 python 中设置了一个 AWS lambda 函数:
-
由 S3 PUT 事件触发
使用 paramiko 层通过 SSH 连接到 EC2 实例
将存储桶中的新对象复制到实例中的某个文件夹中,解压缩实例中的文件并运行清理 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 命令。 【参考方案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 队列中获取详细信息 文件处理完毕后,应再次检查队列,看看是否有另一条消息发送 如果是,则处理文件并重复 如果否,自行关闭顺便说一句,事情有时会出错,因此值得在脚本中放置一个“断路器”,这样它就不会在您想要调试时关闭实例。这可能是传递标志的问题,甚至是向实例添加标签的问题,在调用关闭命令之前会对其进行检查。
【讨论】:
以上是关于无法将 s3 与来自 aws lambda 的 ec2 文件夹同步的主要内容,如果未能解决你的问题,请参考以下文章