Serverless Lambda Layers 报错 Cannot find module 'request'
结论
serverless deploy 会根据 layers 配置创建 Lambda Layer,但不会自动将其关联到所有函数。
当前配置还通过 package.exclude 排除了 node_modules/**,因此函数部署包中不包含 request。如果函数配置也没有显式引用 nodejs Layer,运行时就会报错:
Cannot find module 'request'
要正常加载依赖,需要满足以下两个条件:
- Layer 压缩包的目录结构符合 Node.js Lambda Layer 规范。
- 每个使用该依赖的 Lambda 函数都显式关联这个 Layer。
正确的 Layer 目录结构
对于 Node.js,依赖必须放在 Layer 压缩包的 nodejs/node_modules 目录中:
nodejs.zip
└── nodejs/
├── package.json
└── node_modules/
└── request/
可以使用以下命令生成压缩包:
mkdir -p nodejs
cd nodejs
npm init -y
npm install request
cd ..
zip -r nodejs.zip nodejs
不要只压缩 node_modules 目录里的内容。Lambda 挂载 Layer 后,会从类似以下路径的位置查找 Node.js 模块:
/opt/nodejs/node_modules
在 Serverless 中关联 Layer
顶层的 Layer 定义可以继续保留:
layers:
nodejs:
package:
artifact: nodejs.zip
compatibleRuntimes:
- nodejs8.10
allowedAccounts:
- "*"
还需要在函数配置中显式添加 layers。如果函数直接定义在主配置文件中,可以这样写:
functions:
payment:
handler: src/handlers/payment.handler
layers:
- Ref: NodejsLambdaLayer
NodejsLambdaLayer 是 Serverless 根据 Layer 名称 nodejs 生成的 CloudFormation 逻辑 ID。
如果函数配置来自外部文件:
functions:
- '${file(src/handlers/${opt:api, "payment"}.serverless.yml)}'
则需要在对应的外部文件中为函数添加 Layer 引用,例如:
payment:
handler: payment.handler
layers:
- Ref: NodejsLambdaLayer
具体层级要与外部文件当前导出的函数结构保持一致。
为什么手动上传相同的 ZIP 可以运行
“相同 ZIP”不代表最终的 Lambda 配置也相同。可能出现以下情况:
- 手动操作时,ZIP 被当作函数代码包上传,其中直接包含了可加载的依赖。
- 手动创建 Layer 后,又在 AWS Console 中将其关联到了函数。
- Console 中的函数保留了之前关联的 Layer,而 Serverless 部署创建的是另一个没有关联到函数的新 Layer 版本。
- Serverless 部署的函数包排除了
node_modules,但手动上传的实际函数包仍包含依赖。
因此,需要比较函数的最终配置,不能只比较 ZIP 文件。可以打开 AWS Console 的函数页面,检查 Layers 区域,确认 Serverless 创建的 Layer 版本是否已经关联到该函数。
建议的完整配置方式
provider:
name: aws
runtime: nodejs8.10
stage: ${opt:api-type, 'uat'}-${opt:api, 'payment'}
region: ca-central-1
timeout: 30
memorySize: 128
package:
exclude:
- node_modules/**
- nodejs/**
layers:
nodejs:
package:
artifact: nodejs.zip
compatibleRuntimes:
- nodejs8.10
allowedAccounts:
- "*"
functions:
payment:
handler: src/handlers/payment.handler
layers:
- Ref: NodejsLambdaLayer
部署后,可以检查生成的 CloudFormation 模板,确认函数资源的 Layers 属性中包含 Layer 引用:
serverless package
然后查看:
.serverless/cloudformation-template-update-stack.json
其中应出现类似以下配置:
{
"Layers": [
{
"Ref": "NodejsLambdaLayer"
}
]
}
注意事项
request 已停止维护。现有项目仍可以通过 Layer 加载它,新代码则更适合使用运行时支持的 HTTP 能力或仍在维护的客户端库。
nodejs8.10 也已停止受 AWS Lambda 支持,应尽快升级到当前受支持的 Node.js 运行时。升级后需要重新安装依赖并构建 Layer。如果依赖包含原生二进制模块,还必须在与 Lambda 运行环境兼容的 Linux 环境中构建。
备注:内容仅供参考。
