使用 AWS Lambda 的 ETL 简介
在构建 ETL 管道时,存在许多选项。您可以使用Astronomer或Prefect for Orchestration 之类的工具,但您还需要某个地方来运行计算。有了这个,你有几个选择:
AWS EC2 等虚拟机 (VM)
AWS ECS 或 AWS Fargate 等容器服务
Apache Spark 类似于 AWS EMR(Elastic Map Reduce)
像 AWS Lambda 这样的无服务器计算
其中每一个都有其优点。如果您正在寻求设置、维护和成本方面的简单性,您可以使用** AWS Lambdas** 或无服务器计算来运行简单的作业。
注意我说的是简单。AWS Lambda 不适用于计算密集型或长时间运行的作业。它们适用于执行需要几分钟而不是几小时的少量代码。
什么是 AWS Lambda 和无服务器计算?
AWS 中的 Lambda 函数是响应事件而执行的一段代码。事件可以是对 API 端点的请求、上传到 S3 存储桶的文件或计划的事件。代码被执行,结果被返回。这是对AWS工作原理的一个很好的描述:
Lambda 仅在需要时运行您的函数并自动扩展,从每天几个请求到每秒数千个请求。您只需为消耗的计算时间付费——当您的代码未运行时不收费。有关更多信息,请参阅 AWS Lambda 定价。
Lambda 函数是为需要频繁运行的较小作业考虑 ETL 的绝佳方式。例如在触发时,如 API 调用,或按计划每晚进行。它还允许您编排多个 Lambda 函数以创建更复杂的 ETL 管道。
让我们开始创建我们的第一个 Lambda 函数。
创建您的 Lamba 函数
在 AWS 控制台中,导航到 Lambda 服务。按创建函数按钮开始。系统将提示您选择蓝图。对于此示例,我们将选择Author from scratch。给你的名字一个合适的名字,然后选择Python 3.9作为运行时。选择您喜欢或通常在本地开发的架构;这使得上传与您的 Lambda 函数兼容的新库变得更加容易。
您可以创建新角色或选择现有角色。我们将在下一节中介绍。
兰巴角色
创建 Lambda 函数的一个关键部分是角色。该角色允许函数访问其他 AWS 服务。对于此示例,我们需要授予函数访问Lambda和S3的权限。我还授予了访问VPC的权限,但这对于此设置来说不是必需的。
最好创建一个新角色来帮助您仅隔离此功能所需的权限。或者,如果您要为 ETL 用例创建多个 lambda 函数,请考虑使用更通用的命名方式,例如Lamba-ETL-Role。
设置函数的超时
接下来是配置函数的超时。根据函数执行所需的时间,您可以将其增加到 15 分钟。对于此示例,我们将其设置为1 分钟。如果达到超时,您可以在 CloudWatch 日志中看到。
单击Configuration选项卡,然后在General Configuration下,将超时设置为 1 分钟。
使用参数和机密扩展
接下来 - 这不是 100% 必要的,但是当你想确保你安全地处理敏感数据而不是在你的代码中暴露它时,这是一个很好的做法。过去,我写过有关如何使用环境变量在本地执行此操作的文章;然而,在 AWS 中,我们将使用Parameters and Secrets 扩展。
Parameters and Secrets Extension允许您将敏感数据存储在 AWS Secrets Manager 中并在 Lambda 函数中访问它,这是一种存储 API 密钥、数据库凭证等的好方法。您还可以使用它来存储非敏感数据,例如配置设置。您可以在此处阅读有关此功能的更多信息:配置 Secrets Manager
我们将从向 Lambda 函数添加一个 Layer 开始,以允许我们访问扩展。从Code选项卡,滚动到底部并单击Add a Layer。选择AWS Layers,然后选择AWS-Parameters-and-Secrets-Lambda-Extension-Arm64和该层的最新版本。
接下来,我们需要添加代码来帮助我们访问秘密。我已将其添加到一个小函数中,该函数将查找电影数据库(TMDB) 的 API 密钥并将其返回。您可以在下面查看完整代码。TMDB 是获取电影和电视节目信息的绝佳 API,并且可以免费用于非商业目的。
如下所示,我们将headers使用 JSON 对象创建一个变量。然后,我们会将其传递到我们对secrets_extension_endpoint的 API 调用中,如图所示。响应将是一个带有秘密字符串的 JSON 对象。然后我们将解析该字符串并返回 API 密钥。
在我的代码显示<< your secrets ARN >> 的地方,您需要将其替换为您的密钥的ARN。您可以在AWS Secrets Manager 控制台中找到它。
注意: 您将需要返回并修改您的角色以允许访问您创建的秘密。 您可以在AWS 文档中找到有关如何执行此操作的说明。按照“示例读取一个秘密(附加到身份)”部分中的示例进行操作。
import requests
import jsondefget_tmdb_api_key():
headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get("AWS_SESSION_TOKEN")}
secrets_extension_endpoint = ("http://localhost:"
+ "2773"
+ "/secretsmanager/get?secretId="
+ "<< your secrets arn >>"
)
r = requests.get(secrets_extension_endpoint, headers=headers)
secret = json.loads(r.text)["SecretString"]
secret = json.loads(secret)
TMDB_API_KEY = secret["TMDB_API"]return TMDB_API_KEY
添加对 Pandas 的支持
接下来,我们的示例和许多其他 ETL 用例将需要使用 Pandas。我们需要向 Lambda 函数添加一个层来支持这一点。从Code选项卡,滚动到底部并单击Add a Layer。选择自定义层,然后选择AWSSDKPandas-Python39-Arm64和最新的层版本。
启用这一层后,您可以import pandas在您的代码中无需将包上传到您的 lambda 函数。
将文件写入 S3 存储桶
现代数据仓库/平台的一个非常强大的工作流程是能够直接使用 JSON 数据。我们将通过将数据输出到** S3 存储桶** 中的JSON 文件来利用这一点。
我们将创建一个函数,该函数将采用DataFrame并将其写入 S3 存储桶中的JSON 文件。我们将使用boto3图书馆来做到这一点。我们将使用 Pandasto_json方法创建一个 JSON 字符串,将其编码为utf-8,然后将其写入 S3 存储桶。
defwrite_to_s3(df, type, imdb_id):# Get JSON for the DataFrame
output = json.loads(df.to_json(orient='records'))
string = str(output)
encoded_string = string.encode("utf-8")
bucket_name = "lambda-tmdb"
file_name = "out.json"
s3_path = "output/" + type + "/" + imdb_id + "-" + type + "-" + file_name
s3 = boto3.resource('s3')
object = s3.Object(bucket_name, s3_path)
object.put(Body=encoded_string)return"Success"
当我们准备好使用一些参数(例如DataFrame本身、我们正在写入的数据类型以及电影或电视节目的IMDB ID )将 DataFrame输出到 S3 时,我们会调用此函数。此处的类型是为了方便起见,因此我们可以使用单个函数编写不同的数据类型,同时为每个电影查找创建唯一的文件名。
write_to_s3(df_crew, "crew", imdb_id)
部署您的 Lambda 函数
部署函数的方法有多种,最简单的方法是使用 AWS 控制台并上传 zip 文件。但是,在此处添加一点自动化将简化您更改代码时的工作。
您需要做的第一步是设置 AWS CLI。您可以在 AWS 文档开始使用 AWS CLI中找到有关如何执行此操作的说明。您需要创建一个具有适当权限的新 IAM 用户来写入 Lambda 服务。
接下来,我编写了一个可以从本地终端调用的简单bash脚本,它完成了所有需要的工作。一个非常重要的步骤是将 python 库压缩到正确的级别,并将它们与 zip 文件打包在一起。该脚本向您展示了更改sites-packages目录和压缩要保留的库内容的过程。在这里明确命名您想要的库,这样您就不会最终上传不必要的文件。
注意: 这还假设我已经创建了一个本地虚拟环境并将我的功能代码放在本地。我更喜欢以这种方式构建我的 Lambda,这样我就可以对 GitHub 中的所有内容进行版本控制
# delete the old zip
rm tmdb-deployment-package.zip# change the directory to the site-packages directory and zip the contentscd venv/lib/python3.9/site-packages# Explicitly add the tmdbsimple package folders needed for the lambda function
zip -r ../../../../tmdb-deployment-package.zip tmdbsimple tmdbsimple-2.9.1.dist-info# change back to the root directory and add the needed python filescd ../../../../
zip -g tmdb-deployment-package.zip lambda_function.py# deploy to AWS Lambda
aws lambda update-function-code --function-name lambda-tmdb --zip-file fileb://tmdb-deployment-package.zip
在您的函数成功部署后,您可以返回控制台并查看您的代码和提供的包。注意结构。这些包位于根级别的文件夹中,允许它们像任何其他包一样导入,就像您在本地开发一样。
使用 API 端点触发 Lambda 函数
作为我们函数的最后一步,我们将使用 AWS API 网关通过 API 端点触发它,并将函数参数作为查询字符串传递,这是一种触发特定事件的超强大方式,可以为您要处理的确切数据触发.
导航到“配置”选项卡并单击“添加触发器”按钮。滚动到 API Gateway 并选择Create New API选项。此外,选择HTTP API选项并将安全性设置为Open。
创建端点后,您将拥有一个 URL,您可以使用该 URL 触发您的 Lambda。我们将传递一个 ID 列表作为查询字符串参数,允许我们将多个 ID 传递给函数并一次处理它们。使用查询字符串参数触发 API 是一种非常强大的批量处理数据的方法。
我们构造 URL 如下:
API Gateway URL + ?ids=tt0162346&ids=tt0326900
现在,在我们的函数中,我们可以通过作为所有 Lambda 函数一部分的事件对象访问查询字符串参数列表。我们将通过multiValueQueryStringParameters密钥访问它。然后,使用查询字符串参数的名称,我们想要访问,在这种情况下,ids。
params = event["multiValueQueryStringParameters"]
id_list = params['ids']
使用 API 网关时使用 CloudWatch 监控 Lambda 函数
默认情况下,您将获得函数的 CloudWatch 监控。我遇到了一些需要扩展日志记录的错误。您必须将以下内容添加到API 网关阶段的日志格式中。
$context.integrationErrorMessage
在此处阅读完整说明。
功能码
我们将把这一切放在一起作为我们lambda_handler功能的结局。我们从查询字符串中检索 ID,然后调用我们的函数,get_tmdb_api_key,以从 Secrets Store 获取 API 密钥。最后,我们遍历我们的 ID 并构建一个数据框,并使用函数将其写入 S3 write_to_s3。
deflambda_handler(event, context):# Get the IDs from the Query String
params = event["multiValueQueryStringParameters"]
id_list = params['ids']# Get credentials from the Secrets Manager
KEY = get_tmdb_api_key()# Loop through the IDs passed and do something with themfor i in range(len(id_list)):
imdb_id = id_list[i]# Get the movie details# CODE NOT SHOWN# Save the movie details as a dataframe# write it to S3
write_to_s3(df_crew, "crew", imdb_id)# return a success message in the proper format for a Lambda functionreturn {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "Success",
}
编排和处理仓库中的数据
最后一步是在工作流中触发 API 端点,然后处理仓库中的数据。JSON 如何与雪花一起工作的示例可以在我的文章找到,我将在以后的文章中介绍编排。
结论
构建 ETL 管道时,在计算方面有很多选项可供选择。对于简单的作业,AWS Lambda 函数可能是丰富数据或快速高效处理的绝佳方式。我们首先向您展示了如何创建 Lambda,包括设置运行它的角色。然后我们介绍了一些建议,例如利用参数和机密扩展来安全地存储 API 密钥等信息。然后,我们将了解如何使用 AWS CLI 自动部署。最后,我们使用 API 网关触发该功能并使用 CloudWatch 对其进行监控。我希望这篇文章展示了 Lambda 的美妙之处,以及您可以如何将 Lambda 添加到您的 ETL 工具带中。