概要
前回に作成したファイルだと、バンドル時にハンドラファイルにスキーマファイルを含めてしまい、サイズが肥大化する問題があった。
今回、Lambda Layersを使用し、スキーマファイルを外だしできるようにする。
/opt/nodejs/ にレイヤーのパスが通るため、そこにスキーマファイルを入れるように設定した。
ソースの変更
Lambdaの定義ファイル
tsconfigを修正し、ソースを直接読みにいっていた部分を、デプロイ後のパスである/opt/nodejs/
以下のフォルダを見に行くようにする
backend/tsconfig.json
"paths": {
"@kartagraph-backend/*": ["./src/*"],
"@kartagraph-types/*": ["../types/src/*"],
- "@kartagraph-prisma-zod": ["../prisma/generated/zod/index.ts"],
+ "/opt/nodejs/*": ["../prisma/generated/zod/*"],
}
backend/src/domain/scenario/scenarioRepository.ts
import { execQuery } from '@kartagraph-backend/utils/repository';
- import { scenario_listSchema } from '@kartagraph-prisma-zod';
+ import { scenario_listSchema } from '/opt/nodejs/index';
import { ScenarioListItem } from '@kartagraph-types/index';
export const getScenarioList = async (): Promise<ScenarioListItem> => {
スキーマファイルを個別でバンドルする
prisma/package.json
{
"name": "@kartagraph/prisma-zod",
"scripts": {
+ "bundle": "esbuild --bundle generated/zod/index.ts --outfile=dist/index.js --target=node20 --format=cjs",
"generate": "prisma generate",
"pull": "prisma db pull"
},
"author": "",
"license": "ISC",
"dependencies": {
"@prisma/client": "^5.10.2",
"zod": "^3.22.4"
},
"devDependencies": {
+ "esbuild": "^0.20.1",
"prisma": "^5.10.2",
"zod-prisma-types": "^3.1.6"
}
}
バンドルしたファイルを含めたLayerを作成
infrastracture/lib/process/setup.ts
#!/usr/bin/env node
import * as childProcess from 'child_process';
import * as fs from 'fs-extra';
import * as path from 'path';
const nodeModulesPath = './bundle-node_modules';
export const NODE_LAMBDA_LAYER_DIR = path.resolve(process.cwd(), nodeModulesPath);
+ const zodSchemaPath = '../prisma/dist';
+ const commonModulesPath = './bundle-common';
+ export const COMMON_LAMBDA_LAYER_DIR = path.resolve(process.cwd(), commonModulesPath);
const NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME = `nodejs`;
const runtimeDirName = path.resolve(process.cwd(), `${nodeModulesPath}/${NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME}`);
const distFilePath = (file: string) => path.resolve(process.cwd(), `${nodeModulesPath}/${NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME}/${file}`);
const srcFilePath = (file: string) => path.resolve(`${process.cwd()}/../backend/${file}`);
export const bundleNpm = () => {
createNodeModules();
+ copyCommonModules();
};
const createNodeModules = () => {
copyPackageJson();
childProcess.execSync(`npm install --omit=dev`, {
cwd: getModulesInstallDirName(),
env: { ...process.env },
});
};
const copyPackageJson = () => {
fs.mkdirsSync(getModulesInstallDirName());
['package.json'].map((file) => fs.copyFileSync(srcFilePath(file), distFilePath(file)));
};
const getModulesInstallDirName = (): string => {
return runtimeDirName;
};
+ const copyCommonModules = () => {
+ const dist = path.resolve(process.cwd(), `${commonModulesPath}/${NODE_LAMBDA_LAYER_RUNTIME_DIR_NAME}`);
+ const src = path.resolve(`${process.cwd()}/${zodSchemaPath}`);
+ fs.mkdirsSync(`${commonModulesPath}`);
+ fs.copySync(src, dist);
+ };
infrastracture/bin/lambda-layer-version-deploy.ts
// 省略
bundleNpm();
new LambdaLayersStack(app, `${processEnv.PROJECT_ID}-LambdaLayersStack`, {
ssmKey: `${processEnv.SSM_PARAM_KEY_LAYER_VERSIONS_ARN}-${processEnv.PROJECT_ID}`,
+ commonSsmKey: `${processEnv.SSM_PARAM_KEY_LAYER_VERSIONS_ARN}-${processEnv.PROJECT_ID}-common`,
env,
});
infrastracture/lib/lambda-layer-stack.ts
// 省略
interface LambdaLayersStackProps extends StackProps {
ssmKey: string;
+ commonSsmKey: string;
}
export class LambdaLayersStack extends Stack {
constructor(scope: Construct, id: string, props: LambdaLayersStackProps) {
super(scope, id, props);
const nodeModulesLayer = new lambda.LayerVersion(this, 'NodeModulesLayer', {
code: lambda.AssetCode.fromAsset(NODE_LAMBDA_LAYER_DIR),
compatibleRuntimes: [RUNTIME_VERSION],
});
// 省略
+ const commonModulesLayers = new lambda.LayerVersion(this, 'CommonModulesLayer', {
+ code: lambda.AssetCode.fromAsset(COMMON_LAMBDA_LAYER_DIR),
+ compatibleRuntimes: [RUNTIME_VERSION],
+ });
+ const commonArnParameter = new StringParameter(this, 'ssm-layer-version-common', {
+ parameterName: props.commonSsmKey,
+ stringValue: commonModulesLayers.layerVersionArn,
+ description: 'layer version arn for common lambda',
+ });
Aspects.of(this).add(new Tag('Stack', id));
}
}
infrastracture/package.json
{
// 省略
"scripts": {
// 省略
+ "predeploy-layer": "cd ../prisma && npm run bundle",
"deploy-layer": "cdk -a \"npx ts-node --prefer-ts-exts bin/lambda-layer-version-deploy.ts\" deploy --all",
"deploy-rest": "cdk -a \"npx ts-node --prefer-ts-exts bin/rest.ts\" deploy --all"
},
// 省略
}
スキーマファイル入りのレイヤーを使用するようにする
infrastracture/lib/rest-stack.ts
// 省略
const bundling = {
externalModules: [
'@neondatabase/serverless'
, '@aws-sdk/client-s3'
+ , '/opt/nodejs/*'
],
};
interface Props extends core.StackProps {
projectId: string;
ssmLambdaLayerKey: string;
+ ssmLambdaCommonLayerKey: string;
ssmAPIGWUrlKey: string;
apiVersion: string;
neonEndpoint: string;
cloundFrontDomain: string;
mediaBucketName: string;
}
// 省略
export class KartaGraphRESTAPIStack extends core.Stack {
private apiRoot: apigateway.Resource;
private resourceMap = new Map<string, apigateway.Resource>();
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props);
// 省略
const defaultLambdaProps = this.createLambdaProps({
ssmKeyForLambdaLayerArn: props.ssmLambdaLayerKey,
+ ssmKeyForLambdaCommonLayerArn: props.ssmLambdaCommonLayerKey,
environment: { ...commonEnv },
timeoutSec: 5, // 外部エンドポイントを経由してJSONを処理するため3秒では足りない
});
// 省略
}
// 省略
private createLambdaProps(props: {
ssmKeyForLambdaLayerArn: string;
+ ssmKeyForLambdaCommonLayerArn: string;
environment?: Record<string, string>;
initialPolicy?: iam.PolicyStatement[];
timeoutSec?: number;
}) {
const lambdaLayerArn = StringParameter.valueForStringParameter(this, props.ssmKeyForLambdaLayerArn);
+ const commonLambdaLayerArn = StringParameter.valueForStringParameter(this, props.ssmKeyForLambdaCommonLayerArn);
const layers = [
LayerVersion.fromLayerVersionArn(this, 'node_modules-layer', lambdaLayerArn),
+ LayerVersion.fromLayerVersionArn(this, 'common-layer', commonLambdaLayerArn),
];
// 同じStack上でLayerVersionを作っていない場合、cdk synthで sam local 実行用のoutputを作るときにレイヤーを使うとエラーになる。
const layerSettings = !!process.env['CDK_SYNTH'] ? {} : { bundling, layers };
return {
runtime: RUNTIME_VERSION,
...layerSettings,
environment: props.environment,
initialPolicy: props.initialPolicy,
timeout: props.timeoutSec ? core.Duration.seconds(props.timeoutSec) : undefined,
};
}
// 省略
}
参考
cdkv2を使って別Stackで作ったlambda layerをlambdaに適用したメモ
aws-cdk v2でlambda layers を扱ったメモ
dbマイグレーションの型付けのファイル容量を減らす
Deploying Prisma with AWS Lambda Layers in CDK