如何在AWS CDK创建的Python Lambda函数中安装外部模块?

zte4gxcn  于 8个月前  发布在  Python
关注(0)|答案(7)|浏览(82)

我在Cloud9中使用Python AWS CDK,并且我正在部署一个简单的Lambda函数,该函数应该在对象上传到S3 Bucket(也由CDK创建)时向Atlassian的API发送API请求。以下是我的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.

1tu0hz3e

1tu0hz3e1#

您应该在通过CDK部署lambda之前在本地安装lambda的依赖项。CDK不知道如何安装依赖项以及应该安装哪些库。
在您的情况下,应该在执行cdk deploy之前安装依赖项requests和其他库。
比如说,

pip install requests --target ./asset/package

字符串
an example可供参考。

myss37ts

myss37ts2#

我也遇到了这个问题。当我在我的ubuntu机器上工作时,我使用了@Kane和@Jamie建议的解决方案。然而,我在MacOS上工作时遇到了问题。显然,一些(所有?)python包如果在不同的操作系统上安装pip,则无法在lambda(Linux env)上工作(参见stackoverflow帖子)
我的解决方案是在一个docker容器中运行pip安装,这允许我从我的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"))

字符串

rm5edbpk

rm5edbpk3#

想分享我为此制作的2个模板仓库(深受上述一些启发):

希望对大家有帮助:)
最后,如果你想看到一个关于这个主题的长线程,请参阅这里:https://github.com/aws/aws-cdk/issues/3660

c6ubokkw

c6ubokkw4#

作为我的另一个答案的替代,这里有一个稍微不同的方法,它也适用于docker-in-docker(重复选项方法不适用)。
设置Lambda函数,如

lambda_fn = aws_lambda.Function(
    self,
    "Function",
    runtime=lambdas.Runtime.PYTHON_3_9,
    code=lambdas.Code.from_docker_build(
        "function_source_dir",
    ),
    handler="index.lambda_handler",
)

字符串
function_source_dir/中有这些文件:

  • index.py(与上面的代码相匹配-您可以随意命名)
  • requirements.txt
  • Dockerfile

设置您的Dockerfile,如

# Note that this dockerfile is only used to build the lambda asset - the
# lambda still just runs with a zip source, not a docker image.
# See the docstring for aws_lambda.Code.from_docker_build
FROM public.ecr.aws/lambda/python:3.9.2022.04.27.10-x86_64

COPY index.py /asset/
COPY requirements.txt /tmp/
RUN pip3 install -r /tmp/requirements.txt -t /asset


synth步骤将在docker中构建您的资产(使用上面的dockerfile),然后从镜像中的/asset/目录中提取构建的Lambda源代码。
我还没有深入研究为什么BundlingOptions方法在docker容器中运行时无法构建,但这个方法确实有效(只要docker与-v /var/run/docker.sock:/var/run/docker.sock一起运行以启用docker-in-docker)。一如既往,在这样做时,请务必考虑您的安全状况。

am46iovg

am46iovg5#

这似乎是为我工作,在去:

str1 := "bash"
    str2 := "-c"
    str3 := "pip install --no-cache -r requirements.txt -t /asset-output && cp -au . /asset-output"
    command := []*string{&str1, &str2, &str3}

    lambdaFn := awslambda.NewFunction(stack, jsii.String("foo"), &awslambda.FunctionProps{
        Runtime: awslambda.Runtime_PYTHON_3_9(),
        Handler: jsii.String("index.lambda_handler"),
        Timeout: awscdk.Duration_Seconds(jsii.Number(900)),
        Code: awslambda.Code_FromAsset(jsii.String("lambda/foo/blah"), &awss3assets.AssetOptions{
            Bundling: &awscdk.BundlingOptions{
                Image:   awslambda.Runtime_PYTHON_3_9().BundlingImage(),
                Command: &command,
            },
        }),
    })

字符串

2wnc66cl

2wnc66cl6#

甚至不需要在CDK中使用实验性的PythonLambda功能-CDK中内置了支持将依赖项构建到简单的Lambda包中(而不是Docker镜像)。它使用Docker进行构建,但最终结果仍然是一个简单的zip文件。文档显示在这里: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"
            ],
        ),
    ),
)

3xiyfsfu

3xiyfsfu7#

更新:

现在看来,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)]
        )

相关问题