Agent Skills by ALSEL
Anthropic Claudeソフトウェア開発⭐ リポ 0品質スコア 50/100

aws-serverless

AWS上でプロダクション品質のサーバーレスアプリケーションを構築するための専門スキル。Lambda関数、API Gateway、DynamoDB、SQS/SNSを用いたイベント駆動パターン、SAM/CDKによるデプロイ、コールドスタートの最適化まで幅広くカバーします。

description の原文を見る

Specialized skill for building production-ready serverless applications on AWS. Covers Lambda functions, API Gateway, DynamoDB, SQS/SNS event-driven patterns, SAM/CDK deployment, and cold start optimization.

SKILL.md 本文

AWS Serverless

AWS上でプロダクション対応のサーバーレスアプリケーションを構築するための専門スキルです。Lambda 関数、API Gateway、DynamoDB、SQS/SNS イベント駆動パターン、SAM/CDK デプロイメント、およびコールドスタート最適化をカバーしています。

原則

  • メモリとタイムアウトを適切に設定する(最適化する前に測定する)
  • レイテンシに敏感なワークロードのコールドスタートを最小化する
  • Java/.NET 関数には SnapStart を使用する
  • シンプルなユースケースでは REST API よりも HTTP API を優先する
  • DLQ と再試行で失敗に備えた設計をする
  • デプロイメントパッケージを小さく保つ
  • 設定に環境変数を使用する
  • 関連 ID を使用した構造化ログを実装する

パターン

Lambda ハンドラーパターン

エラーハンドリングを備えた適切な Lambda 関数の構造

使用時期: Lambda 関数の実装、API ハンドラー、イベントプロセッサー、スケジュール実行タスク

// Node.js Lambda Handler
// handler.js

// ハンドラー外で初期化(ウォームな呼び出し全体で再利用)
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

// ハンドラー関数
exports.handler = async (event, context) => {
  // オプション:イベントループをクリアするのを待たない(Node.js)
  context.callbackWaitsForEmptyEventLoop = false;

  try {
    // イベントソースに基づいて入力を解析
    const body = typeof event.body === 'string'
      ? JSON.parse(event.body)
      : event.body;

    // ビジネスロジック
    const result = await processRequest(body);

    // API Gateway 互換の応答を返す
    return {
      statusCode: 200,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
      },
      body: JSON.stringify(result)
    };
  } catch (error) {
    console.error('Error:', JSON.stringify({
      error: error.message,
      stack: error.stack,
      requestId: context.awsRequestId
    }));

    return {
      statusCode: error.statusCode || 500,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        error: error.message || 'Internal server error'
      })
    };
  }
};

async function processRequest(data) {
  // ビジネスロジックをここに記述
  const result = await docClient.send(new GetCommand({
    TableName: process.env.TABLE_NAME,
    Key: { id: data.id }
  }));
  return result.Item;
}
# Python Lambda Handler
# handler.py

import json
import os
import logging
import boto3
from botocore.exceptions import ClientError

# ハンドラー外で初期化(ウォームな呼び出し全体で再利用)
logger = logging.getLogger()
logger.setLevel(logging.INFO)

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TABLE_NAME'])

def handler(event, context):
    try:
        # 入力を解析
        body = json.loads(event.get('body', '{}')) if isinstance(event.get('body'), str) else event.get('body', {})

        # ビジネスロジック
        result = process_request(body)

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps(result)
        }

    except ClientError as e:
        logger.error(f"DynamoDB error: {e.response['Error']['Message']}")
        return error_response(500, 'Database error')

    except json.JSONDecodeError:
        return error_response(400, 'Invalid JSON')

    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}", exc_info=True)
        return error_response(500, 'Internal server error')

def process_request(data):
    response = table.get_item(Key={'id': data['id']})
    return response.get('Item')

def error_response(status_code, message):
    return {
        'statusCode': status_code,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps({'error': message})
    }

ベストプラクティス

  • ハンドラー外でクライアントを初期化する(ウォームな呼び出し全体で再利用)
  • 常に適切な API Gateway 応答形式を返す
  • CloudWatch Insights 用に構造化 JSON でログを記録する
  • トレース用にエラーログにリクエスト ID を含める

