LoginSignup
0
0

More than 3 years have passed since last update.

AWS CDKでLambda Layerを設定しLambdaから読み出してローカル実行を行ったメモ

Last updated at Posted at 2021-03-13

概要

前回にLambdaの実行を確認できた。
生成物を確認し、必要とするモジュールもバンドルされていることも確認できた。
Lambda間で共通のモジュールを使う場合、すべてのLambdaにバンドルするのは無駄である。(容量も重くなるしバンドル時間もかかる)
AWSにはLambda Layerという仕組みがあるのでそれを使って解決する。

AWS CDK を使って node_modules を AWS Lambda Layers にデプロイするサンプルを見れば実現できる。
ただし、Windowsで動かすときに少しハマったので備忘録を残す。

ソースコード

やること

SAMを使ったローカル実行

やらないこと

デプロイ

ソースコード修正

ビルド設定

  • Lambda Layerにいれる成果物をバンドルのときに作成できるように pre-prrocess追加
bin/sample-index.ts
#!/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の記述修正

  • stackにLamba Layerの作成を記述する
lib/sample-stack.ts


+ 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],
    });

  }
}

全文
lib/sample-stack.ts
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', // 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"')), // バグってそう.二重で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を使ってディレクトリを指定するよう修正して対応
lib/process/setup.ts
  // install package.json (production)
  childProcess.execSync(`npm  install --production`, {
    cwd: getModulesInstallDirName(),
    // bundle時にパイプで出力するtemplate.yamlに、余分な文字列が含まれてしまわないように出力はオフ
    stdio: ['ignore', 'ignore', 'ignore'],
    env: { ...process.env },
    shell: 'bash'
  });

全文
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 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 = () => {
  // create bundle directory
  copyPackageJson();

  // install package.json (production)
  childProcess.execSync(`npm  install --production`, {
    cwd: getModulesInstallDirName(),
    // bundle時にパイプで出力するtemplate.yamlに、余分な文字列が含まれてしまわないように出力はオフ
    stdio: ['ignore', 'ignore', 'ignore'],
    env: { ...process.env },
    shell: 'bash'
  });
};

const copyPackageJson = () => {

  // copy package.json and 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回目からはそこまででもないかも

image.png

参考

AWS CDK を使って node_modules を AWS Lambda Layers にデプロイするサンプル
aws lambda layer

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