📀

AWS CDK で Typescriptで記述したLambdaをデプロイする方法

2022/08/26に公開
2

まずは結論から

aws-cdk-libモジュールのaws_lambda_nodejsを使います

フォルダ構成

lib
└── sample-cdk-stack.ts
lambda
└── post-item.ts
import {
  aws_lambda_nodejs
} from "aws-cdk-lib"
import {
  Runtime
} from "aws-cdk-lib/aws-lambda";

export class SampleCdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    
    // ~~ DynamoDBの定義などいろいろ省略 ~~
    
    
    const postItemFunction = new aws_lambda_nodejs.NodejsFunction(this, "postItemFunction", {
      entry: path.join(__dirname, '../lambda/post-item.ts'),
      runtime: Runtime.NODEJS_16_X,
      handler: 'handler',
      environment: {
        TABLE_NAME: dynamoTable.tableName,
        PRIMARY_KEY: "id",
      },
    });
 }
}

cdk deploy を実行すると、TSのコンパイルが走ったような表示が出た後、無事にデプロイされます。

経緯

  1. 参考コードを見ているとデプロイするLambdaがTSで記述されている
  2. 特に何も設定をせずにそのままデプロイしてるので、真似してデプロイしてみる
  3. デプロイ自体は成功するが、Lambdaの実行をすると、このようなエラーが出る "errorMessage": "Error: Cannot find module 'post-item'\nRequire stack:\n- /var/runtime/index.mjs"
  4. エラー内容的に、Lambdaの内部でpost-itemファイルを探しているが、見つからないというエラーの様子
  5. TSファイルをそのまま上げているのがまずいんじゃない?
  6. JSにコンパイルしてから上げる方法を探す
  7. aws_lambda_nodejs.NodejsFunction を使えばいいらしい
  8. 出来たけど懸念事項がある

詳細

参考コードを見ているとデプロイするLambdaがTSで記述されている

https://dev.classmethod.jp/articles/aws-cdk-101-typescript/

こちらのコードを参考にさせていただきました
現在のAWS CDKはVersion2で、参考コードがVersion1なので、書き換えながら進めました
Lambda, APIGateway, DynamoDBの構成作成が非常に捗りました、ありがとうございます

エラーが出ていた時のコード、こんな感じ
(参考コードから少し変えています)

import {
  Runtime, Function, AssetCode
} from "aws-cdk-lib/aws-lambda";

export class SampleCdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    
    // ~~ DynamoDBの定義などいろいろ省略 ~~
    
    
    const postItemFunction = new Function(this, "postItemFunction", {
      code: new AssetCode("lambda"),
      handler: "post-item.handler",
      runtime: Runtime.NODEJS_16_X,
      environment: {
        TABLE_NAME: dynamoTable.tableName,
        PRIMARY_KEY: "id",
      },
    });
 }
}

デプロイ自体は成功するが、Lambdaの実行をするとエラーが出る

デプロイ自体は成功ですが、Lambdaのテスト実行を行うとエラーが出てしまいました

こんなエラーです
(CloudWatch Logs)

{
    "errorType": "Runtime.ImportModuleError",
    "errorMessage": "Error: Cannot find module 'post-item'\nRequire stack:\n- /var/runtime/index.mjs",
    "stack": [
        "Runtime.ImportModuleError: Error: Cannot find module 'post-item'",
        "Require stack:",
        "- /var/runtime/index.mjs",
        "    at _loadUserApp (file:///var/runtime/index.mjs:726:17)",
        "    at async Object.module.exports.load (file:///var/runtime/index.mjs:741:21)",
        "    at async file:///var/runtime/index.mjs:781:15",
        "    at async file:///var/runtime/index.mjs:4:1"
    ]
}

Lambdaは「ハンドラー」の設定で、実行するファイルと関数を決定しているようで、Node.jsのランタイムだと「ハンドラー」を hoge.handlerに設定していたら、 hoge.js ファイルのhandler関数を実行するようです

マネジメントコンソールだと、こんな感じ
(index.jsのhandler関数を実行する)

aws_lambda_nodejs.NodejsFunction を使えばいいらしい

「aws-cdk-libモジュールのaws_lambda_nodejsを使えばいいらしい」

と、調べていると出てきたのでこちらを試してみました