API Gateway 統合パターン

REST API および HTTP API と Lambda の統合

使用時期: Lambda でバックアップされた REST API を構築する、関数の HTTP エンドポイントが必要

# template.yaml (SAM)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 30
    MemorySize: 256
    Environment:
      Variables:
        TABLE_NAME: !Ref ItemsTable

Resources:
  # HTTP API(シンプルなユースケースに推奨)
  HttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: prod
      CorsConfiguration:
        AllowOrigins:
          - "*"
        AllowMethods:
          - GET
          - POST
          - DELETE
        AllowHeaders:
          - "*"

  # Lambda 関数
  GetItemFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/get.handler
      Events:
        GetItem:
          Type: HttpApi
          Properties:
            ApiId: !Ref HttpApi
            Path: /items/{id}
            Method: GET
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref ItemsTable

  CreateItemFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/create.handler
      Events:
        CreateItem:
          Type: HttpApi
          Properties:
            ApiId: !Ref HttpApi
            Path: /items
            Method: POST
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ItemsTable

  # DynamoDB テーブル
  ItemsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST

Outputs:
  ApiUrl:
    Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com/prod"
// src/handlers/get.js
const { getItem } = require('../lib/dynamodb');

exports.handler = async (event) => {
  const id = event.pathParameters?.id;

  if (!id) {
    return {
      statusCode: 400,
      body: JSON.stringify({ error: 'Missing id parameter' })
    };
  }

  const item = await getItem(id);

  if (!item) {
    return {
      statusCode: 404,
      body: JSON.stringify({ error: 'Item not found' })
    };
  }

  return {
    statusCode: 200,
    body: JSON.stringify(item)
  };
};

構造

project/ ├── template.yaml # SAM テンプレート ├── src/ │ ├── handlers/ │ │ ├── get.js │ │ ├── create.js │ │ └── delete.js │ └── lib/ │ └── dynamodb.js └── events/ └── event.json # テストイベント

API 比較

  • Http_api:
    • 低レイテンシ(約 10ms)
    • 低コスト(50~70% 安い)
    • よりシンプル、機能が少ない
    • 最適な用途: ほとんどの REST API
  • Rest_api:
    • より多くの機能(キャッシング、リクエスト検証、WAF)
    • 使用量プランと API キー
    • リクエスト/レスポンス変換
    • 最適な用途: 複雑な API、エンタープライズ機能

イベント駆動 SQS パターン

Lambda が SQS によってトリガーされ、信頼性の高い非同期処理を実現

使用時期: 分離された非同期処理、再試行ロジックと DLQ が必要、メッセージをバッチで処理

# template.yaml
Resources:
  ProcessorFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/processor.handler
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue: !GetAtt ProcessingQueue.Arn
            BatchSize: 10
            FunctionResponseTypes:
              - ReportBatchItemFailures  # 部分的なバッチ失敗処理

  ProcessingQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 180  # Lambda タイムアウトの 6 倍
      RedrivePolicy:
        deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
        maxReceiveCount: 3

  DeadLetterQueue:
    Type: AWS::SQS::Queue
    Properties:
      MessageRetentionPeriod: 1209600  # 14 日間
// src/handlers/processor.js
exports.handler = async (event) => {
  const batchItemFailures = [];

  for (const record of event.Records) {
    try {
      const body = JSON.parse(record.body);
      await processMessage(body);
    } catch (error) {
      console.error(`Failed to process message ${record.messageId}:`, error);
      // このアイテムを失敗として報告(再試行される)
      batchItemFailures.push({
        itemIdentifier: record.messageId
      });
    }
  }

  // 失敗したアイテムを返す
  return { batchItemFailures };
};

async function processMessage(message) {
  // 処理ロジックをここに記述
  console.log('Processing:', message);

  // 作業をシミュレート
  await saveToDatabase(message);
}
# Python バージョン
import json
import logging

logger = logging.getLogger()

