LoginSignup
0
0

More than 1 year has passed since last update.

cdkv2を使って別Stackで作ったlambda layerをlambdaに適用したメモ

Last updated at Posted at 2022-08-07

概要

前回のLambda群はlayerを使用しておらず、無駄なbundleが発生している。
また、同一stackでlambda layer を作成すると、毎回作り直しになるのも無駄である。
今回は、別StackでLambdaLayerを作成し、SSMにarnを保存して使用する方法をとる。

ソースコード

環境

  • Windows10
  • gitbash
  • aws-cli/2.7.21 Python/3.9.11 Windows/10 exe/AMD64 prompt/off
  • cdk v2.35
  • node v16.16.0

ディレクトリ構成

- package.json # lambdaで使用するライブラリを定義
- cdk
  - .env         # 環境変数定義
  - package.json # スクリプト定義
  - bin
    - lambda-layer-version-deploy.ts # lambda layerのデプロイ
    - lambdaWithCognito.ts           # lambdaのデプロイ
  - lib
    - lambda-layers-stack.ts
    - LambdaWithCognitoStack.ts
    - process
      - pre.ts
      - setup.ts

ソース

スクリプト

cdk/package.json
{
  "scripts": {
    "createLayer": "ts-node lib/process/pre.ts",
+    "create-layer-for-lambda-with-cognito-deploy": "npm run createLayer && npm run copyData && cdk -a \"npx ts-node --prefer-ts-exts bin/lambda-layer-version-deploy.ts\" deploy  -c @aws-cdk/core:newStyleStackSynthesis=true --profile hoge",
    "lambda-with-cognito-deploy": "cdk -a \"npx ts-node --prefer-ts-exts bin/lambdaWithCognito.ts\" deploy  -c @aws-cdk/core:newStyleStackSynthesis=true --profile hoge"  
   }
}

lambda_layerのデプロイファイル作成

  • lambda layerにデプロイする node_modulesを作成。
    • npm install --productiondependencyのみをインストール
