前置き
仕事でAWS LambdaとTypeScriptで開発をすることになった
ただAWSの知識はそんなに持ってないし開発環境どうすればいいんだ…
と悩んでいたところに以下の記事を発見。
更に公式でDockerイメージが提供されており使用方法の説明もあった。
じゃあ両方うまいこと合わせたらいい感じの環境を作れるか?
環境構築
最終的にプロジェクトの構成は以下のようにした。
/src
配下のts
をビルドして/dist
配下に生成し
/dist
をdocker上にマウントして動作させるという構成。
/project_dir
┣ /src
┃ ┗ index.ts
┣ /dist
┃ ┣ index.js
┃ ┗ index.js.map
┣ /node_module
┣ Dockerfile
┣ docker-compose.yaml
┣ package.json
┣ package-lock.json
┣ lambda-exec.sh
┗ docker-command.sh
nodeインストール
作業環境は WSL 上の ubuntu を利用することとして
デフォルトだと node のバージョンが低いので nvm を導入
nvm 入れたらとりあえず node が最新版になったので
AWS lambda 公式マニュアルに従い node プロジェクトを作成する。
$ mkdir ~/project_dir
$ cd ~/project_dir
$ npm init
$ npm install -D @types/aws-lambda esbuild
packege.json
にビルド用コマンドを追記
マニュアルから少し変更し index.ts
を src/index.ts
と指定
"scripts": {
"build": "esbuild src/index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outfile=dist/index.js"
}
TypeScriptコード記述
/src
ディレクトリを作成し、その下にコードを記述。
このコードはマニュアルをそのまま利用。
$ mkdir src
$ touch src/index.ts
import { Context, APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda';
export const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => {
console.log(`Event: ${JSON.stringify(event, null, 2)}`);
console.log(`Context: ${JSON.stringify(context, null, 2)}`);
return {
statusCode: 200,
body: JSON.stringify({
message: 'hello world',
}),
};
};
index.ts
が作成出来たらとりあえずビルド。
ビルドすると /dist
配下に index.js
が作成される。
$ pwd
/project_dir
$ npm run build
Docker準備
Dockerfile
と docker-compose.yaml
を作る。
冒頭の記事を参考にしてなんかいい感じに調整。
もっといい書き方とか不要な記述はあるかもしれない
$ pwd
/project_dir
$ touch Dockerfile
$ touch docker-compose.yaml
FROM public.ecr.aws/lambda/nodejs:22 as builder
WORKDIR ${LAMBDA_TASK_ROOT}
COPY package.json ./
RUN npm install
services:
lambda-local:
build:
context: .
dockerfile: Dockerfile
environment:
LAMBDA_TASK_ROOT: /var/task
command: [ "index.handler" ]
restart: always
volumes:
- ./dist:/var/task/
ports:
- "9000:8080"
logging:
options:
max-size: "5m"
max-file: "10"
ファイルを作ったらとりあえずビルドして起動。
$ docker compose build --no-cache
$ docker compose up -d
動作確認
Dockerをビルドして問題なく起動できたら
マニュアルの curl コマンドを使って動くか試す
$ curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
以下のようなレスポンスが返ってくれば成功
{"statusCode":200,"body":"{\"message\":\"hello world\"}"}
変更を反映する
これで環境ができたのでコードの編集を行っていく。
変更した場合はまた index.ts
をビルドして dockerを再起動する必要がある。
$ pwd
/project_dir
$ npm run build
$ docker compose restart lambda-local
nodeなら自動ビルドもあるけど一旦それは置いといて・・・
シェルスクリプトを作っておく
書いた関数の動作確認に長い curl コマンド覚えられないし
変更を反映させるために毎回コマンドを実行するのはちょっとめんどくさいので
簡単なシェルスクリプトを作っておいてちょっと楽をしたい。
#!/bin/bash
curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
#!/bin/bash
cd ~/project_dir
param=$1
if [ -z "$param" ]; then
echo option is "up" "restart" "down" "rebuild"
elif [ $param = "up" ]; then
npm run build
docker compose up -d
elif [ $param = "restart" ]; then
npm run build
docker compose restart lambda-local
elif [ $param = "down" ]; then
docker compose down
elif [ $param = "rebuild" ]; then
docker compose build --no-cache
fi
これでお試しできる環境が一通り揃ったと思う。
番外編:POSTでパラメータを渡す
ちょっと詰まったので備忘録として
POSTでパラメータを渡したとき、lambda関数上でどう受け取るか
マニュアルの curl コマンドだと上手く表示できなかったので色々試した
まずはindex.ts
を編集してパラメータを表示するように修正
import { Context, APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda';
export const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => {
const requestBody = event.body ? JSON.parse(event.body) : null;
console.log('Request Body:', requestBody);
const response: APIGatewayProxyResult = {
statusCode: 200,
body: JSON.stringify({
message: 'Received the request body',
data: requestBody,
}),
};
return response;
};
変更後、動作を確認するために以下のcurlコマンドでうまく行った。
APIGatewayEvent
という型を使った場合、大元をbody
とする必要があった。
// 実行
$ curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"body": "{\"payload\":\"hello world!\"}"}'
// レスポンス
{"statusCode":200,"body":"{\"message\":\"Received the request body\",\"data\":{\"payload\":\"hello world! APItester\"}}"}
上手くできたらlambda-exec.sh
を更新しておく。