def handler(event, context):
    batch_item_failures = []

    for record in event['Records']:
        try:
            body = json.loads(record['body'])
            process_message(body)
        except Exception as e:
            logger.error(f"Failed to process {record['messageId']}: {e}")
            batch_item_failures.append({
                'itemIdentifier': record['messageId']
            })

    return {'batchItemFailures': batch_item_failures}

ベストプラクティス

  • VisibilityTimeout を Lambda タイムアウトの 6 倍に設定する
  • ReportBatchItemFailures を部分的なバッチ失敗に使用する
  • 常に不正なメッセージ用に DLQ を設定する
  • メッセージをべき等に処理する

DynamoDB ストリームパターン

DynamoDB テーブルの変更に対して Lambda で対応

使用時期: データ変更への実時間反応、クロスリージョンレプリケーション、監査ログ、通知

# template.yaml
Resources:
  ItemsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: items
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES

  StreamProcessorFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/stream.handler
      Events:
        Stream:
          Type: DynamoDB
          Properties:
            Stream: !GetAtt ItemsTable.StreamArn
            StartingPosition: TRIM_HORIZON
            BatchSize: 100
            MaximumRetryAttempts: 3
            DestinationConfig:
              OnFailure:
                Destination: !GetAtt StreamDLQ.Arn

  StreamDLQ:
    Type: AWS::SQS::Queue
// src/handlers/stream.js
exports.handler = async (event) => {
  for (const record of event.Records) {
    const eventName = record.eventName;  // INSERT, MODIFY, REMOVE

    // DynamoDB 形式を平易な JS オブジェクトにアンマーシャル
    const newImage = record.dynamodb.NewImage
      ? unmarshall(record.dynamodb.NewImage)
      : null;
    const oldImage = record.dynamodb.OldImage
      ? unmarshall(record.dynamodb.OldImage)
      : null;

    console.log(`${eventName}: `, { newImage, oldImage });

    switch (eventName) {
      case 'INSERT':
        await handleInsert(newImage);
        break;
      case 'MODIFY':
        await handleModify(oldImage, newImage);
        break;
      case 'REMOVE':
        await handleRemove(oldImage);
        break;
    }
  }
};

// AWS SDK v3 のアンマーシャルを使用
const { unmarshall } = require('@aws-sdk/util-dynamodb');

ストリームビュータイプ

  • KEYS_ONLY: キー属性のみ
  • NEW_IMAGE: 変更後
  • OLD_IMAGE: 変更前
  • NEW_AND_OLD_IMAGES: 変更前後の両方

コールドスタート最適化パターン

Lambda コールドスタートレイテンシを最小化

使用時期: レイテンシに敏感なアプリケーション、ユーザー向けの API、トラフィックの多い関数

1. パッケージサイズを最適化

// モジュール形式の AWS SDK v3 インポートを使用
// 良い例 - 必要なものだけをインポート
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');

// 悪い例 - SDK 全体をインポート
const AWS = require('aws-sdk');  // これはしないこと!

2. SnapStart を使用(Java/.NET)

# template.yaml
Resources:
  JavaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.example.Handler::handleRequest
      Runtime: java21
      SnapStart:
        ApplyOn: PublishedVersions  # SnapStart を有効化
      AutoPublishAlias: live

3. メモリを適切に設定

# より多くのメモリ = より多くの CPU = より速い初期化
Resources:
  FastFunction:
    Type: AWS::Serverless::Function
    Properties:
      MemorySize: 1024  # 1GB は全 vCPU を取得
      Timeout: 30

4. プロビジョニングされた同時実行数(必要に応じて)

Resources:
  CriticalFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/critical.handler
      AutoPublishAlias: live

  ProvisionedConcurrency:
    Type: AWS::Lambda::ProvisionedConcurrencyConfig
    Properties:
      FunctionName: !Ref CriticalFunction
      Qualifier: live
      ProvisionedConcurrentExecutions: 5

5. 初期化を軽くする

# 良い例 - 遅延初期化
_table = None

