😱

AWS CDK(aws-lambda-nodejs)のデプロイ時にNode.js v14非対応のpnpm v8が使われエラーになる問題を解消

2023/03/29に公開

発端

2023/03/28、何もコードを変更していないのに、aws-lambda-nodejs を使った Lambda 関数へのコードのデプロイができなくなってしまいました…。エラーは以下。

ERROR: This version of pnpm requires at least Node.js v16.4
The current version of Node.js is v14.21.3
Visit https://r.pnpm.io/comp to see the list of past pnpm versions with respective Node.js version support.

以下は、AWS CDK に上がっている Issue になります。

CDK Deployment crashes: Dockerfile aws-lambda-nodejs/lib/Dockerfile has unsupported version of NodeJs 14 for new pnpm version · Issue #24820 · aws/aws-cdk

aws-lambda-nodejs とは?

aws-lambda-nodejs は TypeScript で書かれたコードを自動的にトランスパイル & バンドルしてくれるパッケージです(以下のリンクは AWS CDK v1 のやつ)
@aws-cdk/aws-lambda-nodejs - npm

コードとしては、以下のように記述します。これだけで、cdk deploy 時にトランスパイル & バンドルまで自動的にやってくれます。

// AWS CDK v2 の場合
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
// AWS CDK v1 の場合
import { NodejsFunction } from "@aws-cdk/aws-lambda-nodejs";

new NodejsFunction(this, "MyFunction", {
  entry: "/path/to/my/file.ts", // accepts .js, .jsx, .ts, .tsx and .mjs files
  handler: "myExportedFunc", // defaults to 'handler'
});

内部的には、esbuild でバンドルしており、esbuild がローカルにインストールされている場合はローカルでバンドルされます。ローカルに esbuild がない場合は、AWS CDK 側で用意されている Docker ファイルをもとに Docker イメージを作り、Docker コンテナ上でバンドルします。

今回問題になったのは、Docker コンテナのイメージ作成時に使われる Docker ファイルの記述です。

何が原因だったのか?

前述の通り、aws-lambda-nodejs は Docker コンテナ上で TypeScript のコードからバンドルを行っています。このとき使用される Docker ファイルの中身を見てみると pnpm がインストールされています。そして、少なくとも、2023/03/29 14 時時点ではバージョンが固定されていませんでした。

# Install pnpm
RUN npm install --global pnpm

以下のコードから抜粋。
https://github.com/aws/aws-cdk/blob/07d3aa74e6c1a7b3b7ddf298cf3cc4b7ff180b48/packages/%40aws-cdk/aws-lambda-nodejs/lib/Dockerfile#L10

コレの何が問題かと言うと、latest(2023/03/29 14 時時点)の pnpm v8.x 以降がインストールされてしまう点です。

pnpm は v8.x 系から Node.js v14 以下のバージョンに対応しなくなりました。上述の Docker ファイルの記述ではバージョン固定されていないため、Node.js v14 を使っている場合でも v8.x 系の pnpm がインストールされるようになってしまいました。

結果として、AWS CDK のコードは何もいじってないのに、Node.js v14 以下のコードをデプロイしようとすると突然、バンドル時にエラーが発生するようになった…というわけです。

影響範囲

  • AWS CDK
    • <= v1.198.0 以下
    • <= v2.70.0 以下
  • Node.js v14 以下

解決策(ワークアラウンド)

patch-package をインストール

patch-package という npm パッケージを使って、パッチを作成します。
ds300/patch-package: Fix broken node modules instantly 🏃🏽‍♀️💨

yarn を使っている場合は、以下の通りインストールします。

yarn add patch-package postinstall-postinstall --dev

パッチを作成する

該当のコードを node_modules から探します。AWS CDK v2 を使っている場合は、以下のようなパスになります。

node_modules/aws-cdk-lib/aws-lambda-nodejs/lib/Dockerfile

上記の Dockerfile を開き、pnpmpnpm@7.30.5 に変更します。

 # Install pnpm
- RUN npm install --global pnpm
+ RUN npm install --global pnpm@7.30.5

以下のコマンドを実行します。

yarn patch-package aws-cdk-lib

すると、ディレクトリに patches/aws-cdk-lib+2.62.1.patch みたいな名前のパッチファイルが作成されます。

最後に、node_modules のファイルを変更したら、package.json に以下のコマンドを追加します(yarn install 後にパッチを当てる処理を記述します)

"scripts": {
  ...
  "postinstall": "patch-package",
  ...
},

最終確認

デプロイを実行して、ログで以下のように pnpm がバージョン固定されてインストールされたら成功です!

Step 4/13 : RUN npm install --global pnpm@7.30.5
 ---> Running in 12abc0012345

added 1 package in 602ms
CureApp テックブログ

Discussion