AWSサーバーレスアーキテクチャの冗長設計をStep Functionsで実現する方法|エラー処理の設計

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > AWS(Amazon Linux) > AWSサーバーレスアーキテクチャの冗長設計をStep Functionsで実現する方法|エラー処理の設計
「Lambda関数がたまにタイムアウトで失敗するんだけど、どうリトライすれば安全なの?」
「Step Functionsのワークフローが途中で止まったとき、どこから再開するのかがわからない。」

サーバーレスアーキテクチャは「サーバーを管理しなくていい」というメリットがある一方、エラーが起きたときの挙動が見えづらく、冗長設計が後回しになりがちです。

この記事では、AWS Lambda・Step Functions・SQSを組み合わせたサーバーレス構成で、エラー処理と冗長設計をどう組み立てるかを解説します。サービス単体のコマンド解説ではなく、複数サービスを組み合わせたアーキテクチャ設計層の話として、冗長性の確保とリトライ設計の考え方を体系的に整理します。想定環境はAWS東京リージョン(ap-northeast-1)のVPC統合Lambdaを前提としています。

この記事のポイント

・Step FunctionsのRetryにJitterStrategy: FULLを加えると分散リトライになる
・Catchで全エラーを拾い、エラー内容をResultPathに保存して後続処理へ渡せる
・Lambda関数はべき等性設計(DynamoDB条件付き書き込み)で二重実行を防ぐ
・VPC Lambdaは複数AZのサブネットを指定することでマルチAZ冗長になる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

サーバーレスアーキテクチャで冗長設計が必要になる3つの理由

「Lambdaはマネージドだから、AWSが面倒を見てくれるはず」と思っている人は少なくありません。確かにインフラ自体の冗長性はAWSが担保していますが、アプリケーション層のエラー処理まで自動でやってくれるわけではありません。

サーバーレス構成で冗長設計が必要になる主な原因は3つあります。

・サービス側の一時障害:Lambda・DynamoDB・外部API等、下流サービスが一時的に不安定になることがあります。リトライ設計がなければワークフロー全体が失敗します
・タイムアウトと同時実行制限:Lambda関数はデフォルト最大実行時間15分ですが、アカウント単位・関数単位の同時実行数上限(デフォルト1,000)に達すると新規呼び出しが絞られます
・部分失敗の見えにくさ:複数のLambda関数を直列・並列で組み合わせたとき、どのステップで何が起きたかをStep Functionsの実行履歴なしに追いにくくなります

これらを踏まえて、Step Functions・Lambda・SQSの3層でエラー処理と冗長設計を組み立てる方法を解説します。

Step FunctionsのRetryとCatchでエラー処理を設計する方法

Step Functionsのステートマシン定義(ASL: Amazon States Language)では、各Taskステートに`Retry`と`Catch`を設定できます。この2つを正しく組み合わせることが、サーバーレスの冗長設計の中心になります。

1. Retry(リトライ)の設定と指数バックオフ

Lambdaが一時的なサービス例外で失敗したとき、Step Functionsに自動リトライさせるにはタスク定義の`Retry`配列に条件を書きます。

Lambdaサービス例外に対してリトライを設定するASLの例です。

# Step Functionsステートマシン定義(ASL形式・一部抜粋) { "Comment": "Lambda失敗時のリトライ設定例", "StartAt": "ProcessData", "States": { "ProcessData": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:ProcessDataFn", "Retry": [ { "ErrorEquals": [ "Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException" ], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2.0, "JitterStrategy": "FULL" } ], "End": true } } }

各パラメータの意味を確認しておきましょう。

・IntervalSeconds:初回リトライまでの待機秒数(上記では2秒)
・MaxAttempts:最大リトライ回数。0にするとリトライなし
・BackoffRate:待機秒数の乗数。2.0なら2秒→4秒→8秒と倍増
・JitterStrategy: FULL:バックオフ時間をランダムに分散させる機能。同じワークフローが多数同時にリトライする場合、一斉リトライによる下流サービスへの集中負荷を分散できます

`Lambda.TooManyRequestsException`はスロットリングエラーです。このエラーはリトライで解消できる場合が多いため、必ずリトライ対象に含めてください。

2. Catch(エラーキャッチ)でフォールバック処理を設計する

`MaxAttempts`を使い切っても失敗した場合、または`Retry`に含まれていないエラーが起きた場合は`Catch`で捕捉します。