def get_table():
    global _table
    if _table is None:
        dynamodb = boto3.resource('dynamodb')
        _table = dynamodb.Table(os.environ['TABLE_NAME'])
    return _table

def handler(event, context):
    table = get_table()  # 最初の使用時のみ初期化
    # ...

最適化の優先順位

  • 1: パッケージサイズを削減(最大の影響)
  • 2: Java/.NET に SnapStart を使用
  • 3: 初期化を速くするためにメモリを増やす
  • 4: 重いインポートを遅延させる
  • 5: プロビジョニングされた同時実行数(最後の手段)

SAM ローカル開発パターン

SAM CLI を使用したローカルテストとデバッグ

使用時期: ローカル開発とテスト、Lambda 関数のデバッグ、API Gateway をローカルでテスト

# SAM CLI をインストール
pip install aws-sam-cli

# 新しいプロジェクトを初期化
sam init --runtime nodejs20.x --name my-api

# プロジェクトをビルド
sam build

# ローカルで実行
sam local start-api

# 単一の関数を呼び出す
sam local invoke GetItemFunction --event events/get.json

# ローカルデバッグ(VS Code を使用した Node.js)
sam local invoke --debug-port 5858 GetItemFunction

# デプロイ
sam deploy --guided
// events/get.json(テストイベント)
{
  "pathParameters": {
    "id": "123"
  },
  "httpMethod": "GET",
  "path": "/items/123"
}
// .vscode/launch.json(デバッグ用)
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to SAM CLI",
      "type": "node",
      "request": "attach",
      "address": "localhost",
      "port": 5858,
      "localRoot": "${workspaceRoot}/src",
      "remoteRoot": "/var/task/src",
      "protocol": "inspector"
    }
  ]
}

コマンド

  • Sam_build: Lambda デプロイメントパッケージをビルド
  • Sam_local_start_api: ローカル API Gateway を開始
  • Sam_local_invoke: 単一の関数を呼び出す
  • Sam_deploy: AWS にデプロイ
  • Sam_logs: CloudWatch ログをテール

CDK サーバーレスパターン

AWS CDK を使用したインフラストラクチャコード

使用時期: Lambda 以上の複雑なインフラストラクチャ、YAML よりプログラミング言語を優先、再利用可能なコンストラクトが必要

// lib/api-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

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

    // DynamoDB テーブル
    const table = new dynamodb.Table(this, 'ItemsTable', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 開発環境のみ
    });

    // Lambda 関数
    const getItemFn = new lambda.Function(this, 'GetItemFunction', {
      runtime: lambda.Runtime.NODEJS_20_X,
      handler: 'get.handler',
      code: lambda.Code.fromAsset('src/handlers'),
      environment: {
        TABLE_NAME: table.tableName,
      },
      memorySize: 256,
      timeout: cdk.Duration.seconds(30),
    });

    // パーミッションを付与
    table.grantReadData(getItemFn);

    // API Gateway
    const api = new apigateway.RestApi(this, 'ItemsApi', {
      restApiName: 'Items Service',
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS,
      },
    });

    const items = api.root.addResource('items');
    const item = items.addResource('{id}');

    item.addMethod('GET', new apigateway.LambdaIntegration(getItemFn));

    // API URL を出力
    new cdk.CfnOutput(this, 'ApiUrl', {
      value: api.url,
    });
  }
}
# CDK コマンド
npm install -g aws-cdk
cdk init app --language typescript
cdk synth    # CloudFormation を生成
cdk diff     # 変更を表示
cdk deploy   # AWS にデプロイ

危険な落とし穴

コールドスタート INIT フェーズが課金対象に(2025 年 8 月)

重大度: 高

状況: プロダクション環境で Lambda 関数を実行

症状: Lambda コストが予想外に増加(10~50% 高い)。 関数初期化の課金が請求に含まれている。 重いスタートアップロジックを持つ関数が予想より高いコストになっている。

なぜこれが問題なのか: 2025 年 8 月 1 日より、AWS は INIT フェーズを呼び出し期間と同じ方法で課金します。 以前は、コールドスタート初期化は完全期間の間は課金されていませんでした。

