1️⃣

lambda memo

2025/02/20に公開1

lambda memo

src.lambda.ts

  • npm install @vendia/serverless-express aws-lambda
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import serverlessExpress from '@vendia/serverless-express';
import { Context, APIGatewayEvent } from 'aws-lambda';
import { INestApplication } from '@nestjs/common';

let cachedServer; // コールドスタート対策のキャッシュ

async function bootstrap(): Promise<INestApplication> {
  const app = await NestFactory.create(AppModule);
  // SwaggerセットアップやCORS設定などを行う
  // 例: const config = new DocumentBuilder().setTitle('Swagger').build();
  // 例: const document = SwaggerModule.createDocument(app, config);
  // 例: SwaggerModule.setup('api-docs', app, document);
  //
  // listenはしない。Lambdaではinit()のみ。
  await app.init();
  return app;
}

export const handler = async (event: APIGatewayEvent, context: Context) => {
  if (!cachedServer) {
    const app = await bootstrap();
    const expressApp = app.getHttpAdapter().getInstance();
    cachedServer = serverlessExpress({ app: expressApp });
  }
  return cachedServer(event, context);
};

Dockerfile

FROM public.ecr.aws/lambda/nodejs:22

WORKDIR /var/task

COPY package*.json ./
RUN npm ci

COPY . .

Nestのビルド
RUN npm run build

CMD ["dist/lambda.handler"]

SAMテンプレート (template.yaml)

このテンプレートでは「Api イベント」を設定しているので、API Gatewayが自動的に作成され、ANY /{proxy+} で全パスを受け取りLambdaへ転送

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Deploy Nest.js on AWS Lambda (Container Image) with SAM

Globals:
  Function:
    Timeout: 15  # Lambdaのタイムアウト秒数を指定

Resources:
  NestLambdaFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: docker
      DockerContext: .
      Dockerfile: Dockerfile
    Properties:
      PackageType: Image
      ImageConfig:
        Command:
          - dist/lambda.handler
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

ビルド、ECRへのプッシュ

  • sam build --use-container
    • Build Failed
      • Error: Building image for NestLambdaFunction requires Docker. is Docker running?
    • WSL2 上に Docker Engine を直接インストールしている場合
      • sudo service docker start や sudo systemctl start docker などで、Docker デーモンを起動する。
      • docker ps などで正常に動いていることを確認
  • sam deploy --guided
    • AWS CLI の設定ファイル: ~/.aws/credentials~/.aws/config に複数のプロファイルを定義している場合
      • sam deploy --guided --profile <profileName>
        初回はパラメータ(スタック名、リージョン、ECRリポジトリの自動作成など)を聞かれます。
        デプロイ時に自動で「ビルドしたイメージをECRにプッシュ」→「CloudFormationスタックを作成」→「Lambda + API Gateway」がデプロイ、という流れになります。
  • アカウント確認
    • aws sts get-caller-identity
    • aws sts get-caller-identity --profile stg

(オプション) --use-container を使わずにビルドしたい場合

もしどうしても Docker を使わずにビルドしたい場合は、SAM によるコンテナビルドを使わずに ローカルビルド した成果物をそのまま Lambda にデプロイする方法もあります。

  • sam build で --use-container を外す (ただし、Node.js のバージョンなどがローカル環境と Lambda ランタイムで整合性が取れていないとビルド結果が動かない可能性がある)
  • 自分でビルドしたディレクトリを sam deploy --template template.yaml --guided などでデプロイ

しかし、コンテナイメージ形式でデプロイするなら sam build --use-container が望ましく、やはり Docker が必要になります。

Swagger UI 内から API を叩く設定(CORS など)

CORS 設定

  • Lambda (Nest.js) 側で app.enableCors() や適切な CORS 設定を行っているか確認してください。
  • API Gateway 側でも、必要に応じて CORS を許可する設定が求められる場合がありますが、ANY /{proxy+} + Nest であれば、基本的には Nest 側の CORS 設定を有効にしておけば十分です。

Swagger の servers セクション

  • Swagger UI から「Try it out」を押した際に、どのベースURLにリクエストを送るかが決まります。
  • デフォルトでは相対パス or localhost:3000 などになっていると、デプロイ先の API を正しく叩けないかもしれません。
  • その場合は、DocumentBuilder() の addServer() などで実際のデプロイ URL を指定するか、あるいは相対パスに調整してください。
  • 例:
