使用 CDK 部署时,Lambda 无法从外部文件夹中找到模块

Lambda can't find modules from outer folders when deployed with CDK

我正在使用 cdk 将一些 api 部署到 API 网关。我的问题是包含 lambda(index.ts) 的文件无法导入该文件夹(名为 get-users 的文件夹)之外的任何文件或 npm 模块。

我尝试将 node_modules 文件夹和其他文件(在文件夹 get-users 之外)复制到文件夹 get-users 并且效果很好。

导入lodash时的示例错误如下,

"errorType": "Runtime.ImportModuleError",
"errorMessage": "Error: Cannot find module 'lodash'",
"stack": [
    "Runtime.ImportModuleError: Error: Cannot find module 'lodash'",

我正在导入 lodash 如下,

import * as _ from "lodash";

我正在导入共享文件如下,

import { validator } from "./shared/validators" // This one works

import { validator } from "../../shared/validators" // This one doesn't work

如果您的 lambda 函数使用依赖项,则必须将所有内容打包到一个 ZIP 文件中,并告诉 CDK 在哪里可以找到该 ZIP。然后它将为您上传到 S3。但包装将由您负责。您需要包括所有依赖项以及您的代码。

CDK目前支持3种"Assets":

  • InlineCode - 对单行 lambdas 有用
  • AssetCode - 没有依赖关系的单文件 lambdas
  • S3Code - 已上传到 S3 存储桶中的现有 lambda 包

对于您的用例,您需要 AssetCode,但您将指向本地 ZIP 文件而不是目录。

相关回答:

既然很多人问这个问题,我会看看是否可以在 Python 中开源我的 lambda 打包结构。如果是,我会 link 放在这里。

编辑:我开源了一个 CDK 结构,我将其用于 lambda 打包,有一百多个依赖项,包括 NumPy。

https://gitlab.com/josef.stach/aws-cdk-lambda-asset

在花了一些时间考虑同样的问题后,我最终使用 webpack 来构建我的 lambda 包,这绝对是我推荐的方式。

在这种情况下你要做的是将你的 AssetCode 指向 Webpack 先前打包整个东西的目录,例如src/your-function/build.

额外的好处是您可以配置 Webpack 来缩小文件,从而加快部署速度,同时也加快了 lambda 冷启动速度。

经过一番研究,我找到了答案。问题是 CDK 没有部署 node_modules 文件夹和位于包含 lambda 源文件的文件夹之外的其他文件夹。

在创建 lambda 文件根路径时,必须将根路径添加到 'code' 属性中,这样它将获取其中的所有 folders/files 并部署到 lambda。

    const pathToRoot = //absolute path to the root folder
    const pathToHandler = //path to handler from root folder
    const lambdaFunction: Function = new Function(construct, name, {
        functionName: name,
        code: Code.asset(pathToRoot),
        handler: `${pathToHandler}/index.handler`,
        runtime: Runtime.NODEJS_10_X
    });

更新

经过更多研究发现处理此问题的最佳方法是 Lambda Layers。我已将节点模块文件夹添加为层。

只想对这个问题给出一个新的答案。在 CDK 中,检查 aws-lambda-nodejs 模块,该模块未包含在该问题的其他答案中。简而言之,它旨在解决这个问题。例如,通过简单地使用此结构,将它带来的所有其他功能放在一边:

  const testLambda = new lambda_nodejs.NodejsFunction(this, 'fox-get', {
    runtime: lambda.Runtime.NODEJS_12_X,
    entry: 'lambda/fox-get/fox-get.js',
    handler: 'handler',
    functionName: 'sn-v1-fox-get',
    description: 'Get all devices from fox API.',
    memorySize: 256,
    timeout: Duration.seconds(360)
  });

它将启动 docker 并生成一个精简的单个文件。它还允许您在项目中使用节点模块。我没有这样做,但是很多其他开发人员已经这样做了,您可以轻松地 google 他们所做的事情。

这对我来说已经足够了。我可以在一个干净的文件夹树中开发我的 lambda 函数,并避免重复的源文件,这就是我所需要的!

我运行遇到了类似的问题。我试图访问定义 lambda 的 g运行dparent 文件夹中的函数。问题是 lambda 只能访问同一文件夹中的文件(它无法访问目录中的任何内容)。例如,下面的 lambda 片段不起作用:

import json
from ....function_outside_of_cdk_dir import outer_fn_hello

def handler(event, context):
    print('EVENT RECEIVED!!!!!!!!: {}'.format(json.dumps(event)))
    outer_fn_hello("test")

要解决这个问题,可以使用DockerImageFunction。解决方案是将您的 lambda 需要的所有文件(包括 g运行dparent 文件夹中的文件)放入 docker 容器,然后 运行 您的 lambda 来自 docker 容器。下面是一些截图和代码,希望能对某人有所帮助(Python):

我们的目录结构如下:

在 outer-folder > cdk-example 中:

function_outside_of_cdk_dir.py

def outer_fn_hello(name):
    print("OUTER FN HELLO", name)

hello_lambda.py(注意它如何访问function_outside_of_cdk_dir.py)

import json
from function_outside_of_cdk_dir import outer_fn_hello

def handler(event, context):
    print('EVENT RECEIVED!!!!!!!!: {}'.format(json.dumps(event)))
    outer_fn_hello("test")

Docker 文件

FROM public.ecr.aws/lambda/python:3.8
ARG APP_ROOT=.
COPY ${APP_ROOT}/function_outside_of_cdk_dir.py ${LAMBDA_TASK_ROOT}/
COPY ${APP_ROOT}/cdk-example/functions ${LAMBDA_TASK_ROOT}/functions


ENV PYTHONPATH=${LAMBDA_TASK_ROOT}

ENTRYPOINT ["/lambda-entrypoint.sh"]
CMD ["functions/hello_lambda.handler"]

cdk-示例 --> cdk_example --> cdk_example_stack.py

from aws_cdk import core as cdk
import os
from aws_cdk.aws_lambda import DockerImageFunction, DockerImageCode
from aws_cdk import core

class CdkExampleStack(cdk.Stack):

    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        docker_context = os.path.join(os.path.dirname(__file__), '../../')

        # The code that defines your stack goes here

        #define lambda
        lambda_1 = DockerImageFunction(self, 'lambda_1', timeout=cdk.Duration.seconds(30),  function_name=f'lambda_1', code=DockerImageCode.from_image_asset(
            docker_context, file='cdk-example/functions/Dockerfile'))

.docker忽略

cdk-example/cdk.out/

我在使用全新的 CDK 应用程序时遇到了类似的问题。我指定了我的函数:

const handler = new lambda.Function(this, "RoutesHandler", {
      runtime: lambda.Runtime.NODEJS_14_X,
      code: lambda.Code.fromAsset("resources"),
      handler: "handler.main",
      environment: {
        BUCKET: bucket.bucketName
      }
});

我的错误

我的错误是我放了一个文件 /resources/main.ts 而不是 /resources/main.js