これは以下の関数に影響します:

  • 重い依存関係の読み込み(大きなパッケージ)
  • 遅い初期化コード
  • 頻繁なコールドスタート(トラフィック少ない、同時実行性の低い)

コールドスタートは直接請求に影響し、レイテンシだけではなくなりました。

推奨される修正:

INIT フェーズを測定

# CloudWatch ログで INIT_REPORT を確認
# Init Duration をミリ秒単位で探す

# ログの例:
# INIT_REPORT Init Duration: 423.45 ms

INIT 期間を削減

// 1. パッケージサイズを最小化
// ツリーシェイキング、開発依存関係を除外
// npm prune --production

// 2. 重い依存関係を遅延ロード
let heavyLib = null;
function getHeavyLib() {
  if (!heavyLib) {
    heavyLib = require('heavy-library');
  }
  return heavyLib;
}

// 3. AWS SDK v3 のモジュール形式インポートを使用
const { S3Client } = require('@aws-sdk/client-s3');
// NOT: const AWS = require('aws-sdk');

Java/.NET に SnapStart を使用

Resources:
  JavaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: java21
      SnapStart:
        ApplyOn: PublishedVersions

コールドスタート頻度を監視

// カスタムメトリックでコールドスタートを追跡
let isColdStart = true;

exports.handler = async (event) => {
  if (isColdStart) {
    console.log('COLD_START');
    // ここで CloudWatch カスタムメトリック
    isColdStart = false;
  }
  // ...
};

Lambda タイムアウト設定エラー

重大度: 高

状況: Lambda 関数を実行、特に外部呼び出しあり

症状: 関数が予期せずタイムアウト。 ログに「Task timed out after X seconds」。 部分的な処理で応答なし。 エラーが捕捉されない静かな失敗。

なぜこれが問題なのか: Lambda のデフォルトタイムアウトはわずか 3 秒。最大は 15 分。

一般的なタイムアウトの原因:

  • デフォルトタイムアウトがワークロードに対して短すぎる
  • ダウンストリームサービスが予想より遅い
  • VPC のネットワーク問題
  • 無限ループまたはブロッキング操作
  • S3 ダウンロードが予想より大きい

Lambda はタイムアウト時にグレースフルシャットダウンなしで終了します。

推奨される修正:

適切なタイムアウトを設定

# template.yaml
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Timeout: 30  # 秒(最大 900)
      # 予想期間 + バッファに設定

タイムアウト認識を実装

exports.handler = async (event, context) => {
  // 残り時間を取得
  const remainingTime = context.getRemainingTimeInMillis();

  // 時間が少なくなったら、グレースフルに失敗
  if (remainingTime < 5000) {
    console.warn('Running low on time, aborting');
    throw new Error('Insufficient time remaining');
  }

  // 長い操作では、定期的にチェック
  for (const item of items) {
    if (context.getRemainingTimeInMillis() < 10000) {
      // 進捗を保存して終了
      await saveProgress(processedItems);
      throw new Error('Timeout approaching, saved progress');
    }
    await processItem(item);
  }
};

ダウンストリームタイムアウトを設定

const axios = require('axios');

// HTTP 呼び出しに常にタイムアウトを設定
const response = await axios.get('https://api.example.com/data', {
  timeout: 5000  // 5 秒
});

メモリ不足(OOM)クラッシュ

重大度: 高

状況: データを処理する Lambda 関数

症状: 関数が突然停止し、エラーなし。 CloudWatch ログが切り詰められて見える。 「Max Memory Used」が設定された制限に達している。 負荷下での不安定な動作。

なぜこれが問題なのか: Lambda がメモリ割り当てを超えると、AWS はランタイムを強制終了します。 これはキャッチ可能な例外を発生させずに起こります。

一般的な原因:

  • メモリ内で大きなファイルを処理
  • 呼び出し全体でのメモリリーク
  • レスポンス本体全体をバッファリング
  • メモリを消費する重いライブラリ

推奨される修正:

