まくろぐ

AWS CDK で TypeScript の Lambda 関数をデプロイする (aws-lambda-nodejs)

更新:
作成:

NodejsFunction コンストラクトについて

AWS CDK の @aws-cdk/aws-lambda-nodejs パッケージ が提供する NodejsFunction コンストラクト を使用すると、CDK によって作成した CloudFormation スタック上に簡単に Lambda 関数をデプロイできます。 Lambda 関数用の汎用的なコンストラクトとしては、@aws-cdk/aws-lambda パッケージ が提供する Functin コンストラクトもあるのですが、Node.js による Lambda 関数実装用に特化した NodejsFunction を使うと、次のような恩恵を得られます。

  • esbuild による Lambda 関数関連アセットの高速なパッケージング(バンドル)。
  • Lambda 関数の TypeScript コードをいちいちコンパイルしなくてよい。

ちょっとややこしいのですが、CDK コード(TypeScript で書いた場合)の実行は ts-code で実行され、Lambda 関数のビルドとパッケージングは esbuild で行われます。

Lambda 関数の作成

ここでは、CDK アプリとして作成した CloudFormation スタック内に、TypeScript で実装した Lambda 関数を配置してみます。 CDK のインストールや、ブートストラッピング (cdk bootstrap) は、下記の記事を参考に完了させてください。

CDK アプリの作成

まずは、cdk init app で CDK アプリのテンプレートを作成します。

$ mkdir myapp && cd myapp
$ cdk init app --language typescript

この時点で、cdk deploy コマンドを実行して、CloudFormation スタックが生成できるか試しておくのもよいです。

npm の代わりに yarn を使うときは下記も実行しておきます。

$ rm package-lock.json
$ yarn intall

Lambda 関数の実装

AWS SDK と Lambda 用の TypeScript 型情報をインストールします。

$ yarn add aws-sdk
$ yarn add @types/aws-lambda --dev

Lambda 関数のコードは、プロジェクトのルートに lambda ディレクトリを作成して、そこに配置していくことにします。

myapp/
  +-- bin/     ... CDK の App コンストラクト (.ts)
  +-- lambda/  ... ラムダ関数の実装コード (.ts) ★これを追加
  +-- lib/     ... CDK の Stack コンストラクトなど (.ts)
  ...

最低限の Hello World 的なラムダ関数を作成します。

lambda/index.ts
import { Handler } from 'aws-lambda'

// Lambda エントリーポイント
export const handler: Handler = async () => {
  console.log('Hello Lambda!')
}

Lambda 関数を含む CDK アプリのデプロイ

Lambda 関数用の AWS リソースを生成するために、NodejsFunction コンストラクト を使用したいので、次のようにコンストラクトパッケージをインストールします。

$ yarn add @aws-cdk/aws-lambda-nodejs

CDK アプリのひな型として、CloudFormation スタックを構築するためのコンストラクト (lib/myapp-stack.ts) が生成されているはずなので、そのスタック内に、NodejsFunction コンストラクトを生成するよう記述します。

lib/myapp-stack.ts
import * as cdk from '@aws-cdk/core'
import * as lambdaNodejs from '@aws-cdk/aws-lambda-nodejs'

export class MyappStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    new lambdaNodejs.NodejsFunction(this, 'HelloFunction', {
      entry: 'lambda/index.ts',
      // handler: 'handler', // デフォルトのハンドラ関数名は 'handler'
      // runtime: Runtime.NODEJS_14_X, // デフォルトは Node.js 14.x
      // timeout: cdk.Duration.minutes(15), // デフォルトは 3 秒
    })
  }
}

最小構成では、上記のように props オブジェクトの entry プロパティで Lambda 関数の index.ts ファイルパスを指定するだけで OK です。 あとは、おもむろに cdk deploy を実行すれば、Lambda 関数をデプロイすることができます。

$ cdk deploy

CDK によるデプロイ処理が完了したら、CloudFormation コンソール を開いて、実際にスタックと Lambda 関数が生成されているか確認しましょう。

TypeScript コードのトランスパイルも esbuild で自動でやってくれるし、ZIP パッケージ化とアップロードも自動でやってくれる(これは CDK の機能ですが)ので、とっても楽ですね!

Lambda 関数コードだけの高速デプロイ (hotswap)

cdk deploy の実行には結構時間がかかりますが、Lambda 関数のコードだけ更新したいときは、hotswap オプションを付けて実行することで高速にデプロイできます。

Lambda 関数だけ高速更新
$ cdk deploy --hotswap

これは開発時のみ使うべき機能として提供されており、Production 環境においては、通常通り CDK アプリ全体のデプロイを行うことが推奨されています。

トラブルシューティング

NodejsFunction の第1引数の this でエラーになるとき

NodejsFunction のコンストラクタの this を渡している部分で、次のような型情報エラーが発生するときは、aws-cdk 本体と、コンストラクトライブラリ(@aws-cdk/aws-lambda-nodejs など)のバージョンが合っていない可能性があります。

Argument of type ‘this’ is not assignable to parameter of type ‘Construct’. Type ‘MyappStack’ is not assignable to type ‘Construct’.

package.json を開いて、両者のバージョンを新しい方に揃えて、yarn install で更新すれば直ります。

デプロイ時に spawnSync docker ENOENT が出る場合

Windows や macOS で cdk diffcdk deploy を実行したときに、spawnSync docker ENOENT というエラーが発生する場合は、esbuild をインストール するとうまくいくようです。

$ yarn add --dev esbuild@0
あるいは
$ npm install --save-dev esbuild@0

関連記事

まくろぐ
サイトマップまくへのメッセージ