それで出来上がったのが、最初にも掲載したこちらのコードになります

import {
  aws_lambda_nodejs
} from "aws-cdk-lib"
import {
  Runtime
} from "aws-cdk-lib/aws-lambda";

export class SampleCdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    
    // ~~ DynamoDBの定義などいろいろ省略 ~~
    
    
    const postItemFunction = new aws_lambda_nodejs.NodejsFunction(this, "postItemFunction", {
      entry: path.join(__dirname, '../lambda/post-item.ts'),
      runtime: Runtime.NODEJS_16_X,
      handler: 'handler',
      environment: {
        TABLE_NAME: dynamoTable.tableName,
        PRIMARY_KEY: "id",
      },
    });
 }
}

出来たけど懸念事項がある

ひとまずデプロイして実行も出来ましたが、気になる点がありました

それは、 new Function を使っているときと new aws_lambda_nodejs.NodejsFunction を使っているときでパラメータが異なっていること

aws_lambda_nodejs.NodejsFunctionが後追いで作られた機能だとしたら、Function 側にパラメータを寄せる気がします

見ていただきたいのは後半部分の差分です

 import {
-  Runtime, Function, AssetCode
+  aws_lambda_nodejs
+} from "aws-cdk-lib"
+import {
+  Runtime
 } from "aws-cdk-lib/aws-lambda";
+ import * as path from 'path'

 export class SampleCdkStack extends Stack {
   constructor(scope: Construct, id: string, props?: StackProps) {
     super(scope, id, props);

     // ~~ DynamoDBの定義などいろいろ省略 ~~


-    const postItemFunction = new Function(this, "postItemFunction", {
-      code: new AssetCode("lambda"),
-      handler: "post-item.handler",
+    const postItemFunction = new aws_lambda_nodejs.NodejsFunction(this, "postItemFunction", {
+      entry: path.join(__dirname, '../lambda/post-item.ts'),
+      handler: 'handler',
       runtime: Runtime.NODEJS_16_X,
       environment: {
         TABLE_NAME: dynamoTable.tableName,
         PRIMARY_KEY: "id",
       },
     });
  }
 }

Functionでは code
NodejsFunctionでは entry
という設定値になっています
パラメータの値も若干変わってしまいました

ここが気になるので、もっと良いやり方がありそう、と思いつつも現状分かっているところまで記事化しました

2022/08/27 追記
コメント頂いたので、追記します!ありがとうございます。

aws-lambda-node.js は Node.js製のLambdaを構築するために特化したパッケージです。
aws-lambda-pythonaws-lambda-go と、それぞれに特化したパッケージがあります。

従来の aws-cdk-lib/aws-lambdaFunction では code でコードの配置されているフォルダを指定する必要があります。
こちらだと、該当のソースがあるフォルダごとLambdaにデプロイされていました。
NodejsFunction では entry で単一のファイルを指定できるため、より使いやすくなっている、という感じのようです。

参考記事

NodejsFunction周りで参考にした記事がいくつかあるので、気になる方はご覧くださいー!

https://dev.classmethod.jp/articles/aws-cdk-101-typescript/
https://dev.classmethod.jp/articles/lambda-bundling-via-aws-cdk/#toc-2
https://bobbyhadz.com/blog/aws-cdk-typescript-lambda
https://tech.basicinc.jp/articles/213
https://qiita.com/misaosyushi/items/104445be7d7d3ba304bc

Discussion

tmokmsstmokmss

こんにちは!

aws_lambda_nodejs.NodejsFunctionが後追いで作られた機能だとしたら、Function 側にパラメータを寄せる気がします

NodejsFunctionFunction をNode.js向けに抽象化したものなので、パラメータもより便利な形に変わってます
記事内のコードの通り、code ではなく entry を指定する形で大丈夫ですよー
AssetCode を意識せずとも、ただファイルのパスだけ指定すれば動くようになっています

酢ろう酢ろう

コメントありがとうございます!

NodejsFunction は Function をNode.js向けに抽象化したものなので、パラメータもより便利な形に変わってます

コメント頂いたことで、改めて他記事を漁りNodejsFunctionへの理解を深めることができました。

確かに、Function からの互換性というよりも、 entry の形式でファイルパスをそのまま指定する方が判りやすいですね。

記事に参考記事含めて色々追記させていただきますー!