メモリ割り当てを増やす

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      MemorySize: 1024  # MB(128~10240)
      # より多くのメモリ = より多くの CPU も

大規模データをストリーミング

// 悪い例 - ファイル全体をメモリにロード
const data = await s3.getObject(params).promise();
const content = data.Body.toString();

// 良い例 - ストリーム処理
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({});

const response = await s3.send(new GetObjectCommand(params));
const stream = response.Body;

// チャンク単位でストリームを処理
for await (const chunk of stream) {
  await processChunk(chunk);
}

メモリ使用量を監視

exports.handler = async (event, context) => {
  const used = process.memoryUsage();
  console.log('Memory:', {
    heapUsed: Math.round(used.heapUsed / 1024 / 1024) + 'MB',
    heapTotal: Math.round(used.heapTotal / 1024 / 1024) + 'MB'
  });
  // ...
};

Lambda Power Tuning を使用

# 最適なメモリ設定を見つける
# https://github.com/alexcasalboni/aws-lambda-power-tuning

VPC に接続された Lambda コールドスタート遅延

重大度: 中

状況: VPC 内のプライベートリソースにアクセスする Lambda 関数

症状: 極端に遅いコールドスタート(10 秒以上、現在は約 100ms)。 アイドル期間後の最初の呼び出しでタイムアウト。 VPC 内の関数は動作するが、非 VPC と比べて遅い。

なぜこれが問題なのか: VPC の Lambda 関数にはElastic Network Interfaces(ENI)が必要です。 AWS は Hyperplane ENI でこれを大幅に改善しましたが:

  • VPC の最初のコールドスタートはオーバーヘッドがある
  • NAT ゲートウェイの問題がタイムアウトを引き起こせる
  • セキュリティグループの設定ミスがトラフィックをブロック
  • DNS 解決が遅い可能性

推奨される修正:

VPC 設定を確認

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSecurityGroup
        SubnetIds:
          - !Ref PrivateSubnet1
          - !Ref PrivateSubnet2  # 複数の AZ

  LambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Lambda SG
      VpcId: !Ref VPC
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0  # HTTPS アウトバウンドを許可

AWS サービス用に VPC エンドポイントを使用

# NAT ゲートウェイを避ける AWS サービス呼び出し
DynamoDBEndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    ServiceName: !Sub com.amazonaws.${AWS::Region}.dynamodb
    VpcId: !Ref VPC
    RouteTableIds:
      - !Ref PrivateRouteTable
    VpcEndpointType: Gateway

S3Endpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
    VpcId: !Ref VPC
    VpcEndpointType: Gateway

VPC が必要な場合のみ使用

VPC に Lambda を接続する必要は以下の場合のみ:

  • RDS/ElastiCache を VPC でアクセス
  • VPC 内のプライベート EC2 インスタンスにアクセス
  • コンプライアンス要件

ほとんどの AWS サービスは VPC なしでアクセス可能。

Node.js イベントループがクリアされない

重大度: 中

状況: コールバックまたはタイマーを持つ Node.js Lambda 関数

症状: 関数は予想より完全なタイムアウト期間を取る。 「Task timed out」エラー、ロジックは完了。 アイドル時間について余分な請求。

なぜこれが問題なのか: デフォルトでは、Lambda は Node.js イベントループがクリア(空)になるまで待機します。 以下がある場合:

  • 未解決の setTimeout/setInterval
  • ハングしているデータベース接続
  • 保留中のコールバック

Lambda はタイムアウトまで待機し、応答が準備ができていても、コールバック関数は空になりません。

推奨される修正:

Lambda にイベントループを待たないよう指示

exports.handler = async (event, context) => {
  // イベントループがクリアされるのを待たない
  context.callbackWaitsForEmptyEventLoop = false;

  // ここにコードを記述
  const result = await processRequest(event);

  return {
    statusCode: 200,
    body: JSON.stringify(result)
  };
};

接続を適切に閉じる

// データベース接続の場合、接続プーリングを使用するか、
// 明示的に接続を閉じる

const mysql = require('mysql2/promise');

