使用AWS CDK设置Lambda Layer并从Lambda中读取并进行本地执行的备忘录
总结
我之前已经确认了Lambda的执行情况。
我已经确认了生成物并且确认了必要的模块也已经被打包。
如果要在Lambda之间使用共同的模块,把它们都打包到每个Lambda中是浪费的(占用空间且打包时间长)。
AWS有一个叫Lambda Layer的机制可以用来解决这个问题。
如果你想使用AWS CDK来将node_modules部署到AWS Lambda Layers,你可以参考一个示例来实现。然而,我在Windows系统上遇到了一些问题,所以在此留下备忘录。
源代码
要做的事情。
使用SAM进行本地执行
不做的事情
部署
源代码修正
建置設定
- Lambda Layerにいれる成果物をバンドルのときに作成できるように pre-prrocess追加
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { SampleStack } from '../lib/sample-stack';
import { bundleNpm } from '../lib/process/setup';
+ // pre-process
+ bundleNpm();
// create app
const app = new cdk.App();
new SampleStack(app, 'SampleStack2021');
修改堆栈的描述
- stackにLamba Layerの作成を記述する
+ import { NODE_LAMBDA_LAYER_DIR } from './process/setup';
export class SampleStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
+ const nodeModulesLayer = new lambda.LayerVersion(this, 'NodeModulesLayer',
+ {
+ code: lambda.AssetCode.fromAsset(NODE_LAMBDA_LAYER_DIR),
+ compatibleRuntimes: [lambda.Runtime.NODEJS_14_X]
+ }
+ );
new NodejsFunction(this, 'test', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/test.ts',
functionName: 'kotatest',
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'date-fns', // Layrerに入れておきたいモジュール
],
define: { // Replace strings during build time
'process.env.API_KEY': JSON.stringify(JSON.stringify('"xxx-xxx"')),
},
},
+ layers: [nodeModulesLayer],
});
}
}
import * as cdk from ‘@aws-cdk/core’;
import { NodejsFunction } from ‘@aws-cdk/aws-lambda-nodejs’;
import * as lambda from ‘@aws-cdk/aws-lambda’;
import { NODE_LAMBDA_LAYER_DIR } from ‘./process/setup’;
export class SampleStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new NodejsFunction(this, ‘hello’, {
runtime: lambda.Runtime.NODEJS_14_X,
entry: ‘src/lambda/handlers/hello.ts’,
functionName: ‘kotahello’,
handler: ‘lambdaHandler’
});
const nodeModulesLayer = new lambda.LayerVersion(this, ‘NodeModulesLayer’,
{
code: lambda.AssetCode.fromAsset(NODE_LAMBDA_LAYER_DIR),
compatibleRuntimes: [lambda.Runtime.NODEJS_14_X]
}
);
new NodejsFunction(this, ‘test’, {
runtime: lambda.Runtime.NODEJS_14_X,
entry: ‘src/lambda/handlers/test.ts’,
functionName: ‘kotatest’,
bundling: {
externalModules: [
‘aws-sdk’, // 使用 Lambda 运行时中可用的 ‘aws-sdk’
‘date-fns’, // 希望放在 Layer 中的模块
],
define: { // 在构建时替换字符串
‘process.env.API_KEY’: JSON.stringify(JSON.stringify(‘”xxx-xxx”‘)), // 似乎存在错误。 必须对字符串进行两次 stringify,否则会出现 Invalid define value 错误
},
},
layers: [nodeModulesLayer],
});
}
}
Layer的成果物制作
-
- pre-prrocessで呼び出される
Lambda Layerにアップロードするディレクトリの作成
package.json, package-lock.jsonのコピー
npm installでnode_modulesディレクトリを作成
参考のnpm –prefix ${getModulesInstallDirName()} install –productionだとnpm installされずにシンボリックリンクが作成されてしまった
以下のようにcwdを使ってディレクトリを指定するよう修正して対応
// install package.json (production)
childProcess.execSync(`npm install --production`, {
cwd: getModulesInstallDirName(),
// bundle時にパイプで出力するtemplate.yamlに、余分な文字列が含まれてしまわないように出力はオフ
stdio: ['ignore', 'ignore', 'ignore'],
env: { ...process.env },
shell: 'bash'
});
#!/usr/bin/env node
import * as childProcess from ‘child_process’;
import * as fs from ‘fs-extra’;
import * as path from ‘path’
const bundlePath = ‘./bundle’;
export const NODE_LAMBDA_LAYER_DIR = path.resolve(process.cwd(), bundlePath);
const NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME = `nodejs`;
const runtimeDirName = path.resolve(process.cwd(), `${bundlePath}/${NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME}`);
const distFilePath = (file: string) => path.resolve(process.cwd(), `${bundlePath}/${NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME}/${file}`)
const srcFilePath = (file: string) => path.resolve(`${process.cwd()}/${file}`)
export const bundleNpm = () => {
// 创建 bundle 目录
copyPackageJson();
// 安装 package.json (production)
childProcess.execSync(`npm install –production`, {
cwd: getModulesInstallDirName(),
// 在打包时不输出多余的字符串到 template.yaml 文件中
stdio: [‘ignore’, ‘ignore’, ‘ignore’],
env: { …process.env },
shell: ‘bash’
});
};
const copyPackageJson = () => {
// 复制 package.json 和 package.lock.json
fs.mkdirsSync(getModulesInstallDirName());
[‘package.json’, ‘package-lock.json’]
.map(file => fs.copyFileSync(srcFilePath(file), distFilePath(file)));
};
const getModulesInstallDirName = (): string => {
return runtimeDirName;
};
实践
-
- 動いた
-
- Build imageが大分長くなった気がする。。。
2回目からはそこまででもないかも

请参考
考虑这个问题时,请参考这个方案。
使用AWS CDK将node_modules部署到AWS Lambda Layers的示例。