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
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/sickn33/antigravity-awesome-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。