如何在 AWS CDK 创建的 Python Lambda 函数中安装外部模块?
How to install external modules in a Python Lambda Function created by AWS CDK?
我在 Cloud9 中使用 Python AWS CDK,我正在 部署一个简单的 Lambda 函数,它应该发送一个 API 请求 到 Atlassian API 当对象上传到 S3 Bucket(也是由 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 安装请求模块?
I've already looked around online and found this。但它似乎直接修改了 lambda 函数,这会导致 Stack Drift(有人告诉我这对 IaaS 来说是不好的)。我也查看了 AWS CDK 文档,但没有发现任何关于外部 modules/libraries 的提及(我现在正在对其进行彻底检查)有人知道我如何解决这个问题吗?
在通过 CDK 部署 lambda 之前,您应该在本地安装 lambda 的依赖项。 CDK 不知道如何安装依赖项以及应该安装哪些库。
在你的情况下,你应该在执行 cdk deploy
.
之前安装依赖项 requests 和其他库
例如,
pip install requests --target ./asset/package
有an example供参考
更新:
现在看来,CDK 中似乎有一种新型(实验性)Lambda 函数,称为 PythonFunction。 Python docs for it are here. And this includes support for adding a requirements.txt file which uses a docker container to add them to your function. See more details on that here。具体来说:
If requirements.txt or Pipfile exists at the entry path, the construct will handle installing all required modules in a Lambda compatible Docker container according to the runtime.
原答案:
所以这是我的经理写的我们现在使用的很棒的代码:
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 class 作为方法的一部分(不在 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)]
)
我也运行关注这个问题。当我在我的 ubuntu 机器上工作时,我使用了@Kane 和@Jamie 建议的解决方案就好了。但是,我 运行 在 MacOS 上工作时遇到了问题。显然一些(全部?)python 包在 lambda(linux env)上不起作用,如果它们是 pip 安装在不同的 os 上(参见 Whosebug post)
我的解决方案是 运行 在 docker 容器中安装 pip。这允许我从我的 macbook 进行 cdk 部署,而不是 运行 进入 lambda 中的 python 包的问题。
suppos你的 cdk 项目中有一个目录 lambda_layers/python,它将存放你的 python lambda 层包。
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"))
甚至没有必要使用 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"
],
),
),
)
想分享我为此制作的 2 个模板存储库(深受上述一些启发):
- https://github.com/iguanaus/cdk-ecs-python-with-requirements- - 基本python功能
的ecs服务演示
- https://github.com/iguanaus/cdk-lambda-python-with-requirements - 具有要求的 lambda python 作业演示。
希望对大家有所帮助:)
最后;如果您想查看有关此主题的长篇文章,请参阅此处:https://github.com/aws/aws-cdk/issues/3660
我在 Cloud9 中使用 Python AWS CDK,我正在 部署一个简单的 Lambda 函数,它应该发送一个 API 请求 到 Atlassian API 当对象上传到 S3 Bucket(也是由 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 安装请求模块?
I've already looked around online and found this。但它似乎直接修改了 lambda 函数,这会导致 Stack Drift(有人告诉我这对 IaaS 来说是不好的)。我也查看了 AWS CDK 文档,但没有发现任何关于外部 modules/libraries 的提及(我现在正在对其进行彻底检查)有人知道我如何解决这个问题吗?
在通过 CDK 部署 lambda 之前,您应该在本地安装 lambda 的依赖项。 CDK 不知道如何安装依赖项以及应该安装哪些库。
在你的情况下,你应该在执行 cdk deploy
.
例如,
pip install requests --target ./asset/package
有an example供参考
更新:
现在看来,CDK 中似乎有一种新型(实验性)Lambda 函数,称为 PythonFunction。 Python docs for it are here. And this includes support for adding a requirements.txt file which uses a docker container to add them to your function. See more details on that here。具体来说:
If requirements.txt or Pipfile exists at the entry path, the construct will handle installing all required modules in a Lambda compatible Docker container according to the runtime.
原答案:
所以这是我的经理写的我们现在使用的很棒的代码:
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 class 作为方法的一部分(不在 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)]
)
我也运行关注这个问题。当我在我的 ubuntu 机器上工作时,我使用了@Kane 和@Jamie 建议的解决方案就好了。但是,我 运行 在 MacOS 上工作时遇到了问题。显然一些(全部?)python 包在 lambda(linux env)上不起作用,如果它们是 pip 安装在不同的 os 上(参见 Whosebug post)
我的解决方案是 运行 在 docker 容器中安装 pip。这允许我从我的 macbook 进行 cdk 部署,而不是 运行 进入 lambda 中的 python 包的问题。
suppos你的 cdk 项目中有一个目录 lambda_layers/python,它将存放你的 python lambda 层包。
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"))
甚至没有必要使用 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"
],
),
),
)
想分享我为此制作的 2 个模板存储库(深受上述一些启发):
- https://github.com/iguanaus/cdk-ecs-python-with-requirements- - 基本python功能 的ecs服务演示
- https://github.com/iguanaus/cdk-lambda-python-with-requirements - 具有要求的 lambda python 作业演示。
希望对大家有所帮助:)
最后;如果您想查看有关此主题的长篇文章,请参阅此处:https://github.com/aws/aws-cdk/issues/3660