如何在 AWS CDK 创建的 Python Lambda 函数中安装外部模块?
Posted
技术标签:
【中文标题】如何在 AWS CDK 创建的 Python Lambda 函数中安装外部模块?【英文标题】:How to install external modules in a Python Lambda Function created by AWS CDK? 【发布时间】:2020-03-10 08:24:37 【问题描述】:我在 Cloud9 中使用 Python AWS CDK,并且我正在部署一个简单的 Lambda 函数,该函数应该向 Atlassian 的 API 发送 API 请求当对象上传到 S3 存储桶时(也由 CDK 创建)。这是我的 CDK 堆栈代码:
from aws_cdk import core
from aws_cdk import aws_s3
from aws_cdk import aws_lambda
from aws_cdk.aws_lambda_event_sources import S3EventSource
class JiraPythonStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# The code that defines your stack goes here
jira_bucket = aws_s3.Bucket(self,
"JiraBucket",
encryption=aws_s3.BucketEncryption.KMS)
event_lambda = aws_lambda.Function(
self,
"JiraFileLambda",
code=aws_lambda.Code.asset("lambda"),
handler='JiraFileLambda.handler',
runtime=aws_lambda.Runtime.PYTHON_3_6,
function_name="JiraPythonFromCDK")
event_lambda.add_event_source(
S3EventSource(jira_bucket,
events=[aws_s3.EventType.OBJECT_CREATED]))
lambda 函数代码使用我导入的requests
模块。但是,当我检查 CloudWatch 日志并测试 lambda 函数时 - 我得到:
无法导入模块“JiraFileLambda”:没有名为“requests”的模块
我的问题是:如何通过 Python CDK 安装 requests 模块?
I've already looked around online and found this。但它似乎直接修改了 lambda 函数,这会导致堆栈漂移(我被告知这对 IaaS 来说是坏的)。我也查看了 AWS CDK 文档,但没有发现任何外部模块/库的提及(我现在正在对其进行彻底检查)有人知道我该如何解决这个问题吗?
编辑: It would appear I'm not the only one looking for this.
Here's another GitHub issue that's been raised.
【问题讨论】:
【参考方案1】:更新:
现在看来,CDK 中有一种新类型的(实验性)Lambda 函数,称为 PythonFunction。 Python docs for it are here。这包括对添加 requirements.txt 文件的支持,该文件使用 docker 容器将它们添加到您的函数中。 See more details on that here。具体来说:
如果入口路径中存在 requirements.txt 或 Pipfile,则该构造将根据运行时将所有必需的模块安装到与 Lambda 兼容的 Docker 容器中。
原答案:
这是我的经理编写的一段很棒的代码,我们现在使用:
def create_dependencies_layer(self, project_name, function_name: str) -> aws_lambda.LayerVersion:
requirements_file = "lambda_dependencies/" + function_name + ".txt"
output_dir = ".lambda_dependencies/" + function_name
# Install requirements for layer in the output_dir
if not os.environ.get("SKIP_PIP"):
# Note: Pip will create the output dir if it does not exist
subprocess.check_call(
f"pip install -r requirements_file -t output_dir/python".split()
)
return aws_lambda.LayerVersion(
self,
project_name + "-" + function_name + "-dependencies",
code=aws_lambda.Code.from_asset(output_dir)
)
它实际上是作为方法的 Stack 类的一部分(不在 init 内)。我们在这里设置它的方式是我们有一个名为 lambda_dependencies
的文件夹,其中包含我们正在部署的每个 lambda 函数的文本文件,其中只有一个依赖项列表,例如 requirements.txt
。
为了利用这段代码,我们在 lambda 函数定义中包含如下:
get_data_lambda = aws_lambda.Function(
self,
.....
layers=[self.create_dependencies_layer(PROJECT_NAME, GET_DATA_LAMBDA_NAME)]
)
【讨论】:
谢谢,我使用了一个 build.sh 脚本,我必须预先手动调用(在使用 cdk cli 部署之前)。该脚本或多或少与上述相同(对lamnda本身使用不同的requirements.txt并将其打包成一个zip。然后该zip就是我用from_asset()在顶部堆栈本身中引用的内容。但我不得不说,创建一个层并将其包含在 lambda 中感觉更好。肯定会看到我如何集成到我的解决方案中。【参考方案2】:在通过 CDK 部署 lambda 之前,您应该在本地安装 lambda 的依赖项。 CDK 不知道如何安装依赖项以及应该安装哪些库。
在你的情况下,你应该在执行cdk deploy
之前安装依赖requests和其他库。
例如,
pip install requests --target ./asset/package
有an example供参考。
【讨论】:
如果你使用 pipenv 运行以下命令:pipenv run pip install requestst --target ./asset/package
我不确定我是否关注你。我是否需要使用lambda.Code.fromAsset
或以某种方式指示cdk 在哪里获取已安装的依赖项?还是cdk deploy
默认取./asset/package
上传到s3做其他魔术?
您必须安装与您的处理程序一致的软件包。在 lamda 的文档中查看详细信息。
我的经理实际上最终编写了一些非常简洁的代码,用于使用依赖层在 CDK + Lambda 环境中管理包。我会尽量记住在我回来工作时发布它。
我整个上午都在试图弄清楚这一点。多谢你们。所以@Jamie 分享将不胜感激。虽然我自己会尝试使用构建脚本来生成一个部署包,然后我可以在堆栈 lambda 定义中引用它。【参考方案3】:
甚至没有必要在 CDK 中使用实验性 PythonLambda 功能 - CDK 内置支持将依赖项构建到简单的 Lambda 包(不是 docker 映像)中。它使用 docker 进行构建,但最终结果仍然是一个简单的压缩文件。文档在这里显示:https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-readme.html#bundling-asset-code;要点是:
new Function(this, 'Function',
code: Code.fromAsset(path.join(__dirname, 'my-python-handler'),
bundling:
image: Runtime.PYTHON_3_9.bundlingImage,
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output'
],
,
),
runtime: Runtime.PYTHON_3_9,
handler: 'index.handler',
);
我在我的 CDK 部署中使用了这个确切的配置,它运行良好。
而对于 Python,它很简单
aws_lambda.Function(
self,
"Function",
runtime=aws_lambda.Runtime.PYTHON_3_9,
handler="index.handler",
code=aws_lambda.Code.from_asset(
"function_source_dir",
bundling=core.BundlingOptions(
image=aws_lambda.Runtime.PYTHON_3_9.bundling_image,
command=[
"bash", "-c",
"pip install --no-cache -r requirements.txt -t /asset-output && cp -au . /asset-output"
],
),
),
)
【讨论】:
是的......这是正确的答案。只为一些简单的包做一个层是不行的。 第二。这个解决方案需要更多的支持,【参考方案4】:我也遇到了这个问题。当我在我的 ubuntu 机器上工作时,我使用了像 @Kane 和 @Jamie 这样的解决方案。但是,我在使用 MacOS 时遇到了问题。显然,如果将某些(全部?)python 包安装在不同的操作系统上,则它们不适用于 lambda(linux env)(请参阅 *** 帖子)
我的解决方案是在 docker 容器中运行 pip install。这让我可以从我的 macbook 进行 cdk 部署,而不会在 lambda 中遇到我的 python 包的问题。
假设您的 cdk 项目中有一个目录 lambda_layers/python,它将容纳 lambda 层的 python 包。
current_path = str(pathlib.Path(__file__).parent.absolute())
pip_install_command = ("docker run --rm --entrypoint /bin/bash -v "
+ current_path
+ "/lambda_layers:/lambda_layers python:3.8 -c "
+ "'pip3 install Pillow==8.1.0 -t /lambda_layers/python'")
subprocess.run(pip_install_command, shell=True)
lambda_layer = aws_lambda.LayerVersion(
self,
"PIL-layer",
compatible_runtimes=[aws_lambda.Runtime.PYTHON_3_8],
code=aws_lambda.Code.asset("lambda_layers"))
【讨论】:
我更新了我的原始答案,现在包括来自 AWS 的官方(实验性)实现,其中结合了 docker 容器的使用。它可能是更适合您的简化解决方案! 哦,太棒了!谢谢!以上是关于如何在 AWS CDK 创建的 Python Lambda 函数中安装外部模块?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用适用于 Python 的 AWS CDK 通过 ARN 查找现有的经典负载均衡器 (CLB)?