# CatchでエラーハンドリングStateへ遷移する設定例(ASL抜粋) "ProcessData": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:ProcessDataFn", "Retry": [ { "ErrorEquals": ["Lambda.TooManyRequestsException"], "IntervalSeconds": 3, "MaxAttempts": 5, "BackoffRate": 2.0, "JitterStrategy": "FULL" } ], "Catch": [ { "ErrorEquals": ["States.TaskFailed"], "Next": "NotifyFailure", "ResultPath": "$.error" }, { "ErrorEquals": ["States.ALL"], "Next": "NotifyFailure", "ResultPath": "$.error" } ], "Next": "SaveResult" }

`ResultPath: "$.error"` を設定すると、エラー情報(エラー名とメッセージ)が入力データの`$.error`フィールドに追加されて次のステートに渡されます。これにより通知処理やログ記録の際に、どのエラーが発生したかを後続ステートで参照できます。

`States.ALL`はすべてのエラーにマッチするキャッチオールです。`States.TaskFailed`はLambda等のタスクが失敗したことを示すStep Functions組み込みエラーです。特定エラーを先に書き、最後に`States.ALL`を置くのが基本パターンです。

3. Dead Letter Queue(DLQ)との組み合わせで保全を高める

Step Functionsのエラー処理と並行して、Lambda関数レベルのDLQ(デッドレタキュー)設定も重要です。特に非同期呼び出し(イベント駆動)を使う場合、Lambda側のリトライでも失敗したメッセージをSQSのDLQに保存しておくことで、後から再処理できます。

DLQを設定するaws lambdaコマンドの例です。

# Lambda関数の失敗時送信先にSQS DLQを設定する aws lambda update-function-event-invoke-config \ --function-name ProcessDataFn \ --destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:ap-northeast-1:123456789012:ProcessDataDLQ"}}' \ --region ap-northeast-1 # DLQに蓄積されたメッセージ数を確認する aws sqs get-queue-attributes \ --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/ProcessDataDLQ \ --attribute-names ApproximateNumberOfMessages \ --region ap-northeast-1

出力例(環境により異なります)

{ "Attributes": { "ApproximateNumberOfMessages": "0" } }

DLQに積まれたメッセージは、問題を修正してから手動または自動でメインキューに戻して再処理します。定期的に`ApproximateNumberOfMessages`をCloudWatchで監視し、増えてきたらアラートを出す設計にしておくと見落としを防げます。

Lambda関数のべき等性設計でリトライを安全にする

リトライ設計を機能させるためには、Lambda関数側が「べき等(同じリクエストを複数回処理しても1回と同じ結果になる)」であることが前提です。べき等でない関数は、リトライのたびに二重処理・重複レコードを生みます。

よく使われる手法がDynamoDBの条件付き書き込みを使った重複排除です。

# DynamoDBに実行IDをキーとして書き込む(Python Lambda内での処理パターン) # PK に Step Functions の executionId を使い、attribute_not_exists でべき等性を確保する import boto3 from botocore.exceptions import ClientError dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('ProcessedJobs') def lambda_handler(event, context): execution_id = event.get('executionId') try: # attribute_not_exists(id) の条件:idが存在しなければ書き込む(重複なら失敗) table.put_item( Item={ 'id': execution_id, 'status': 'processing', }, ConditionExpression='attribute_not_exists(id)' ) except ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': # 同じexecutionIdで既に処理済み → スキップして正常終了 return {'status': 'already_processed', 'executionId': execution_id} raise # ここから実際のビジネスロジックを実行する result = do_business_logic(event) table.update_item( Key={'id': execution_id}, UpdateExpression='SET #s = :done', ExpressionAttributeNames={'#s': 'status'}, ExpressionAttributeValues={':done': 'completed'} ) return result

Step Functionsで実行するLambdaには、`context.aws_request_id`(Lambda側の一意なリクエストID)か、Step Functionsが渡す実行ID(ステートマシン入力に乗せて渡す)をキーに使います。重複書き込みを`ConditionalCheckFailedException`で検知してスキップすることで、リトライが安全に行えます。

AWSのサーバーレスアーキテクチャとLinuxサーバー管理を体系的に学びたい場合は、AWS(Amazon Linux)入門からサーバー構築まで解説している記事もあわせて参考にしてください。

VPC LambdaのマルチAZ冗長設計

プライベートサブネット内のリソース(RDSやElastiCache等)にLambdaからアクセスするために、「VPC Lambda」(Lambda関数をVPCに統合する設定)を使うケースがあります。このとき、AZ障害への耐性を確保するには複数のAZのサブネットをLambdaの設定に含めることが重要です。

VPC Lambdaで複数サブネットを指定するCLIコマンドの例です。

