如何在 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)?

AWS CDK - 云观察

AWS CDK - 角色和策略创建

Cloudfront 提供通过 AWS CDK Python 为 S3 存储桶来源创建的访问被拒绝响应,无需公共访问

如何覆盖 AWS CDK 中的资源?

如何识别 aws cdk 应用程序何时在部署状态下运行?