これは、なにをしたくて書いたもの?
AWS Lambda関数を書く時にAWS SAMをよく使っているのですが、Node.jsを使ってsam build
した時に.aws-sam
ディレクトリに
package-lock.json
をコピーせずにnpm install
してしまうイメージがありました。
実際に前はそうだったのですが、ちゃんと確認してみるとこの挙動が変わっていたので、書いておこうかなと。
また、npm ci
も使えるようになったみたいです。
AWS SAMとpackage-lock.json
最初に書いたとおり、以前のAWS SAMを使ってNode.jsのAWS Lambda関数に対してsam build
するとpackage-lock.json
をコピーしてくれない
(たとえプロジェクト内には存在していても)という挙動だったのですが、これが以下のPull Requestで修正されているようです。
ビルド時のNodejsNpmrcCopyAction
というアクションで.npmrc
しかコピーしてくれなかったのが、加えてpackage-lock.json
と
npm-shrinkwrap.json
が存在していればコピーしてくれるNodejsNpmrcAndLockfileCopyAction
アクションになったようです。
2022年3月の修正ですね。
ソースコードとしては、こちらが該当します。
AWS SAM CLIのバージョンとしては1.41.0での内容みたいです。
https://github.com/aws/aws-sam-cli/blob/v1.41.0/requirements/reproducible-linux.txt#L15
Release aws-lambda-builders v1.14.0 release · aws/aws-lambda-builders · GitHub
また、ドキュメントを読んでいるとtemplate.yaml
のMetadata
/ BuildProperties
配下にUseNpmCi: True
と記述することでnpm ci
を
実行してくれるようにもなっているみたいです。
Node.js アプリケーションでは、npm installの代わりに、npm ciを使用して依存関係をインストールします。npm ci を使用するには、Lambda 関数の Metadata リソース属性の BuildProperties の下に UseNpmCi: True を指定します。npm ci を使用するには、アプリケーションは Lambda 関数の CodeUri に package-lock.json または npm-shrinkwrap.json ファイルが必要です。
AWS Serverless Application Model / アプリケーションの構築 / 例
こんなところに書いてあるんだ、という気はしますが…。
今回は、これらを確認してみようと思います。
環境
$ sam --version SAM CLI, version 1.53.0
利用するNode.jsのバージョン。
$ node --version v16.16.0 $ npm --version 8.11.0
AWS SAMプロジェクトを作成する
まずは、AWS SAMプロジェクトを作成します。ランタイムはnodejs16.x
で、Amazon API Gateway+AWS Lambdaのテンプレートを選択。
$ sam init --name sam-nodejs-npm-ci --runtime nodejs16.x --app-template hello-world --package-type Zip --no-tracing
作成されたプロジェクト内に移動。
$ cd sam-nodejs-npm-ci
ディレクトリツリー内に移動。
$ tree . ├── README.md ├── events │ └── event.json ├── hello-world │ ├── app.js │ ├── package.json │ └── tests │ └── unit │ └── test-handler.js └── template.yaml 4 directories, 6 files
package.json
を確認すると、dependencies
とdevDependencies
が含まれているので、こちらをこのまま使うことにします。
hello-world/package.json
{ "name": "hello_world", "version": "1.0.0", "description": "hello world sample for NodeJS", "main": "app.js", "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", "author": "SAM CLI", "license": "MIT", "dependencies": { "axios": ">=0.21.1" }, "scripts": { "test": "mocha tests/unit/" }, "devDependencies": { "chai": "^4.2.0", "mocha": "^9.1.4" } }
そのままビルドしてみる
最初はこの状態のままビルドしてみます。
$ sam build
以下のようなアクションが実行されます。
Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrcAndLockfile Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmInstall Running NodejsNpmBuilder:CleanUp Clean up action: .aws-sam/deps/ef743125-04fc-4805-a623-5281392d189f does not exist and will be skipped. Running NodejsNpmBuilder:CopyDependencies Running NodejsNpmBuilder:CleanUpNpmrc Running NodejsNpmBuilder:LockfileCleanUp Running NodejsNpmBuilder:LockfileCleanUp
ここで、.aws-sam
ディレクトリ内を確認してみます。
$ find .aws-sam | grep -v node_modules/ .aws-sam .aws-sam/build .aws-sam/build/template.yaml .aws-sam/build/HelloWorldFunction .aws-sam/build/HelloWorldFunction/app.js .aws-sam/build/HelloWorldFunction/package.json .aws-sam/build/HelloWorldFunction/node_modules .aws-sam/build.toml .aws-sam/deps .aws-sam/deps/ef743125-04fc-4805-a623-5281392d189f .aws-sam/deps/ef743125-04fc-4805-a623-5281392d189f/node_modules
AWS Lambda関数が配置されているディレクトリ内の一部がコピーされ、npm install
が行われたらしき状態であることが確認できます。
ここで、いったん.aws-sam
ディレクトリを削除します。
$ rm -rf .aws-sam
package-lock.jsonを作成してsam buildする
では、今度はpackage-lock.json
を作成してからsam build
してみます。
まずは、npm i
でモジュールをインストール。
$ cd hello-world $ npm i $ cd ..
このようになりました。
$ tree -L 2 . ├── README.md ├── events │ └── event.json ├── hello-world │ ├── app.js │ ├── node_modules │ ├── package-lock.json │ ├── package.json │ └── tests └── template.yaml 4 directories, 6 files
では、sam build
してみます。
$ sam build
実行されたアクション。
Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrcAndLockfile Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmInstall Running NodejsNpmBuilder:CleanUp Clean up action: .aws-sam/deps/8f327018-920e-4514-817b-d48da3872879 does not exist and will be skipped. Running NodejsNpmBuilder:CopyDependencies Running NodejsNpmBuilder:CleanUpNpmrc Running NodejsNpmBuilder:LockfileCleanUp Running NodejsNpmBuilder:LockfileCleanUp
今度は、package-lock.json
も.aws-sam
ディレクトリ内に現れます。
$ find .aws-sam | grep -v node_modules/ .aws-sam .aws-sam/build .aws-sam/build/template.yaml .aws-sam/build/HelloWorldFunction .aws-sam/build/HelloWorldFunction/app.js .aws-sam/build/HelloWorldFunction/package-lock.json .aws-sam/build/HelloWorldFunction/package.json .aws-sam/build/HelloWorldFunction/node_modules .aws-sam/build.toml .aws-sam/deps .aws-sam/deps/8f327018-920e-4514-817b-d48da3872879 .aws-sam/deps/8f327018-920e-4514-817b-d48da3872879/node_modules
最初にAWS SAMのソースコードを確認したように、CopyNpmrcAndLockfile
でコピーが行われているようです。
Running NodejsNpmBuilder:CopyNpmrcAndLockfile
ちなみに、参照しているソースコードが正しければnpm install
は以下のオプションで実行されているはずです。
self.subprocess_npm.run( ["install", "-q", "--no-audit", "--no-save", mode, "--unsafe-perm"], cwd=self.artifacts_dir )
1度.aws-sam
ディレクトリを削除して
$ rm -rf .aws-sam
確認してみましょう。
$ strace -f -e trace=execve sam build
strace
の出力内容を追っていくと、こんな感じになっていたので大丈夫でしょう。
[pid 29457] execve("$HOME/.nvm/versions/node/v16.16.0/bin/node", ["node", "$HOME/.nvm/versions/nod"..., "install", "-q", "--no-audit", "--no-save", "--production", "--unsafe-perm"], 0x7fff2c4a5c98 /* 73 vars */) = 0
npm ciを使う
最後は、こちらに習ってnpm ci
を使うようにしてみましょう。
AWS Serverless Application Model / アプリケーションの構築 / 例
template.yaml
のAWS Lambda関数のリソース定義部分はこちらですが、
Resources: HelloWorldFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: hello-world/ Handler: app.lambdaHandler Runtime: nodejs16.x Architectures: - x86_64 Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /hello Method: get
以下のようにMetadata
を追加してUseNpmCi
をTrue
にします。
Resources: HelloWorldFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: hello-world/ Handler: app.lambdaHandler Runtime: nodejs16.x Architectures: - x86_64 Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /hello Method: get Metadata: BuildProperties: UseNpmCi: True
この部分ですね。
Metadata: BuildProperties: UseNpmCi: True
1度、package-lock.json
を削除してみましょう。
$ rm hello-world/package-lock.json
sam build
します。
$ sam build
ふつうに動作してしまいました。package-lock.json
がない場合は、npm install
になるようです。
Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrcAndLockfile Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmInstall Running NodejsNpmBuilder:CleanUp Clean up action: .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991 does not exist and will be skipped. Running NodejsNpmBuilder:CopyDependencies Running NodejsNpmBuilder:CleanUpNpmrc Running NodejsNpmBuilder:LockfileCleanUp Running NodejsNpmBuilder:LockfileCleanUp
.aws-sam
ディレクトリの中身はこちら。
$ find .aws-sam | grep -v node_modules/ .aws-sam .aws-sam/build .aws-sam/build/template.yaml .aws-sam/build/HelloWorldFunction .aws-sam/build/HelloWorldFunction/app.js .aws-sam/build/HelloWorldFunction/package.json .aws-sam/build/HelloWorldFunction/node_modules .aws-sam/build.toml .aws-sam/deps .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991 .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991/node_modules
もう1度、package-lock.json
を作成します。
$ cd hello-world $ npm i $ cd ..
1度.aws-sam
ディレクトリを削除。
$ rm -rf .aws-sam
sam build
。
$ sam build
すると、今度はNpmCI
アクションが使われるようになりました。UseNpmCi
が有効であることの表示も出ています。
Building codeuri: /path/to/sam-nodejs-npm-ci/hello-world runtime: nodejs16.x metadata: {'BuildProperties': {'UseNpmCi': True}} architecture: x86_64 functions: HelloWorldFunction Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrcAndLockfile Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmCI Running NodejsNpmBuilder:CleanUp Clean up action: folder .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991 will be cleaned Running NodejsNpmBuilder:CopyDependencies Running NodejsNpmBuilder:CleanUpNpmrc Running NodejsNpmBuilder:LockfileCleanUp Running NodejsNpmBuilder:LockfileCleanUp
ポイントは、こちらのようですね。
npm ci を使用するには、アプリケーションは Lambda 関数の CodeUri に package-lock.json または npm-shrinkwrap.json ファイルが必要です。
AWS Serverless Application Model / アプリケーションの構築 / 例
CodeUri
は今回はこうなっています。
Properties: CodeUri: hello-world/
.aws-sam
ディレクトリの内容。
$ find .aws-sam | grep -v node_modules/ .aws-sam .aws-sam/build .aws-sam/build/template.yaml .aws-sam/build/HelloWorldFunction .aws-sam/build/HelloWorldFunction/app.js .aws-sam/build/HelloWorldFunction/package-lock.json .aws-sam/build/HelloWorldFunction/package.json .aws-sam/build/HelloWorldFunction/node_modules .aws-sam/build.toml .aws-sam/deps .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991 .aws-sam/deps/4819e4df-04ba-4cd1-8921-702ea890f991/node_modules
npm ci
が動作していることも確認してみましょう。
.aws-sam
ディレクトリを削除。
$ rm -rf .aws-sam
strace
を仕込んで確認。
$ strace -f -e trace=execve sam build
OKですね。
[pid 31187] execve("$HOME/.nvm/versions/node/v16.16.0/bin/node", ["node", "$HOME/.nvm/versions/nod"..., "ci"], 0x7ffcadd9b850 /* 73 vars */) = 0
まとめ
AWS SAMでNode.jsを使ったAWS Lambda関数をパッケージングする際に、以前はpackage-lock.json
が利用されなかったのですがこの状況が
変わっていること、そして設定次第ではnpm ci
を利用できることを確認できました。
package-lock.json
の扱いについては気になっていたことのひとつだったので、解消されていたことに気づけて良かったかなと思います。