# Lambda関数のVPC設定に複数AZのサブネットを指定する(東京リージョン 1a/1b/1c) aws lambda update-function-configuration \ --function-name ProcessDataFn \ --vpc-config SubnetIds=subnet-aaaaaa01,subnet-bbbbbb02,subnet-cccccc03,SecurityGroupIds=sg-xxxxxxxx \ --region ap-northeast-1

・subnet-aaaaaa01:ap-northeast-1a のプライベートサブネット
・subnet-bbbbbb02:ap-northeast-1b のプライベートサブネット
・subnet-cccccc03:ap-northeast-1c のプライベートサブネット

サブネットを1つだけ指定した場合、そのAZが障害になるとLambda関数が起動できなくなります。東京リージョンでは3AZを指定しておくのが標準的です。また、各サブネットのNATゲートウェイや、エンドポイント経由の場合はVPCエンドポイントも各AZに配置しておく必要があります。

サーバーレス構成の全体像を整理すると、LambdaとStep Functions自体はリージョンスコープのマネージドサービスであり単一AZ障害の影響を受けません。マルチAZ設計が必要になるのは主に次の部分です。

・VPC Lambda(プライベートサブネット統合)のサブネット配置
・Lambda関数から接続するRDS/ElastiCacheの配置AZ
・SQS DLQのメッセージ保持設定(リージョンサービスのためAZ設定不要だが、保持期間の設計は必要)

トラブルシュート:よくあるエラーと対処

「Lambda.TooManyRequestsException」が頻発する

同時実行数の上限に達しているか、特定Lambdaにリクエストが集中しているケースです。対処は2つあります。

・予約済み同時実行数(Reserved Concurrency)を関数に設定し、重要なワークフロー用のLambdaには一定の実行枠を確保する
・プロビジョニング済み同時実行数(Provisioned Concurrency)でコールドスタートを減らし、急増リクエストへの応答速度を上げる

# Lambdaの予約済み同時実行数を100に設定する aws lambda put-function-concurrency \ --function-name ProcessDataFn \ --reserved-concurrent-executions 100 \ --region ap-northeast-1

Step Functionsの実行が「RUNNING」のまま止まる

Step FunctionsのタスクがwaitForTaskTokenパターンを使っている場合、タスクトークンのコールバックが届かずに待ち続けることがあります。`HeartbeatSeconds`を設定してタイムアウトを強制させましょう。

# waitForTaskTokenパターンに HeartbeatSeconds を設定する(ASL抜粋) "WaitForExternalSystem": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken", "Parameters": { "FunctionName": "ExternalCallFn", "Payload": { "taskToken.$": "$$.Task.Token", "input.$": "$" } }, "HeartbeatSeconds": 300, "TimeoutSeconds": 3600, "Retry": [ { "ErrorEquals": ["States.HeartbeatTimeout"], "IntervalSeconds": 10, "MaxAttempts": 2 } ], "Catch": [ { "ErrorEquals": ["States.HeartbeatTimeout", "States.Timeout"], "Next": "HandleTimeout" } ] }

DLQにメッセージが蓄積し続ける

DLQはあくまで「後で処理するための保全」です。蓄積し続ける場合はアプリケーション側に根本的な問題がある可能性が高いため、コードを修正してからメッセージを再処理(Dead-letter queue redrive)する流れになります。CloudWatchのメトリクス`ApproximateNumberOfMessagesNotVisible`も確認し、処理中で詰まっていないかも合わせて見てください。

本記事のまとめ

設計要素 推奨設定・パターン
Step Functions Retry JitterStrategy: FULL付き指数バックオフ、MaxAttempts 3~5
Step Functions Catch 特定エラーを先に書き、最後にStates.ALLで全捕捉、ResultPathでエラー情報を保持
DLQ(Dead Letter Queue) Lambda非同期呼び出しにSQS DLQを設定して再処理に備える
べき等性設計 DynamoDB条件付き書き込み(attribute_not_exists)で二重処理を防ぐ
VPC LambdaのマルチAZ 東京リージョンなら1a/1b/1cの3サブネットをSubnetIdsに指定する
同時実行数対策 Reserved Concurrencyで重要ワークフローの実行枠を確保する
サーバーレスアーキテクチャは「インフラを管理しなくていい」だけで、エラー処理の設計責任はアプリケーション側にあります。Retry・Catch・べき等性・マルチAZサブネットの4点を押さえておくことで、本番環境でも安心できる冗長設計の土台ができます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、AWSを含むサーバー管理の実務スキルを2日間で習得できる「Linux Master Pro Seminar」の詳細をご確認ください。
>> AWSでのLinuxサーバー構築を学ぶ

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。