cdk/lib/process/pre.ts
import { bundleNpm } from "./setup";
bundleNpm();
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);
export const bundleNpm = () => {
  createNodeModules();
};

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()}/../${file}`)

const createNodeModules = () => {
  copyPackageJson();
  childProcess.execSync(`npm install --production`, {
    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;
};
package.json
{
  "dependencies": {
    "aws-jwt-verify": "^3.1.0",
    "aws-sdk": "^2.1189.0",
    "date-fns": "^2.29.1",
    "pg": "^8.7.3"
  },
  "devDependencies": {
    "@types/aws-lambda": "^8.10.101",
    "@types/pg": "^8.6.5",
    "esbuild": "^0.14.53",
    "typescript": "^4.7.4"
  }
}
  • lambda_layerに入っているモジュールをlabdaのバンドル対象から外す設定を記載しておく
cdk/constants/lambda-layer.ts
export const externalModules = [
  'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
  'date-fns', // Layrerに入れておきたいモジュール
  'pg',
  'aws-jwt-verify'
]

lambda layerのデプロイ

  • SSMのkeyは任意の名前。今回は/layer_versions_arnとした。
.env
# 省略
# lambda layerのarn
SSM_PARAM_KEY_LAYER_VERSIONS_ARN=/layer_versions_arn
cdk/bin/lambda-layer-version-deploy.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as dotenv from 'dotenv'
import * as cdk from 'aws-cdk-lib';
import { LambdaLayersStack } from '../lib/lambda-layers-stack';
dotenv.config()
const envList = [
  'SSM_PARAM_KEY_LAYER_VERSIONS_ARN'
] as const
for (const key of envList) {
  if (!process.env[key]) throw new Error(`please add ${key} to .env`)
}
const processEnv = process.env as Record<typeof envList[number], string>
const app = new cdk.App();
const env = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
}

new LambdaLayersStack(app, 'LambdaLayersStack', {
  ssmKey: processEnv.SSM_PARAM_KEY_LAYER_VERSIONS_ARN,
  env,
});
cdk/lib/lambda-layers-stack.ts
import { Aspects, Stack, StackProps, Tag, Tags } from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import { NODE_LAMBDA_LAYER_DIR } from './process/setup';
import { StringParameter } from 'aws-cdk-lib/aws-ssm';

interface LambdaLayersStackProps extends StackProps {
  ssmKey: 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: [lambda.Runtime.NODEJS_14_X]
      }
    );

    // Lambda Layer参照用にarnを保存
    const layerArnParameter = new StringParameter(this, "ssm-layer-version", {
      parameterName: props.ssmKey,
      stringValue: nodeModulesLayer.layerVersionArn,
      description: 'layer version arn for lambda'
    });
    Tags.of(layerArnParameter).add('Name', 'ssm-layer-version');

    Aspects.of(this).add(new Tag('Stack', id));
  }
}

lambdaのデプロイ

cdk/bin/lambdaWithCognito.ts
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from 'aws-cdk-lib'
import { LambdaWithCognitoStack } from '../lib/LambdaWithCognitoStack'
import * as dotenv from 'dotenv'

// 依存関係: lambda-layer-vesion-deploy
// create-layer-for-lambda-with-cognito-deploy を事前に行い、Lambda LayerのArnをSSMに保存していること

dotenv.config()
const envList = [
  'PROJECT_ID',
  'DOMAIN_PREFIX',
  'CALLBACK_URLS',
  'LOGOUT_URLS',
  'FRONTEND_URLS',
+  'SSM_PARAM_KEY_LAYER_VERSIONS_ARN'
] as const
for (const key of envList) {
  if (!process.env[key]) throw new Error(`please add ${key} to .env`)
}
const processEnv = process.env as Record<typeof envList[number], string>

const app = new cdk.App()
const env = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
}
const projectId = processEnv.PROJECT_ID

new LambdaWithCognitoStack(app, `${projectId}-lambda-with-cognito-stack`, {
  projectId,
  domainPrefix: processEnv.DOMAIN_PREFIX,
  callbackUrls: processEnv.CALLBACK_URLS.split(','),
  logoutUrls: processEnv.LOGOUT_URLS.split(','),
  frontendUrls: processEnv.FRONTEND_URLS.split(','),
+  ssmKeyForLambdaLayerArn: processEnv.SSM_PARAM_KEY_LAYER_VERSIONS_ARN,
  env
})
cdk/lib/LambdaWithCognitoStack.ts
import * as lambda from 'aws-cdk-lib/aws-lambda'
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
+ import * as ssm from 'aws-cdk-lib/aws-ssm';
+ import { externalModules } from '../constants/lambda-layer'
// 省略
interface Props extends StackProps {
  projectId: string
  domainPrefix: string
  frontendUrls: string[]
  callbackUrls: string[]
  logoutUrls: string[]
+  ssmKeyForLambdaLayerArn: string
}

export class LambdaWithCognitoStack extends Stack {
  constructor(scope: Construct, id: string, props: Props) {
    // 省略
+    const lambdaLayerArn = ssm.StringParameter.valueForStringParameter(this, props.ssmKeyForLambdaLayerArn);
    const baseFunctionProps = {
      runtime: lambda.Runtime.NODEJS_14_X,
+      layers: [
+        lambda.LayerVersion.fromLayerVersionArn(this, 'node_modules-layer', lambdaLayerArn)
+      ],
+      bundling: {
+        externalModules
+      }
    }
    const handler = new NodejsFunction(
      this,
      `${props.projectId}-hello-lambda`,
      {
        ...baseFunctionProps,
        entry: '../src/handler/api/hello.ts',
        functionName: 'hello',
        description: 'ハロー',
      },
    )

実行

gitbashで以下のコマンドを実行。

npm run create-layer-for-lambda-with-cognito-deploy
npm run lambda-with-cognito-deploy

確認

layerが設定された。

image.png

複数Layerの適用

node_modulesが2つになるが、マージされて普通に使用可能。
今回は、検証のみに必要なモジュールがあるため、それだけ別レイヤに分けて試してみた。

変更コミット

参考

AWS CDK - Lambda への Layer(ARN)のアタッチ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0