exports.handler = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false;

  const connection = await mysql.createConnection({...});
  try {
    const [rows] = await connection.query('SELECT * FROM users');
    return { statusCode: 200, body: JSON.stringify(rows) };
  } finally {
    await connection.end();  // 常に閉じる
  }
};

API Gateway ペイロードサイズ制限

重大度: 中

状況: 大きなレスポンスを返す、または大きなリクエストを受け取る

症状: 「413 Request Entity Too Large」エラー 「Execution failed due to configuration error: Malformed Lambda proxy response」 レスポンスが切り詰められるか、失敗

なぜこれが問題なのか: API Gateway には厳しいペイロード制限があります:

  • REST API: リクエスト/レスポンス 10 MB
  • HTTP API: リクエスト/レスポンス 10 MB
  • Lambda 自体: 同期レスポンス 6 MB、非同期 256 KB

これらを超えると、明らかでない失敗につながります。

推奨される修正:

大規模ファイルアップロード用

// API Gateway を通さず、プリサインド S3 URL を使用

const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');

exports.handler = async (event) => {
  const s3 = new S3Client({});

  const command = new PutObjectCommand({
    Bucket: process.env.BUCKET_NAME,
    Key: `uploads/${Date.now()}.file`
  });

  const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });

  return {
    statusCode: 200,
    body: JSON.stringify({ uploadUrl })
  };
};

大規模レスポンス用

// S3 に保存し、プリサインドダウンロード URL を返す
exports.handler = async (event) => {
  const largeData = await generateLargeReport();

  await s3.send(new PutObjectCommand({
    Bucket: process.env.BUCKET_NAME,
    Key: `reports/${reportId}.json`,
    Body: JSON.stringify(largeData)
  }));

  const downloadUrl = await getSignedUrl(s3,
    new GetObjectCommand({
      Bucket: process.env.BUCKET_NAME,
      Key: `reports/${reportId}.json`
    }),
    { expiresIn: 3600 }
  );

  return {
    statusCode: 200,
    body: JSON.stringify({ downloadUrl })
  };
};

無限ループまたは再帰呼び出し

重大度: 高

状況: イベントでトリガーされた Lambda 関数

症状: 異常なコスト。 数分で数千の呼び出し。 CloudWatch ログが繰り返される呼び出しを示す。 Lambda が自身をトリガーするソースバケット/テーブルに書き込み。

なぜこれが問題なのか: Lambda は誤って自身をトリガーできます:

  • S3 トリガーが同じバケットに書き込む
  • DynamoDB トリガーが同じテーブルを更新
  • SNS が自身をトリガーするトピックに発行
  • Step Functions で間違ったエラーハンドリング

推奨される修正:

異なるバケット/プレフィックスを使用

# プレフィックスフィルターを持つ S3 トリガー
Events:
  S3Event:
    Type: S3
    Properties:
      Bucket: !Ref InputBucket
      Events: s3:ObjectCreated:*
      Filter:
        S3Key:
          Rules:
            - Name: prefix
              Value: uploads/  # uploads/ でのみトリガー

# 出力は別のバケットまたはプレフィックスへ
# OutputBucket または processed/ プレフィックス

べき等性チェックを追加

exports.handler = async (event) => {
  for (const record of event.Records) {
    const key = record.s3.object.key;

    // 処理済みファイルをスキップ
    if (key.startsWith('processed/')) {
      console.log('Skipping already processed file:', key);
      continue;
    }

    // ファイルを処理して別の場所に書き込む
    await processFile(key);
    await writeToS3(`processed/${key}`, result);
  }
};

予約された同時実行数を回路ブレーカーとして設定

Resources:
  RiskyFunction:
    Type: AWS::Serverless::Function
    Properties:
      ReservedConcurrentExecutions: 10  # 最大 10 並行
      # 異常な呼び出しの影響範囲を制限

CloudWatch アラームで監視

InvocationAlarm:
  Type: AWS::CloudWatch::Alarm
  Properties:
    MetricName: Invocations
    Namespace: AWS/Lambda
    Statistic: Sum
    Period: 60
    EvaluationPeriods: 1
    Threshold: 1000  # 呼び出し/分が 1000 を超えたらアラート
    ComparisonOperator: GreaterThanThreshold

