サーバーレスアーキテクチャは、インフラ管理なしでアプリケーションを構築できます。本記事では、実践的なサーバーレスパターンを解説します。
サーバーレスアーキテクチャ
flowchart TB
subgraph Serverless["Serverless"]
APIGateway["API Gateway"]
Lambda["Lambda"]
StepFunctions["Step Functions"]
EventBridge["EventBridge"]
end
subgraph Data["データ"]
DynamoDB["DynamoDB"]
S3["S3"]
SQS["SQS"]
end
APIGateway --> Lambda
Lambda --> DynamoDB
EventBridge --> Lambda
StepFunctions --> Lambda
style Serverless fill:#f59e0b,color:#000
Lambda設計パターン
関数構成
import json
import boto3
from aws_lambda_powertools import Logger, Tracer, Metrics
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.utilities.typing import LambdaContext
logger = Logger()
tracer = Tracer()
metrics = Metrics()
app = APIGatewayRestResolver()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('my-table')
@app.get("/users/<user_id>")
@tracer.capture_method
def get_user(user_id: str):
response = table.get_item(Key={'user_id': user_id})
if 'Item' not in response:
return {"message": "User not found"}, 404
return response['Item']
@app.post("/users")
@tracer.capture_method
def create_user():
body = app.current_event.json_body
table.put_item(Item=body)
metrics.add_metric(name="UserCreated", unit="Count", value=1)
return {"message": "User created"}, 201
@logger.inject_lambda_context
@tracer.capture_lambda_handler
@metrics.log_metrics
def lambda_handler(event: dict, context: LambdaContext):
return app.resolve(event, context)
SAMテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 30
MemorySize: 256
Runtime: python3.11
Tracing: Active
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: my-service
LOG_LEVEL: INFO
Resources:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.lambda_handler
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref Table
Events:
GetUser:
Type: Api
Properties:
Path: /users/{user_id}
Method: get
CreateUser:
Type: Api
Properties:
Path: /users
Method: post
Table:
Type: AWS::DynamoDB::Table
Properties:
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: user_id
AttributeType: S
KeySchema:
- AttributeName: user_id
KeyType: HASH
Lambdaレイヤー
PowertoolsLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: powertools-layer
ContentUri: layers/powertools/
CompatibleRuntimes:
- python3.11
CommonLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: common-layer
ContentUri: layers/common/
ApiFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- !Ref PowertoolsLayer
- !Ref CommonLayer
API Gateway
REST API設計
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: my-api
EndpointConfiguration:
Types:
- REGIONAL
UsersResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
PathPart: users
GetUsersMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref UsersResource
HttpMethod: GET
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref CognitoAuthorizer
RequestValidatorId: !Ref RequestValidator
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Function.Arn}/invocations
CognitoAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoAuthorizer
Type: COGNITO_USER_POOLS
RestApiId: !Ref RestApi
IdentitySource: method.request.header.Authorization
ProviderARNs:
- !GetAtt UserPool.Arn
HTTP API(軽量版)
HttpApi:
Type: AWS::Serverless::HttpApi
Properties:
StageName: prod
CorsConfiguration:
AllowOrigins:
- "https://example.com"
AllowMethods:
- GET
- POST
AllowHeaders:
- Content-Type
- Authorization
Auth:
DefaultAuthorizer: OAuth2Authorizer
Authorizers:
OAuth2Authorizer:
AuthorizationScopes:
- email
JwtConfiguration:
issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
audience:
- !Ref UserPoolClient
Step Functions
ワークフロー定義
flowchart TB
subgraph Workflow["注文処理ワークフロー"]
Validate["入力検証"]
CheckInventory["在庫確認"]
Process["処理"]
Success["成功"]
Failed["失敗"]
end
Validate --> CheckInventory
CheckInventory --> |"在庫あり"| Process
CheckInventory --> |"在庫なし"| Failed
Process --> Success
style Workflow fill:#8b5cf6,color:#fff
ASL定義
{
"Comment": "Order Processing Workflow",
"StartAt": "ValidateInput",
"States": {
"ValidateInput": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:xxx:function:validate",
"Next": "CheckInventory",
"Catch": [
{
"ErrorEquals": ["ValidationError"],
"Next": "HandleValidationError"
}
]
},
"CheckInventory": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:getItem",
"Parameters": {
"TableName": "inventory",
"Key": {
"product_id": {"S.$": "$.product_id"}
}
},
"Next": "IsInStock",
"ResultPath": "$.inventory"
},
"IsInStock": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.inventory.Item.quantity.N",
"NumericGreaterThan": 0,
"Next": "ProcessOrder"
}
],
"Default": "OutOfStock"
},
"ProcessOrder": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "UpdateInventory",
"States": {
"UpdateInventory": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:xxx:function:updateInventory",
"End": true
}
}
},
{
"StartAt": "SendNotification",
"States": {
"SendNotification": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:ap-northeast-1:xxx:orders",
"Message.$": "$.order"
},
"End": true
}
}
}
],
"Next": "Success"
},
"Success": {
"Type": "Succeed"
},
"OutOfStock": {
"Type": "Fail",
"Error": "OutOfStock",
"Cause": "Product is out of stock"
},
"HandleValidationError": {
"Type": "Fail",
"Error": "ValidationError",
"Cause": "Input validation failed"
}
}
}
Express Workflow
ExpressStateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Type: EXPRESS
DefinitionUri: statemachine/definition.asl.json
Logging:
Level: ALL
IncludeExecutionData: true
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt LogGroup.Arn
Tracing:
Enabled: true
Events:
ApiEvent:
Type: Api
Properties:
Path: /process
Method: post
イベント駆動パターン
EventBridge連携
OrderEventBus:
Type: AWS::Events::EventBus
Properties:
Name: orders
OrderCreatedRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref OrderEventBus
EventPattern:
source:
- orders.api
detail-type:
- OrderCreated
Targets:
- Id: ProcessOrder
Arn: !GetAtt ProcessOrderFunction.Arn
- Id: SendEmail
Arn: !GetAtt SendEmailFunction.Arn
- Id: UpdateAnalytics
Arn: !GetAtt AnalyticsQueue.Arn
SqsParameters:
MessageGroupId: orders
イベント発行
import boto3
import json
eventbridge = boto3.client('events')
def publish_order_event(order):
eventbridge.put_events(
Entries=[
{
'Source': 'orders.api',
'DetailType': 'OrderCreated',
'Detail': json.dumps(order),
'EventBusName': 'orders'
}
]
)
エラーハンドリング
DLQ設定
ProcessFunction:
Type: AWS::Serverless::Function
Properties:
DeadLetterQueue:
Type: SQS
TargetArn: !GetAtt DLQ.Arn
EventInvokeConfig:
MaximumEventAgeInSeconds: 300
MaximumRetryAttempts: 2
DestinationConfig:
OnFailure:
Destination: !GetAtt FailureQueue.Arn
OnSuccess:
Destination: !GetAtt SuccessTopic.Arn
ベストプラクティス
flowchart TB
subgraph BestPractices["Best Practices"]
SinglePurpose["単一責任"]
Stateless["ステートレス"]
Timeout["適切なタイムアウト"]
ErrorHandling["エラーハンドリング"]
end
style BestPractices fill:#22c55e,color:#fff
| カテゴリ | 項目 |
|---|---|
| 設計 | 単一責任の関数 |
| パフォーマンス | Provisioned Concurrency |
| 信頼性 | DLQ/リトライ設定 |
| 運用 | X-Rayトレーシング |
まとめ
| サービス | 用途 |
|---|---|
| Lambda | コンピュート |
| API Gateway | APIエンドポイント |
| Step Functions | オーケストレーション |
| EventBridge | イベント駆動 |
サーバーレスパターンを活用して、スケーラブルなアプリケーションを構築しましょう。