const config = new DocumentBuilder()
  .setTitle('My API')
  .addServer('/') // 相対パスで同じドメインにリクエスト
  // or
  // .addServer('https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com')
  .build();
  • 多くの場合は 相対パス指定 にしておくと、Swagger UI を開いている URL と同じドメイン配下にリクエストを送るため、追加の設定なく動きやすいです。

Discussion

yuyamayuyama

AWS Lambdaに紐づけるロールを事前に管理者などに作成してもらった場合、SAMのデプロイ時に「既存ロールを使う」設定を行うことで、IAMロールの自動作成をスキップできます。以下のステップで進めるのが一般的です。

  1. 事前にロールを作成し、ARNを把握する

管理者権限がある人(あるいはIAMの操作が可能な担当者)が、Lambda用のロールを作成します。
• 作成するロールには、以下のようなポリシーが必要です(例):
• Lambdaの実行に必要なポリシー(AWSLambdaBasicExecutionRole など)
• 必要に応じて、追加のサービス(DynamoDBやS3など)へアクセスするためのポリシー

作成されたロールのARN(例: arn:aws:iam::123456789012:role/MyPreCreatedLambdaRole)を控えます。

注意: そのロールをLambdaに渡すには、「iam:PassRole」権限が必要です。あなたのユーザー/ロールに iam:PassRole が付与されていない場合、デプロイ時にエラーとなります。管理者に「PassRoleも許可してほしい」と依頼してください。
  1. SAM テンプレートに「既存ロール」を指定する

template.yaml の Lambda関数定義部分にある Properties に、Role というプロパティを追加し、既存ロールのARNを設定します。

例: template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My Serverless App

Resources:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
ImageUri: <YOUR_ECR_IMAGE_URI or SAM build method>
Role: arn:aws:iam::123456789012:role/MyPreCreatedLambdaRole # ここで既存ロールを指定
Events:
ApiEvent:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY

•	ここで Role: arn:aws:iam::... とすることで、SAM/CloudFormationが新たなロールを作成しようとしなくなります。
•	既存ロールを使うため自動作成の手順がスキップされ、あなたが持っている iam:PassRole 権限の範囲でLambdaにそのロールが割り当てられます。
  1. sam deploy --guided でデプロイする

テンプレートを修正したら、改めて sam build → sam deploy --guided を実行します。
• sam build (イメージビルドやパッケージング)

sam build --use-container

•	sam deploy --guided  (CloudFormationスタックの作成/更新)

sam deploy --guided

•	初回実行時には「スタック名」や「リージョン」などを聞かれますが、「IAMロールの自動作成」は聞かれなくなり、CloudFormationは template.yaml に指定された Role ARN を使います。
•	もし「ロールを新規作成するか?」と聞かれたら「いいえ(No)」を選び、既存ロールを使う設定を進めてください(SAM CLIのバージョンによって画面が若干異なります)。

権限エラーが出た場合
	•	デプロイ中に「You are not authorized to perform: iam:PassRole on resource…」のようなエラーが出たら、iam:PassRole 権限が不足しています。管理者にPassRoleを許可するポリシーを付けてもらう必要があります。
  1. デプロイ完了後にAPI Gatewayのエンドポイントを確認

デプロイが成功すると、CloudFormationスタックが作成/更新され、LambdaとAPI Gatewayが紐づきます。
sam deploy の出力か、CloudFormationコンソールの「Outputs」で、API GatewayのURLを確認できます。

まとめ
1. 管理者に事前にロールを作成してもらう
• 必要ポリシー(Lambda実行権限等)を付与したロール。ARNをメモする。
2. template.yaml に Role: <ARN> を設定
• こうすることで、SAMが自動でIAMを作成しなくなる。
3. 自分(デプロイ者)には iam:PassRole 権限が必要
• これがないと、CloudFormationが「このロールをLambdaに紐づける」操作でエラーになる。
4. sam build && sam deploy --guided
• 正常に完了すれば、既存ロールを使ったLambdaデプロイが成功する。

これでPowerUserAccessのみを持っていて「IAMロールの作成」ができない状況でも、既存ロールを指定してデプロイが可能になります。