検証チェック

ハードコードされた AWS 認証情報

重大度: エラー

AWS 認証情報は絶対にハードコードされてはいけません

メッセージ: ハードコードされた AWS アクセスキーが検出されました。IAM ロールまたは環境変数を使用してください。

AWS シークレットキー(ソースコード内)

重大度: エラー

シークレットキーは Secrets Manager または環境変数を使用する必要があります

メッセージ: ハードコードされた AWS シークレットキー。IAM ロールまたは Secrets Manager を使用してください。

過剰にパーミッシブな IAM ポリシー

重大度: 警告

Lambda IAM ロールでワイルドカード許可を避ける

メッセージ: 過度にパーミッシブな IAM ポリシー。最小権限の原則を使用してください。

Lambda ハンドラーのエラーハンドリングなし

重大度: 警告

Lambda ハンドラーはグレースフルなエラーのために try/catch を持つべき

メッセージ: Lambda ハンドラーのエラーハンドリングなし。try/catch を追加してください。

callbackWaitsForEmptyEventLoop がない

重大度: 情報

Node.js ハンドラーは callbackWaitsForEmptyEventLoop を設定するべき

メッセージ: context.callbackWaitsForEmptyEventLoop = false を設定することを検討してください

デフォルトメモリ設定

重大度: 情報

デフォルトの 128MB は多くのワークロードには低すぎる可能性

メッセージ: デフォルトの 128MB メモリを使用。パフォーマンスの向上のために増加を検討してください。

低いタイムアウト設定

重大度: 警告

非常に低いタイムアウトは予期しない失敗を引き起こす可能性

メッセージ: 1~3 秒のタイムアウトは低い可能性があります。外部呼び出しを行う場合は増加してください。

デッドレターキュー設定なし

重大度: 警告

非同期関数は失敗した呼び出しのために DLQ を持つべき

メッセージ: DLQ が設定されていません。非同期呼び出し用に追加してください。

完全な AWS SDK v2 のインポート

重大度: 警告

AWS SDK v3 から特定のクライアントをインポートしてパッケージを小さくする

メッセージ: AWS SDK 全体をインポート。パッケージを小さくするため、モジュール形式の SDK v3 インポートを使用してください。

ハードコードされた DynamoDB テーブル名

重大度: 警告

テーブル名は環境変数から来るべき

メッセージ: ハードコードされたテーブル名。ポータビリティのため環境変数を使用してください。

連携

委譲トリガー

  • ユーザーが GCP サーバーレスを必要とする -> gcp-cloud-run(Cloud Run コンテナ、Cloud Functions イベント用)
  • ユーザーが Azure サーバーレスを必要とする -> azure-functions(Azure Functions、Logic Apps)
  • ユーザーがデータベース設計を必要とする -> postgres-wizard(RDS デザイン、または DynamoDB パターンを使用)
  • ユーザーが認証を必要とする -> auth-specialist(Cognito、API Gateway オーソライザー)
  • ユーザーが複雑なワークフローを必要とする -> workflow-automation(Step Functions、EventBridge)
  • ユーザーが AI 統合を必要とする -> llm-architect(Lambda が Bedrock または外部 LLM を呼び出し)

使用時期

このスキルは、上述の機能とパターンに明らかにマッチするリクエストがある場合に使用します。

制限事項

  • このスキルは、上述のスコープに明らかにマッチするタスクのときのみ使用してください。
  • 出力を環境固有の検証、テスト、または専門家レビューの代わりとして扱わないでください。
  • 必要な入力、許可、安全境界、または成功基準が不足している場合は、停止して明確にしてください。

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
sickn33
リポジトリ
sickn33/antigravity-awesome-skills
ライセンス
MIT
最終更新
不明

Source: https://github.com/sickn33/antigravity-awesome-skills / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: sickn33 · sickn33/antigravity-awesome-skills · ライセンス: MIT