📖

re:Invent 2025: AWS Lambdaの内部アーキテクチャとEvent Source Mappingの実装詳細

に公開

はじめに

海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!

re:Invent 2025 の書き起こし記事については、こちらの Spreadsheet に情報をまとめています。合わせてご確認ください

📖 re:Invent 2025: AWS re:Invent 2025 - From Trigger to Execution: The Journey of Events in AWS Lambda (CNS423)

この動画では、AWS LambdaのServerlessチームのJulianとプリンシパルエンジニアのRajesh Pandeが、Lambdaの内部アーキテクチャを詳細に解説しています。毎月15兆以上の呼び出しを処理するLambdaの3つのinvokeタイプ(Synchronous、Async、Event source mapping)の仕組み、Firecrackerベースの実行環境、そしてLambda Managed Instancesの新機能について説明されます。特にpollerアーキテクチャに焦点を当て、キューイング理論を適用した設計思想、shuffle shardingやtoken bucketを用いた負荷分散、DynamoDB障害時の復旧戦略など、大規模分散システムの運用における具体的な課題と解決策が示されます。バッチ処理、フィルタリング、エラーハンドリングなどのEvent Source Mapping機能の実装詳細も含まれています。

https://www.youtube.com/watch?v=4zFJ3zDWgeM
※ こちらは既存の講演の内容を最大限維持しつつ自動生成した記事になります。誤字脱字や誤った内容が記載される可能性がありますのでご留意下さい。

本編

Thumbnail 0

Lambda under the hoodシリーズの紹介:サーバーレスの秘密とAWSの役割

私はJulianです。AWSのServerlessチームで開発者アドボケートをしており、Lambdaが提供する素晴らしい機能を使ってアプリケーション開発を加速させるのを支援することが大好きです。Rajeshさん、皆さんこんにちは。私はRajesh Pandeで、AWS Lambdaのプリンシパルエンジニアです。Lambdaに携わって6年になりますが、その間にLambdaが数千の顧客から数百万の顧客へと成長するのを見てきました。本当に素晴らしい経験です。今日はそうした楽しい話をいくつか皆さんと共有できるのを楽しみにしています。

Thumbnail 30

では、今日は前回のre:Inventから続いているLambda under the hoodシリーズの一部をお話しして、Lambdaについてもっと理解していただきたいと思います。Lambdaの基礎、様々な呼び出し方法、そしてポーリング機能についてさらに詳しく説明します。その後、Rajeshが戻ってきて、Lambdaをどのように構築したか、どのような選択をしたか、そしてポーラーについてのさらに詳しい情報について、深掘りしていきます。正直に言うと、Lambdaの魔法の一部は、今日お話しすることの多くは実は知る必要がないものです。私たちがすべて処理してくれるので。本当に素晴らしいサービスで、これほどの規模でどのように動作するのかが謎に見えないように、仕組みを説明するのが大好きです。ただ、魔法のように見えるかもしれませんが。

Thumbnail 70

Thumbnail 80

Thumbnail 90

では、トリビアから始めましょう。サーバーレスの最大の秘密を知りたい人はいますか?実は、その下には相当数のサーバーがあるんです。申し訳ありませんが、実は数十万台もあります。ただし、消費者である皆さんはそれらを見ることはありません。なぜなら、AWSである私たちがこの基盤となるインフラストラクチャをすべて管理しているからです。これにより、皆さんはビジネスシナリオに集中することができます。 Lambdaについては、皆さんが卵、つまりコードをもたらし、ビジネスの生命をもたらすと考えています。そして私たちは、皆さんの卵を世話するために、規模に応じた信じられないほど堅牢な巣を構築しました。 これは、皆さんが自分で多くのことを保持したり構築したりする必要がないということを意味します。オートスケーリングロジック、アベイラビリティゾーンのフェイルオーバー、リトライロジック、コネクションプーリング、バックプレッシャー、リース管理など、こうしたあらゆる種類のことを私たちが引き受けるので、皆さんはそれをする必要がありません。

Thumbnail 110

Thumbnail 120

Thumbnail 130

Thumbnail 140

Thumbnail 150

Lambdaの規模と基盤技術:FirecrackerからLambda Managed Instancesへ

これは膨大な規模で実行されており、毎月15兆以上の呼び出しがあり、Prime Dayだけで1.7兆の呼び出しがあります。 私たちは99.99%の可用性と復元力をサービスに組み込んでいるので、それのために追加料金を支払う必要はありません。 Lambdaの基礎についていくつか復習しましょう。Lambdaの主要なテクノロジー基盤の1つはオープンソースのFirecrackerですが、今は基盤を拡張して、昔ながらのEC2も含めています。 日曜日に発表されたLambda Managed Instancesは、Lambdaの運用の簡潔性とEC2のインスタンスの範囲を提供します。Gravitonまたはプロセッサ、メモリ、またはネットワーク最適化インスタンスをLambda関数に使用することを選択でき、これらのインスタンスを自分で管理する必要はありません。 また、大規模なワークロードと複数の同時実行性、およびEC2の価格インセンティブで最大12倍の節約が得られ、すべてパッケージに含まれています。また、session CNS382もあり、そこでもっと詳しく知ることができます。

Thumbnail 180

Thumbnail 190

通常のLambda、いわゆるオンデマンドでは、すべてがLambda service VPC内で実行され、呼び出しはクライアントから来て、最終的にはLambda service VPC内のLambda所有のEC2ワーカーホスト上で実行されます。 Lambda Managed Instancesでは、コントロールプレーンはLambdaアカウント内に留まりますが、皆さんのアカウント内、皆さんのVPC内にEC2インスタンスをプロビジョニングして管理し、その上で実行されているコンテナに関数呼び出しをルーティングします。 つまり、Lambda on-demandとFirecrackerとLambda Managed Instancesは2つの異なるコンピュートモデルですが、残りのアーキテクチャは基本的に同じままです。

Thumbnail 210

Lambdaの3つの呼び出しタイプとアーキテクチャの全体像

Lambda には3つの invoke タイプがあります。Synchronous は、呼び出し元が直接呼び出すか API Gateway 経由で呼び出す場合で、リクエストを Lambda サービスに送信して処理を行い、レスポンスを待ってからクライアントに返します。Async リクエストの場合は、クライアント呼び出しか S3 の変更通知や EventBridge ルールのマッチングなどを通じてイベントを送信しますが、関数コードからのレスポンスを待ちません。イベントを Lambda に渡すと、Lambda がそれ以降の処理を引き継ぎます。イベントを内部キューに置いて、呼び出し元に「イベントを受け取りました」という成功レスポンスを返し、その後、別のプロセスがそれを処理します。

Thumbnail 260

Thumbnail 260

Event source mapping は、Kinesis や DynamoDB などのストリームから、または SQS などのキューからアイテムを読み取る Lambda リソースです。プロデューサーアプリケーションがこれにメッセージを置いておくと、Lambda がポーラーを管理します。今日はそれについてカバーしますが、メッセージを読み取ってから invoke に送信して処理します。 Lambda API はフロントエンドで、Lambda サービスへのすべてのリクエストがここに到達し、マルチ AZ で lambda.region-name.amazonaws.com に解決するロードバランサーを備えています。これが invoke リクエストをデータプレーンにルーティングし、最終的にはワーカーホスト(あなたのものか私たちのもの)に到達します。これが Lambda でのイベントの流れです。

Thumbnail 290

Thumbnail 300

フロントエンドロードバランサーは、コントロールプレーンリクエストと invoke リクエストを区別します。コントロールプレーンリクエストは、コンソール、CLI ツール、または SDK からの関数設定またはリソース管理です。その後、データプレーンが別のデータプレーンにルーティングします。1つは sync invoke 用で、もう1つは async invoke 用です。

Thumbnail 340

技術的には、これを front-end invoke service と呼び、async を event invoke front-end service と呼びますが、命名は少し混乱するので、シンプルにするために sync と async で統一します。これは実は意図的な選択で、数年前に巨大な async スパイクが sync サービスを圧倒した時に、システムに組み込まれました。これで sync サービスを async フラッドから保護できます。データプレーンはまた、ステータスホストのフリートです。Lambda はマルチ AZ サービスなので、複数のアベイラビリティゾーン間で関数の負荷分散について心配する必要はありません。Lambda がそれを処理します。

Thumbnail 360

パフォーマンスと安定性のために、データプレーンは function versions DynamoDB テーブルから関数に関する情報を取得する必要があり、ホスト上と L2 AZ キャッシュの両方でキャッシュします。invoke をできるだけ高速にしたいので、すべての invoke でルックアップを避けたいのです。このアプローチは可用性にも役立ちます。

Thumbnail 390

同期呼び出しパスの詳細:認証からexecution environmentまで

では、同期パスについてもっと詳しく見ていきましょう。ALB は sync invoke service の一部として、invoke リクエストをホストのフリートに分散させます。ここでは図を簡略化しています。複雑になってくるので。Lambda は複数のアベイラビリティゾーンにわたってすべてが実行されるように構築されています。sync invoke service はまず、リクエストの認証と認可を実行して、Lambda がセキュアであることを確認し、認可された呼び出し元だけが Lambda のフロントドアを通過できるようにします。

Thumbnail 410

その後、サービスはリクエストとともにメタデータをロードします。フロントエンドはまた、counting service と呼ばれるものを呼び出します。これはアカウントまたは予約済みコンカレンシーに基づいて、クォータ制限を適用する必要があるかどうかをチェックします。counting service は高スループット用に最適化されており、1.5 ミリ秒以下のレイテンシーである必要があります。各 invoke で呼び出されるからです。invoke パスに対して重要であるため、複数のアベイラビリティゾーンにわたって高可用性を備えており、非常に高速です。

Thumbnail 430

その後、フロントエンドは assignment service と呼ばれるものと、また control plane service という別のコンポーネントと通信します。これは調整とバックグラウンドタスクを管理するコンポーネントです。関数の作成などの control plane 関連のことを処理し、assignment service ノードのライフサイクルを管理します。assignment service は placement service を調整します。

Thumbnail 450

関数の最初の invoker の場合、ワーカーホスト上で新しい execution environment を開始する必要があります。placement service は時間ベースのリースを使用してワーカー上に execution environment を作成します。また、ワーカーのヘルスをモニタリングし、ワーカーを unhealthy としてマークするタイミングを判断します。execution environment が起動して実行されると、init パスが見えます。assignment service は、定義した権限を持つ IAM ロールと環境変数を提供し、それらをワーカーに渡します。

Thumbnail 470

その後、execution environment は Java、Python、または別の言語かどうかに関わらず、言語ランタイムを開始し、関数コードをダウンロードするか、コンテナイメージを実行します。その後、関数の init プロセスが実行されます。sync invoke service は invoke を実行し、関数が実行されて、結果を呼び出し元に返します。レイテンシーを最小化するために、ワーカーホスト上の invoke proxy に直接呼び出します。

Thumbnail 480

後続のリクエストが来ると、フロントエンドは assignment service と通信します。そこで「はい、既に実行環境が起動して動作しています」と言われて、invoke ペイロードがその実行環境に直接ルーティングされ、ハンドラーが実行されます。実行環境のリースが終了するか何らかのエラーが発生すると、assignment service は段階的に接続をドレインして、実行環境をシャットダウンし、今後の invoke を停止することができます。

Thumbnail 510

Lambdaワーカーホストの内部構造とFirecrackerの再設計

Lambda ワーカーの詳細を見てみましょう。ワーカーホストは、実行環境を配置する標準的なベアメタル EC2 インスタンスです。効率的に invoke をできるだけ高速に実行するために、ホスト上にはさまざまなメモリサイズの空の microVM プールを保持しています。invoke が来ると、ホスト管理はリクエストを満たすために最小のメモリサイズを割り当てて、コードまたはイメージをダウンロードできます。実行環境を起動するのに時間がかかる可能性があるため、事前に準備しておくことで、これは大幅に高速化されます。

Thumbnail 540

Firecracker ワーカーの場合、Firecracker は OS の上に位置し、invoke 用の単一のセキュアな実行環境を管理するプロセスです。実行環境内では、sandbox と呼ばれるものを保持しています。これは基本的には特権コンテナ namespace で、関数コード、ランタイム、エクステンション、またはコンテナイメージ用のものです。また、コードがアクセスできない別の managed sandbox も保持しており、これにより実行環境内のものを管理できます。

Thumbnail 550

興味深いことに、実は今年、Lambda の現在の動作方法を改善し、将来の機能を構築するために、これがどのように機能するかを完全に再設計しました。これはフリート全体にロールアウトされており、どこを見るべきかを知らない限り、気づくことはありません。数兆の invoke が発生しており、基本的には Lambda のタイヤを、車が狂ったような速度で動いている最中に交換しているようなものです。

Thumbnail 590

sandbox の外側では、制御プレーン機能用の management agent と、実行環境からメトリクスとログを透過的に取得できる telemetry agent も実行しています。ワーカーホストには、その他多くのコンポーネントがあります。Host management は他のサービスと調整し、それらの実行環境を構築および破棄します。

Thumbnail 600

invoke proxy は、フロントエンドからあなたのコードへの直接的なリンクです。

Thumbnail 630

また、runtime やバイナリなどの microVM ファイルをホスト上に保存して、より高速に読み込めるようにする必要があります。そして、コンテナイメージのキャッシュと、パフォーマンスのためにできるだけ多くのものをキャッシュしています。また、コンテナイメージ制御プレーンに外部と通信してコンテナイメージをダウンロードするエージェント、そして関数コードを実行環境に取得するために S3 に接続するエージェントが必要です。もちろん、これは外部に接続する必要があるので、外の世界に接続するためのネットワークがあります。

Thumbnail 640

Lambda マネージドインスタンスの場合、基本的にはほぼ同じですが、Firecracker を使用せず、論理的な分離のために containerd を使用し、microVM 実行環境の代わりにコンテナを使用しています。

Thumbnail 650

Thumbnail 660

非同期呼び出しの仕組み:内部SQSキューとポーラーの役割

では、async invokes を見てみましょう。これは sync サービスと同様の方法で機能しますが、async イベントリクエストのみを処理します。また、最近ペイロードを 1 メガバイトに増やしたので、さらに多くのユースケースに対応できるようになりました。フロントエンドは invoke リクエストを内部 SQS に送信し、呼び出し元に Lambda が関数を非同期で呼び出すというアクナレッジメントで応答します。リクエストを受け取りました、そして Lambda が SQS キューを管理するので、実際にはそれらの可視性さえありません。

Thumbnail 690

Thumbnail 700

複数のキューを実行し、負荷と関数の同時実行数に応じて動的にスケールアップおよびスケールダウンします。キューの一部は共有されていますが、Lambda が可能な限り低いレイテンシーで膨大な量の async invokes を処理できるようにするために、一部のイベントを専用キューに送信します。その後、管理する複数のポーラーインスタンスのフリートがあり、詳細についてはカバーします。これらのポーラーは内部 SQS からメッセージを読み取り、関数、アカウント、ペイロードを決定してから、最終的に invoke リクエストを同期的に再び sync invoke サービスに送信します。

Thumbnail 730

ご覧の通り、すべての Lambda の呼び出しは最終的には同期呼び出しとしてランディングし、その関数は前にお話しした同じ同期パスウェイを使用します。関数はレスポンスをポーラーに返し、ポーラーはそのメッセージをキューから削除します。リクエストが成功しない場合、ポーラーは実際にはメッセージをキューから削除せず、自分の SQS キューで使用するのと同じ可視性タイムアウトを使用します。その後、同じまたは別のポーラーがメッセージをピックアップして再度試行できます。

Thumbnail 740

非同期呼び出しのイベント宛先を設定して、処理後のコールバックを提供することもできます。呼び出しが成功したか失敗したか、またはすべての再試行が終了した後かどうかに関わらずです。その他のコントロールプレーンサービスも関わっています。キューマネージャーがあり、キューの管理、監視、バックアップが必要かどうかの確認、新しいキューの作成と削除を処理する必要があります。

これは leasing service とうまく連携し、leasing service はどのポーラーがどのキューを処理しているかを管理し、ポーラーが失敗したときを検出して、その作業を別のポーラーに渡すことができます。

Thumbnail 760

Event Source Mappingの機能とストリーム・キューの処理

では、event source mapping またはポーラー呼び出しを見てみましょう。プロデューサーアプリケーションは非同期でメッセージをストリームまたはキューに配置します。

Thumbnail 770

その後、イベントソースが何であるかに応じて異なるクライアントがあるため、わずかに異なるポーラーを複数実行します。これは、その後の非同期呼び出しと非常に似たアーキテクチャです。

Thumbnail 790

ポーラーはストリームまたはキューからメッセージを読み取り、それらをフィルタリングし、バッチ処理し、単一のペイロードにまとめて、同じ sync frontend service を使用して関数に同期的に送信することもできます。覚えておいてください、すべての Lambda 呼び出しは最終的には同期になります。

Thumbnail 800

キューの場合、ポーラーは関数が正常に処理したときにメッセージをキューから削除できます。イベントソースに応じて、SQS、SNS、EventBridge、S3、または最近発表した Kafka ソースの場合は Kafka に再度情報を送信できます。

Thumbnail 810

また、多くのコントロールプレーンサービスがあり、state manager、stream tracker があり、イベントソースに応じて、ポーラーを管理し、イベントソースを管理し、作業を検出し、ポーラーフリートのスケーリングを処理します。

Thumbnail 830

leasing service はポーラーを特定のイベントまたはストリーミングソースで作業するように割り当てます。再び問題がある場合、その作業を別のポーラーに移動します。キューとストリームの 2 つのタイプのイベントソース間には、アーキテクチャ上の違いもあります。

キューは個別のタスク処理用で、各メッセージは独立しており、処理後にメッセージが削除されます。ストリームは複数のコンシューマーがいる場合、おそらく同じ種類のデータが必要で、特に順序が重要な場合、そしてメッセージはリプレイのために再試行される場合です。これらは 2 つの異なるアーキテクチャスタイルです。

Thumbnail 860

Thumbnail 880

Lambda Event Source Mapping は、これらすべてのイベントソースにわたって、たくさんの機能を提供しています。フィルタリング、バッチコントロール、ストリームに対するものも含めて、バッチを分割して問題のあるレコードを見つけることができます。ストリーム内のどこから開始するかを選択したり、リトライと失敗処理、Kinesis のアナリティクス、そしていくつかのプラットフォームパフォーマンス設定オプションがあります。 素晴らしいことは、Lambda Event Source Mapping がこれらすべてをあなたの代わりに処理してくれるということです。Event Source Mapping を設定する以外に、異なる特性について考える必要はありません。実際にすべてが起こるようにするタスクは、私たちが引き受けます。

Thumbnail 890

Event Source Mappingの高度な機能:認証、順序保持、フィルタリング、バッチ処理

Event Source Mapping をもう少し詳しく見て、ストリーミングとキューイングの両方でどのように機能するかを確認しましょう。Event Source Mapping はイベントソース、キューまたはストリームから利用可能なレコードをプルします。 さて、実際にイベントソースに到達することについて考える必要があります。SQS と Kinesis はパブリックエンドポイント上にありますが、プライベートサブネットに接続されているイベントソース、または AWS の外部にあるイベントソース、しばしば Kafka を使用しているものもあります。ですから、実際の関数が VPC に接続されている可能性があるのと同様に、それも処理する必要があります。

Thumbnail 910

Thumbnail 920

Thumbnail 930

Thumbnail 940

認証はイベントソースによって異なります。AWS サービスの場合、IAM を使用できますが、Kafka の場合は、ESM が認証できるようにすべての可能な認証方法をサポートしたいと考えています。 SQS FIFO やストリーミングソースのような順序付けがある場合、メッセージ処理で順序が常に維持されることを確認する必要があります。ストリームの場合、ESM 設定の一部として開始位置を 最初から、特定のタイムスタンプから、または最新のレコードだけを取得するように設定できます。ESM はまた、オフセットまたはシーケンス 番号を使用してストリーム内のどこにいたかを覚えておく必要があります。

Thumbnail 960

Thumbnail 970

リトライとリシャーディングを処理する必要があります。ストリームは成長または縮小する可能性があるため、新しい Kafka ブローカーがオンラインになっているか、またはイベントソースの 1 つから処理する新しいシャードまたはパーティションがあるかどうかを確認するために、ソースを継続的に監視する必要があります。親シャードが分割されて新しい子シャードが作成されるとき、このプロセス中に順序を維持する必要があります 。そうすることで、正しいレコードを新しい Lambda 関数に送信して処理をスケールアップできます。Kinesis の場合、tumbling windows と呼ばれる便利な追加設定があり、これをデータ集約に使用できます。 複数の個別の Lambda 呼び出しにわたって状態を渡して、リアルタイムでデータを集約し、いくつかの分析ソリューションに対して迅速で簡単な代替手段を提供できます。

Thumbnail 980

Thumbnail 990

Kinesis から消費する方法は 2 つあります。 最大 5 つのコンシューマーがストリームの読み取りスループットを共有できる shared fan-out があり、各シャードは約 200 ミリ秒のレイテンシーで読み取りレートとデータレートをサポートしています。しかし、ストリームデータへのより高速な応答のために、Kinesis Enhanced Fan-Out または EFO をセットアップできます。これにより、ESM が Kinesis からプルするフローから、Kinesis が HTTP プッシュメカニズムを使用して ESM にデータを送信するフローに変わり、ESM にデータをより高速に、最低 70 ミリ秒で取得します。ESM はこれを理解し、ロジックフローの変更を透過的に管理します。これは自分で管理する必要がないものです。

Thumbnail 1030

SQS と Kafka 向けの専用ポーラーオファリングもあり、スパイクに対応するためにポーラーを事前にプロビジョニングできます。最小ポーラー数と最大ポーラー数を含むさまざまな設定を構成して、スループットを制御できます。これにより、ポーリングのスケーリングとスループットを劇的に向上させることができます。SQS では標準ポーリングの 1,250 と比べて 16 倍のスループット、最大 20,000 の同時実行数を実現できます。Kafka も、これを有効にするとネットワークとコストモデルを簡素化しますが、イベント処理ユニットに基づく追加料金が発生し、これを管理できます。プロビジョニングモードは、トラフィックが多いソースで低レイテンシーが必要な場合に最適で、スケール時にコストを削減できます。

Thumbnail 1070

Thumbnail 1080

Thumbnail 1090

プロビジョニングモードにより、スキーマレジストリをサポートし、Avro や Protocol Buffers などの効率的な バイナリ形式を可能にするなど、ポーラーに新しい機能を構築して追加できます。フィルタリングにより、処理したくないレコードを削除できます。なぜでしょうか。フィルタリングは CPU 集約的なプロセスであり、Lambda の呼び出しやコード内でそれを実行して無駄にしたくないからです。処理したいメッセージを含めるポジティブフィルタリングか、特定の条件に一致しないすべてのメッセージを除外する ネガティブフィルタリングを使用できます。これは EventBridge フィルタリングと同じ構文を使用して非常に柔軟です。この例では、タイヤ圧が 32 未満のメッセージを処理して、それらだけを関数に送信しています。

Thumbnail 1110

Thumbnail 1120

Thumbnail 1140

次はバッチ処理です。これはレコードをグループ化して効率的に処理でき、Lambda の呼び出しを効率的に制御できます。バッチはどのように定義されるのでしょうか。複数の方法でこれを構成して制御できます。サイズを構成できます。これは 1 から デフォルトの 10,000 までの範囲です。通常は 10 個の項目から始めますが、10,000 まで増やすことができ、かなり大きいです。バッチウィンドウは、トラフィックが少ない場合の効率を向上させるのに役立ちます。バッチ全体が形成される前にメッセージを処理できます。また、Lambda の 6 メガバイトペイロード制限内に留まる必要があります。

Thumbnail 1150

ストリームとキューのスケーリング戦略:上限問題と下限問題

バッチが作成されると、同期コントロールプレーン経由で関数を呼び出すため、異なるアーキテクチャに対して異なるスケーリングを考慮する必要があります。ストリーミングは実は上限の問題です。一般的にストリームからデータを可能な限り高速に処理したいため、考慮する必要がある最大スループットがあります。Kinesis と Kafka ストリームでは、受信データに追いつき、ラグを最小化するために、可能な限り高いレートでメッセージを消費して処理しようとしています。課題は、取り込みレートに対応するか、それを超える可能性のあるレートまでスケールアップすることです。

キューイングは異なります。これは実は下限の問題です。データを効率的に処理したいのですが、API やデータベースなどのダウンストリームサービスを圧倒しないようにしたいのです。SQS では、キューはバッファまたはショックアブソーバーとして機能し、ダウンストリームリソースを保護するためにキューをドレインするレートを制御します。課題は、ダウンストリームリソースを尊重する最小限の安全な処理レートを維持することです。これらは 2 つの異なるアーキテクチャスタイルであり、SQS がこれを行うために、フロー制御、Lambda がメッセージを消費するレートを管理したいのです。

Event Source Mapping の最大同時実行数を設定することで、Lambda が Lambda に送信しようとする同時実行の数を制御して、ダウンストリームサービスに過負荷をかけないようにできます。これが非常に重要なバッファリング制御です。Reserved concurrency は Lambda 関数の別の設定で、関数から容量を予約するために設定するもので、利用可能なアカウント同時実行数内でスケールアップできることを保証します。実際には、バッファとフローを維持・管理するために最大同時実行数を使用したいのですが、両方を一緒に使用することもできます。その場合は、reserved concurrency が最大同時実行数より高いことを確認して、スロットリングを防ぐようにしてください。

Thumbnail 1210

Thumbnail 1260

Thumbnail 1280

Thumbnail 1290

ストリームの場合、ストリームがスケールする方法をスケールアップする必要があります。これは自動的に発生し、Event Source Mapping がこれを把握する必要があります。Kinesis はシャードを追加することでスケールし、Kafka はパーティションを追加することでスケールします。 Lambda は自動的にこれをすべて把握して、コンシューマーポーラーをスケールアップして受信スループットに合わせ、各パーティションまたはシャード内のメッセージ順序を維持します。シャードまたはパーティションが増えるほど、処理が増えます。顧客は Kinesis でさらに多くの処理を望んでいたので、parallelization factor と呼ばれるものを考え出しました。 デフォルトでは個別のシャードあたり 1 つの関数ですが、最大 10 までスケールアップして、大規模な Lambda スループットを実現しながら、順序を維持できます。 これは高スループットストリーム処理のために Lambda 用に特別に構築されたものです。

エラーハンドリングの詳細:partial batch responseとbisecting batches

エラーハンドリングについては、ストリームとキューのセマンティクスが異なります。ストリームは順序を保持する必要がありますが、poison pill メッセージをどうするかを決める必要があります。処理を停止すべきでしょうか?ログデータや銀行取引のような場合は、絶対に、ストリーム処理を停止して問題を修正してから続行したいです。しかし、頻繁なセンサーデータやハイヤー車の GPS データのような場合は、数秒後に次の結果が来るので、いくつかのデータ損失を処理できて、すぐに続行できます。このシナリオでは、メッセージ処理を続行したいので、ストリームを停止したくありません。キューの場合、一般的には失敗したメッセージで停止したくなく、残りを処理し続けたいです。

Thumbnail 1340

より多くの制御を提供するために、2 つのバッチエラーハンドリングオプションがあります。 Partial batch response は、失敗したレコードを知っていて、成功したレスポンスを返してから、Event Source Mapping にどのメッセージが失敗したかを伝えることができるときです。それは再試行され、処理が続行されます。AWS Power Tools for AWS Lambda は多くの言語向けの優れたユーティリティで、このを支援するバッチ処理ユーティリティがあります。ストリームの場合、bisecting batches は、エラーを処理しないときに非常に役立つことができ、キューでは通常考慮する必要があるものではありません。

Thumbnail 1380

Thumbnail 1390

Thumbnail 1400

Thumbnail 1410

この例では、メッセージ 3 が悪いものですが、実際にはまだそれを知ることはできません。バッチを処理して失敗したレスポンスを取得します。同じバッチを無限に再試行する代わりに、Event Source Mapping はバッチを分割して最初の半分を試して、順序を保持し、より多くのメッセージを処理できるようにします。 分割されたバッチは欠陥のあるメッセージを持っているため再び失敗するので、Event Source Mapping は再び分割して新しいバッチを処理しようとします。 今回は、最初のバッチが正常に処理され、poison メッセージを含む単一のバッチが再び失敗します。その後、元の最初のバッチの 2 番目の半分を続行して、成功したメッセージの順序を保持し、 バッチ全体を拒否して欠陥のあるメッセージを処理する必要なく、できるだけ多くのメッセージを完全に処理できます。 これはエラーハンドリングを制御できる本当に効率的な方法です。

関数のエラーを処理する必要があるので、Lambda の on-failure destinations を設定して関数の invoke の問題に対応する必要があります。SQS にはなぜ実は 2 つの異なるエラーハンドリングパスがあるのかと疑問に思うかもしれません。SQS Dead Letter Queue はポーリングまたは処理中に繰り返し失敗するメッセージをキャプチャします。一方、on-failure destinations は Lambda 関数の invoke エラーをキャプチャします。これはネットワークの問題、スロットリング、あるいは関数を削除してしまった場合や関数のパーミッション問題など、様々なことが考えられます。どちらも異なる目的を果たしており、包括的なエラーハンドリングのために一緒に使用することができます。

Thumbnail 1450

これで Lambda の基礎を一通り説明しました。 sync、async、workers、pollers、そして Event Source Mapping のすべての機能をカバーしました。単なる設定オプションに見えるかもしれませんが、Lambda は内部で膨大な作業を行っており、ポーリング、プッシング、プルを行い、できるだけ効率的に、ストリームやキューのさまざまなアーキテクチャからメッセージを取得しています。では、Rajesh にバトンを渡します。彼は pollers についてさらに深く掘り下げ、Lambda を構築する際に学んだいくつかの教訓について話します。

Thumbnail 1500

Lambdaをキューイングサービスとして理解する:mental modelsとキューイング理論

ありがとうございます、Julian。

このセグメントの概要をお話しします。このパートを 2 つのセクションに分けました。まず、Lambda を設計する際に使用した mental models と、それに伴う operational complexity について説明します。この complexity は実は Lambda の設計に組み込まれた多くのイノベーションを促進しました。まず mental model から始めましょう。

Thumbnail 1530

Thumbnail 1540

前回ここに立ったとき、Lambda はある意味では storage service であり、Elastic Block Store のようなサービスを構築する際の多くの学習を適用していることを皆さんに納得させました。 今年は、Lambda は同時に queuing service でもあり、queuing services と queueing theory の概念を構築する際の多くの学習をどのように適用してきたかについて、皆さんを納得させたいと思います。 compute を queuing service として考えるのは少し直感的ではないかもしれませんが、私が実際に何を意味しているのかを説明させていただき、その後、Lambda サービスを設計する際の思考プロセスについて説明します。

Thumbnail 1560

キューサービスとは何かというところから始めましょう。 私は2013年にAmazonに入社したので、もう10年以上になります。最初にやったことはキュープロセッサーを構築することでした。基本的に、キュープロセッサーはメッセージバッファを持っていて、そこにたくさんのイベントとメッセージが保存されます。ワーカーがキューからプルして、キューを処理する方法を書きます。そして、メッセージを処理してそれに対して何かをするビジネスロジックがあります。

そのために、私は上流と下流をモデル化するためにたくさんのシミュレーションを構築して、障害ポイントが何かを特定し、レジリエントなサービスを構築するために何が必要かを決定する必要がありました。当時はLambdaが存在しなかったので、私は多くの重い作業をしなければならず、キュープロセッサーをゼロから自分で構築しました。2018年から2019年頃にLambdaに参加したとき、同じパターンがいたるところに現れていることに気づきました。私たちが構築した多くの機能と、Julianが今歩いてくれた多くのことは、2013年以来、10年以上にわたって私が行ってきた同じ分析に触発されています。

Thumbnail 1640

キューイング理論を素早く説明しましょう。これはこのセッションの残りの部分の基礎を設定します。キューイング理論は何も難しいものではありませんが、基本的には待ち行列の研究です。物がどのように到着し、待ち、処理され、そして去るかです。 このダイアグラムは、あなたがおそらく何らかのバージョンを見たことがあるすべてのキューイングシステムの基本的な形を示しています。イベントはある速度で到着し、その速度は決して一定ではありません。顧客によっては急増することもあれば、ゆっくりで限定的な到着パターンを持つ人もいます。

右側には、ワーカーがいます。彼らの速度はサービスレートで、到着と同じように、これも一定ではありません。時にはあなたのデータまたは依存関係が10ミリ秒以上かかることもあれば、100ミリ秒かかることもあります。ですから、これも固定されていません。その周りのさまざまなことをモデル化する必要があります。次に、ここで見えるのはバッファ、バースト、そして変動性です。分散とは、キューイングサービスを構築しているときに、スパイクがある場合、スレッドの問題がある場合、またはあなたのサービスがワークロードを処理できない場合、いくつかの問題が発生する可能性があります。システムの分散はバックログの蓄積につながる可能性があります。

特定のセクションに入るときに、もっと詳しく説明します。Lambdaがキューイング理論から学んだ教訓と、それをどのように適用したかを見てみましょう。最初に話したいのは、キューイング理論から学んだ教訓です。バッファは分散を平滑化するということです。到着は決して均一ではありません。バースト的で、スパイクがあり、予測不可能です。ですから、私たちはそれらをすぐに処理しようとはしません。それは不可能で、費用がかかります。ピークのためにプロビジョニングすることはいつでもできますが、そうするとはるかに多くの費用を支払うことになります。

Thumbnail 1770

Thumbnail 1790

2番目のレッスンは、ワークロードタイプに応じて労働者を特化させるということです。これがLambdaの設計にどのようにつながったか、具体的にはpollerとLambda実行環境をどのように分離したか、そして異なるpollerの性質が実行環境とどのように異なるかについて説明します。 3番目のレッスンは、分散を制御して不安定性を防ぐということです。平均到着率が平均サービス率より低い場合でも、分散は一時的な不安定性を引き起こす可能性があり、その分散がシステムを破壊するのです。4番目のレッスンは、共有リソースを集中管理を通じて調整するということです。

キューイング理論のモデルでは、ワーク・コンサベーション(仕事保存則)とは、仕事があり、サーバーがあれば、仕事とサーバーが決してアイドル状態になってはいけないということを意味します。しかし理論は常にグローバルな状態が存在することを前提としています。分散システムでは、それを構築する必要があります。

Thumbnail 1810

Thumbnail 1820

バッファによる分散の平滑化:injection tierとshuffle sharding

Lambdaがそれをどのように構築しているかについて説明します。まずはバッファが分散を平滑化することから始めましょう。先ほど説明したように、トラフィックパターンは常に変動しています。定常状態のトラフィックを持つ顧客もいれば、ランダムなトラフィックを持つ顧客もいます。 10倍から50倍にスパイクする顧客もいて、こうした状況に対応するシステムを用意する必要があります。ゆっくりと立ち上がって急に本格的な顧客になる顧客もいれば、マイクロバースト的なパターンを持つ顧客もいます。こうしたすべてに対応できるシステムを構築する必要があります。

Thumbnail 1850

基本的に構築しているのはインジェクションティアで、データソース、インジェクションティア、ストレージがあります。そのシステムの特徴は、非常に変動するマルチテナントトラフィックを受け入れることができるということです。トラフィックを受け取った後は、リクエストを正規化して検証する必要があり、メッセージまたはイベントを適切なキューに書き込む必要があります。それを永続化して確認応答する必要があります。確認応答する前に、それが永続的に保存されていることを確認する必要があります。また、アドミッション制御とバックプレッシャーを適用して自分たちを保護する必要があります。

Thumbnail 1900

Thumbnail 1920

Thumbnail 1930

Thumbnail 1940

このようなものを構築する必要がある場合、このインジェクションティアを構築するのに役立つ耐久性のあるアーキテクチャが必要です。Lambdaが行ったことはこれです。変動するトラフィックパターンを持つ顧客のレイヤーがあります。 ロードバランサーがあり、初めてinvokeするたびに—Julianが話していたフロントエンドとinvokeとイベントinvokeフロントエンドについて—これはイベントinvokeフロントエンドであるフロントエンドです。分散ロード、ヘルスチェックなど、いろいろなものがあります。それが上位フロントエンドに到達し、これはリバースプロキシと呼ばれるもので、複数のホストを持つマルチAGサービスです。 同時に、CPUやメモリのスパイクがある場合、これらのものが自動的にオートスケールできるようにオートスケーリングルールも備えています。

それ以外にも、injection tier が完了したら、メッセージを処理する必要があります。バッファが必要で、このバッファは耐久性を持つ必要があります。つまり、顧客に「メッセージを受け付けました」と伝えた後は、メッセージを失うことはできません。でも結局のところ、やりたいことはメッセージを queue に入れて、そこで処理することです。単にメッセージをインデックスするだけではなく、処理もしたいんです。では、その間にあるこのルーティング層をどうやって構築するのか。説明していきましょう。

Thumbnail 1980

Thumbnail 2010

まず、invoke request がイベントメッセージと一緒にやってきます。その後、一連の validator を通ります。authentication をして、payload を検証して、その関数がアクティブな関数かどうかを確認します。この validation layer を通過したら、そのメッセージを取り込める queue を見つける必要があります。先ほど話したように、私たちは何百万もの顧客を抱えています。何百万もの queue を持つことはできないので、queue の中に何らかのマルチテナンシーが必要です。そのために、最も一般的なパターンは queue のセットを構築して、それを hash ring にハッシュすることです。

Thumbnail 2020

Thumbnail 2030

Thumbnail 2050

ですから、メッセージがすべての validation ステージを通過したら、queue finder を見つけます。hash ring のパーティションにハッシュします。パーティションを見つけたら、そのメッセージを取得して、queue を取得して、hash ring で見つけた queue にそれを書き込みます。でも、ご存知の通り、単一のパーティションでも本当にホットになることがあります。なぜなら何百万もの顧客が同じパーティションに重なることができるからです。ですから、私たちが使う技術は shuffle sharding と呼ばれるものです。

Shuffle sharding は基本的には、顧客を単一の queue にハッシュするのではなく、今は顧客を複数の queue にハッシュできるということです。私たちがやることは、account ID に基づいた単一の顧客があり、私たちが持っているいくつかのヒューリスティックがあります。その顧客を 2 つの hash range にハッシュします。それらの hash range に基づいて queue を選んで、その時点で queue depth が最も低い queue を見つけます。そしてそこにメッセージを挿入します。これが shuffle sharding と呼ばれるものです。そしてこれは私たちにとって本当に素晴らしい効果をもたらします。このハードパーティショニングを取り除くのに役立ちます。そしてそれは消えてなくなります。

Thumbnail 2090

これに加えて、express lane と呼ばれるものもあります。顧客がノイジーになることがわかっていて、もっと多くのトラフィックを持つようになったら、すでに彼らのためにサイドライン queue を作成します。彼らがトラフィックを吸収するのを許可して、私たちもトラフィックを吸収します。そして、彼らが完了したら、その queue を回収して、彼らを動的な共有 queue に戻すことができます。

Thumbnail 2120

Thumbnail 2150

これまで私が話してきたのは Lambda によって管理されているキューです。これらはイベント invoke キューです。Lambda を取り込むか、または Lambda を非同期で invoke するたびに、そこに行くわけです。バッファのもう一つの部分があって、それが分散を平滑化するのに役立ちます。それが customer-managed 側です。これは event source mapping 側のもので、キューとストリームは customer の境界に留まります。私たちがやることは処理を提供するだけで、この時点以降、polling は customer-managed キューと service-managed キューの両方で同じになります。

Thumbnail 2190

ポーラーワーカーの特化と内部構造:pollingと executionの分離

私たちが適用した 2 番目の教訓は、ワークロードタイプに合わせて worker を特化させることです。Lambda がこの教訓をどのように適用しているかを見てみましょう。それを理解するために、まず worker が何であるかを理解する必要があります。Worker はキューを見て、メッセージやイベントを見つけるたびにそれを処理するプロセッサです。キューイング理論は、異なるサービスレートを持つ異種サーバーは最高のパフォーマンスのために異なる方法で処理される必要があることを示しました。私たちはこの原則を適用して、polling ワークロードを execution ワークロードから分離しました。

Polling と execution ワークロードは本質的に異なります。Polling は継続的で、ステートフルで、接続が多いです。一方、execution はバースト的で、ステートレスで、短命です。実は 2 つのタイプの worker があります。1 つは Lambda によって管理される polling worker で、もう 1 つは顧客が書く Lambda function worker です。Julian は execution worker 側について話しましたが、今日は私は polling worker 側を深く掘り下げたいと思います。さあ、poller を構成する主要なコンポーネントを探索してみましょう。

Thumbnail 2270

私は今日私たちが行っていることを分割して見ました。これは私たちが新しいことを学び進化するにつれて常に変わるので、これが今日の状態です。私たちは work acquirer コンポーネントを持っていて、それはアクティブな work があるかどうかを見ます。それは assignment manager store を見て、work が利用可能な SQS キューまたはストリームがあるかどうかをチェックします。それは work を拾い上げて event source configuration を開始します。ストリームの場合、Kinesis には KCL を使用し、Kafka には Kafka Consumer を使用し、SQS には SDK SQS client を使用します。

Thumbnail 2300

設定されると、connector を設定し、レコードをプルし、それらをメモリ内キュー buffer に書き込むことで work を開始します。Julian が話した多くの機能はこのメモリ内キューで実行されていて、そこで私たちは view を作成して event source に圧力をかけないようにします。私たちはこの view を構築して、ここで batching、filtering、そしてそれがサポートしている場合は schema validation を行います。その後、invoke orchestrator layer があって、batches の準備ができたら、batching はさまざまな方法で行うことができます。

Thumbnail 2330

Julian がバッチ処理の3つの方法について話しました。条件を定義したバッチの準備ができたら、invoke orchestrator を実行します。これは Lambda を同期的に呼び出してバッチを処理し、レコードを扱います。レコードが失敗した場合、error handling component に渡され、設定に基づいて Dead Letter Queue に書き込むか、リトライします。

Thumbnail 2350

これに加えて、メッセージが正常に処理された場合、checkpoint manager があり、checkpoint を進めるのを担当します。これにより、メッセージを削除する前に、システムがそれを処理したことを確認し、メッセージを失わないようにします。さらに、auto scaler があります。これはシステムの現在の状態を見て、auto scale が必要かどうかを判断します。システムが高負荷で動作している場合、現在の worker の信号を見て、さらにスケールする必要があるかどうかを判断します。スケーリングは2つの部分に分かれています。1つは vertical scaling です。ホストに容量がある場合、メモリと CPU のフットプリントを見て、より多くのトラフィックを吸収するために vertical scale できるかどうかを判断します。できない場合、global coordinator に信号を送って、新しい worker を起動してもらい、その作業を引き継いでもらいます。

Thumbnail 2400

それに加えて、システムが高負荷で動作している場合、worker の一部から負荷を削減したいと考えています。load shedding component があり、高負荷で動作している場合に作業を削除します。顧客に影響を与えたくないのです。さらに、overall health checker があり、システムのさまざまな部分を常に監視して、そのデータを異なるコンポーネントで利用できるようにしています。auto scaler はそのデータを見て、適切なスケーリング アクションを判断します。

Thumbnail 2420

auto scaler は「高負荷で動作しています。auto scale が必要です」と言い、load shedder は「高負荷で動作しています。メモリ内の何かを削減する必要があります」と言います。key buffers も同様にそのビューを構築して、異なるコンポーネントで利用できるようにします。

Thumbnail 2460

ここまで、worker とその worker のビュー、そして worker を構築するために必要なことについて説明してきました。しかし、すべての worker が同じではありません。より安全な worker もあれば、VPC の境界を越える必要がある worker もあります。私たちは原点に戻り、poller の構造を詳しく見て、そこで何ができるかを検討しました。覚えていれば、これは私たちが今見たばかりのものです。そして、文字通り目を細めて見ると、worker を水平にスライスすると、基本的にはすべてのシステム タッチポイントがある下部が得られます。さらに目を細めて見ると、下半分はさらに3つの部分に分割でき、3つの異なるタッチポイントがあります。

Thumbnail 2480

Thumbnail 2510

このワーカーの最初の部分はイベントソースを扱っており、別の部分は内部サービス通信を扱っており、3番目の部分は Lambda のプライベート呼び出しを扱っています。この3つはすべて異なるセキュリティ境界で動作しています。そこで私たちは、ワーカーのこの部分 を取り出して、1つの特定のセキュリティ境界セットでラップすることにしました。次にワーカーのこちら側を取り出して、1つの特定のセキュリティ境界に入れます。最後の部分は Lambda 実行環境で、これは非常にプライベートな VPC 関数を持つことができ、それを呼び出すことができます。

Lambda ワーカーと Lambda ポーラーに適用したのと同じ原則を Lambda ワーカー自体の中に適用しました。そこにも3つの部分があると言いました。これで、独立していて、ワーカーの残りの部分とは何の関係もない部分を自動的にスケールできるようになります。Kafka を使ったことがあれば、コンシューマーのリバランシング、デスループ、静的メンバーシップ、協調的リバランスといった問題を知っているでしょう。これら2つのものを分離して、Lambda 関数を呼び出すことでスループットを実際に増やしながら、コンシューマーを別々に扱うことができたらどうでしょうか。

Thumbnail 2610

私たちはそれらをセキュリティ境界でラップしました。1つはイベントソースを扱い、1つは内部サービスを扱い、1つは関数実行を扱っています。実行環境とポーラーワーカー間で VPC ピアリングを持つことができます。顧客のイベントソースとのクロスアカウント接続を持つことができ、その後、内部サービスはそれが動作している同じ境界内にあります。これでポーラーワーカーを理解したので、次のキューイング理論からの教訓を見てみましょう。これはこれらのワーカーの生産動作について教えてくれます。教訓3番は、不安定性を防ぐために分散を制御することです。

Thumbnail 2620

分散制御とグローバルステート管理:安定性を保つための4つの防御層

分散はシステムをスケールさせます。これをもっと詳しく見てみましょう。ここで見ているのは、高レイテンシーを持つワークロードです。 それが通過して、システムのより多くのリソースを吸収し始めると、ワークロード B とワークロード C にはリソースが残されていません。Little の法則は、処理レイテンシーが増加すると、到着率が一定であっても同時実行数がスパイクすることを示しています。この時点で、私たちはシステムに何らかの公平性を構築したいと考えています。

Thumbnail 2660

Thumbnail 2670

Thumbnail 2690

これに対処するために、私たちはシステムの安定性を維持するために異なるメカニズムを備えた複数層の防御制御を構築しました。最初の層 は分散削減です。キューにメッセージが入ってくる場合、例えばそれらをバッチ処理してみてください。2番目は容量制御です。 同時実行数をキャップするなど、ハードリミットを設定して、単一の関数がフリートを乗っ取ることができないようにします。3番目は入場制御です。満杯の時にリクエストをスロットルします。ちょうど店が容量で扉をロックするようなものです。そして4番目はバックプレッシャーです。 オーバーロード時に反応します。苦しむ必要はありません。ポーリングを遅くするか、低優先度を削減します。これが私たちが構築した4つの層です。これらの層の一部は顧客が設定でき、一部はサービスチームが所有・管理しています。

Thumbnail 2710

では、ワーカーの到着パターンを見て、サービスを実行し続けるものが何かを確認したので、もう一つお話しします。 共有リソースを一元化されたコンポーネントを通じて調整する必要があります。待ち行列理論は、常に決定論的またはスタブルなパターンが存在することを教えてくれます。1 つのワーカーが失敗すると、別のワーカーがその仕事を引き継ぎます。分散システムではそのような利点がないので、このグローバルステートを自分たちで構築する必要があります。

Thumbnail 2750

Thumbnail 2760

Thumbnail 2770

グローバルステートを構築するには 4 つの特性が必要です。1 つ目は work conserving scheduling です。仕事があり、アイドル状態のワーカーがいれば、それらをペアリングするようにします。2 つ目は conflict-free assignment です。 1 つのワーカーが何かに取り組んでいる場合、それを他の誰かに与えないようにして、互いに干渉しないようにします。3 つ目は capability-aware dispatch です。 特定の仕事を処理できる人に仕事を割り当てています。例えば、queuing の仕事や SQS の仕事を stream poller に与えたくありません。4 つ目は failure detection and recovery です。 誰かが仕事を要求したのに、実際にはそれに取り組んでいない場合、その人がそのタスクで実際に進捗していないことを知る方法が必要です。

Thumbnail 2820

Thumbnail 2840

ここで私たちが行うことです。control plane があります。Event Source Mapping またはイベント invoke config を作成するたびに、config store に送られます。その後、ESM Lifecycle Manager があり、その役割はワーカーを起動してシステムで利用可能にすることです。assignment を作成し、Assignment Manager という別のサービスがあり、assignment の詳細を config に保存します。ここでグローバルステートを構築します。 poller worker が仕事を取得し、その後、heartbeat とメタデータを assignment config store に送り返します。これはグローバルステートの信頼できる情報源です。同時に、anti-entropy job が、仕事を要求している人が実際にそれを持っていて、それに対して行動しているかどうかを確認しています。もし行動していなければ、人間のオペレーターを巻き込んで、彼らが対応できるようにします。 これは、何度も見てきた Lambda poller worker architecture につながり、これはこのような architecture を構築する舞台裏の部分でした。

Thumbnail 2870

Thumbnail 2880

では、簡単に要点をまとめます。まず、ingestion tier と buffer が必要で、smoothing mechanism が必要です。 次に、ワーカーがあり、異なるタイプがあります。異なる特性を持つものもあるので、それらを分離するようにしてください。3 つ目は、stability controls が重要です。なぜなら、variance はシステムを殺すので、stability を維持し、システムに stability を構築するための controls が必要です。4 つ目は、 継続的に進捗するために必要なグローバルステートが必要です。これはフリートを健全に保つために必要なものです。これは基本的に、Lambda が待ち行列理論からどのような教訓を適用しているかについて、ほぼすべてを知る必要があることを示しています。

Thumbnail 2920

依存関係障害への対処:DynamoDB障害時の復旧戦略とtoken bucket

ここまでで、Lambda が多くの点で重要なサービスである理由についてのアイデアを与えられたことを願っています。Lambda には数百万の顧客がいて、これらのシステムを通じて数兆のイベントが流れています。では、すべてを実行し続けるにはどうすればよいでしょうか。すべてを事前に見ることは本当に難しいです。 賢い人が言ったように、すべてはいつも失敗します。では、queue とワーカーを持ったら、それが失敗した場合はどうしますか?このトークの 2 番目の部分は、このような queue サービスを運用する際に、運用の複雑さにどのように対処するかについてです。明らかに、それを確実に実行するために、異なる次元で革新する必要がありました。

Thumbnail 2940

Thumbnail 2960

Thumbnail 2980

キューイング理論では、障害は独立しており、システムは線形にスケールし、飽和は段階的に起こると言われています。これはエンジニアとして見たい世界の姿です。しかし、実際のマルチテナント分散システムを運用する瞬間、すべてがより複雑になることに気づきます。現実には相関のある障害があります。1つのサービスが失敗すると、他のサービスも失敗し始めるのです。システムは線形にスケールしません。どこかでリトライループが起こっていると、より速く加速してから急落します。シミュレーションで何かをモデル化するようなものですが、常にその1つの条件を見落としてしまい、それが急落につながるのです。急落と言うとき、私が言っているのはこの急落です。2013年に遡ると、Lambda は障害を経験しました。フロントエンドのレイテンシバグが再び浮上し、それがエラー率の増加につながったのです。これが私が言っているケースです。テスト中にすべてのケースをモデル化するのは本当に難しいのです。キューイングサービスも同じで、すべてをモデル化することはできませんが、できることはこれから回復できるシステムを構築することです。

Thumbnail 3030

Thumbnail 3040

これから、Lambda を運用するために適用している3つの主要なレジリエンシパターンを紹介します。1つ目は依存関係の障害です。サービスを構築するために依存している中核的な依存関係がなくなったときに、私たちは何をするのか。2つ目はスケール反転です。より大きなフリートがより小さなフリートをハンマーで叩き始めたらどうするのか。そして3つ目は可用性ゾーンの障害です。これは Lambda サービスを運用する問題に非常に特殊でユニークであり、それをどのように解決するのか、そして Lambda サービスの運用にとってどのように異なるのかについてです。では、依存関係の障害から始めましょう。

2025年10月の最近の障害を見てみましょう。これはみんなの記憶にまだ新しいと思います。これは DynamoDB の最近の障害で、多くのサービスが影響を受け、Lambda もその1つでした。私たちはデータプレーンを運用するためにさまざまなことに DynamoDB を使用しています。この問題の根本原因は、DynamoDB DNS 管理における潜在的な競合状態でした。ここで何が起こったのかに興味がある場合は、参加すべきトークがあると思います。

Thumbnail 3070

キューサービスオペレーターの観点から考えると、これはさらに興味深くなります。障害が起こると、メッセージが蓄積され始めます。依存関係が回復したとき、蓄積したバックログと新しく入ってくるトラフィックの両方に対処する必要があります。追いつくために、私たちは2倍の努力をしなければならないですよね。メッセージが蓄積されており、新しいメッセージセットも引き続き入ってきています。では、私たちは何をするのか。

Thumbnail 3100

2番目に見られるのは公平性のジレンマです。うるさい顧客はうるさい顧客ですが、良い顧客がうるさい顧客になってしまいます。なぜなら、私たちが区別する方法がないからです。そのうるさい顧客は数十万または数百万のメッセージを持っています。良い顧客、良い市民も、今では数百万のメッセージを持っています。なぜなら、私たちが停止していて蓄積が始まったからです。私たちはどのようにしてそれらを区別するのか。

Thumbnail 3130

障害が発生するたびに、できるだけ早くバックログをクリアしたいのですが、同時にサービスをハンマーダウンして復旧を遅延させたくもありません。バランスを取ることが大事なんです。障害が発生するたびに、私たちにとっていつもバランスを取ることになります。もし私たちが過度に低い処理をしていれば、復旧を遅延させることになります。メッセージがキューに長く留まります。しかし、もし過度に高い処理をしていれば、サービスをオーバーフローさせてしまう可能性があります。なぜなら、プロビジョニングされた容量には限界があり、実行できる作業量にも限界があるからです。バッファを持つことで多くの容量をアンロックできますが、蓄積されたバックログの作業量と、それにかかる時間には依然として限界があります。

Thumbnail 3170

私たちが行っていることがいくつかあり、その戦略のいくつかについて話します。これは決して網羅的ではありませんが、興味深いことがいくつかあり、共有したいと思います。私たちが構築する1つのことは、token bucketを使用した段階的なランプアップです。そのtoken bucketレートはシステムのビューを通じて制御されます。CPUとメモリの圧力がどのように見えているか、同期トラフィックがどのように見えているか、そして異なる呼び出しモードがどのように見えているかを確認します。その後、非同期フリートから生成しているTPSのレートを構築してチューニングし、段階的に復旧するので、オーバーフローさせません。

Thumbnail 3200

また、ハンドシェイクメカニズムもあります。障害が発生するたびに、自動ハンドシェイクメカニズムを構築し、ノブをダイアルアップし続け、サービスが復旧している速度を確認します。また、可能な限りFIFOキューをLIFOキューに変換しようとします。バックログがあり、最初のメッセージを見始めることに問題がなければ、顧客を作成して共有し始めます。古いメッセージバックログはゆっくりドレインできますが、新しいメッセージは非常に高速に復旧を見始めます。

Thumbnail 3240

また、関数、バージョン、エイリアスがある場合、キューをシャーディングすることもあります。キューを関数、関数バージョン、そしてエイリアスにシャーディングしようとするので、復旧がはるかに高速に見え始めます。そして、私が言っていたように、良い顧客がノイジーになることもあり、ノイジーな顧客はノイジーな顧客です。では、これらのことをどのように分離するのでしょうか?数百万のメッセージを持ち、関数の同時実行性を1に設定している顧客を考えてください。私たちはそれにどのように対処するのでしょうか?その顧客がキューにいるとき、その顧客をメインキューから分離しようとするので、独自の処理を行うことができます。なぜなら、それはノイジーな顧客ではなく、障害がそれをノイジーにしたからです。

ですから、別の種類のtoken bucketメカニズムがあり、関数の同時実行性またはダウンストリーム構成に基づいてバッファのセットを割り当てるか、トークンを与えます。その1つの特定のtoken bucketをその顧客に割り当て、その顧客を分離しようとするので、共有キューにいても、処理容量を定義するためにそのtoken bucketを使用しています。

Thumbnail 3300

Scale inversionとstatic stability:hot pathとcold pathの分離

これは依存関係がダウンした時に何が起こるかについてでした。私たちが対処しなければならない別の種類の障害があります。それは scale inversion です。これは大きなフリートが小さなフリートに依存していて、小さなフリートがダウンするか、大きなフリートが異常な動作をして、その結果小さなフリートがダウンするという状況です。 これは私たちが対処しなければならない別の障害です。

Thumbnail 3310

Thumbnail 3330

Thumbnail 3340

これは大量の接続アクティビティの急増が retry storm を引き起こし、それが小さなフリートをダウンさせた別の障害です。同時に control plane が復旧中だったため、復旧が遅れました。私たちの場合、leasing services がダウンした時のグローバルな状態を考えると 、これが1分間でもダウンしていると、メッセージが蓄積され、新しいメッセージが引き続き届きます。では、どのようにして復旧するのでしょうか?これは私たちにとって問題になります。

Thumbnail 3360

Thumbnail 3370

これに対処するために、私たちは static stability を構築しました。static stability が意味するのは、ワークロードが依存関係の障害があっても、リアクティブな変更を必要とせずに正しい動作を続ける能力です。この static stability を達成するための最も重要なステップは、hot path と cold path を分離することです。 data plane は処理中に control plane を呼び出す必要がありません。control plane は lifecycle management だけを行い、data plane は定常状態で動作するのに十分です。

Thumbnail 3380

Thumbnail 3400

1つの戦略は configuration を push down することです。Lambda の場合、Lambda 関数の configuration を pollers に push down して、pollers がそのワークを要求するのではなく対応しています。 2番目の戦略は L1 と L2 キャッシュで状態をレプリケートすることです。また、ディスクをバックアップとして使用して、障害が発生した場合にワークを消費して再開または再開できるようにしています。 その後、柔軟なタイムアウトで stickiness を構築して、false positive と false negative を減らすようにしています。障害があり、それが一時的な障害だと判断した場合、ワークを離れたり、ワークをリリースしたりしません。Lease-driven independence は、パーティションが独自の専用ワーカーまたは専用ワーカーのチャーターを持つことを意味します。circuit breaker は、エラーがフリート全体に広がっているのが見える場合、それは特定の coast に共通点がないかもしれませんが、システム全体に広がっているということです。

Thumbnail 3430

Thumbnail 3440

Thumbnail 3450

Thumbnail 3460

Thumbnail 3470

Thumbnail 3480

Thumbnail 3500

Availability Zone障害への対応:detection、evacuation、observation

3番目の種類の障害は availability zone の障害です。 これが意味するのは、houses は非常に奇妙だということです。どこかで雷嵐が発生して、availability zone の障害につながる可能性があります。 では、AZ の障害がある場合、私たちにはどうなるのでしょうか?サンプルワークロードに3つの AZ があり、1つの AZ がダウンしている場合、移行する必要があります。 なぜなら、私たちは数百万のキューを処理していて、数百万のワーカーがあり、それらの間に関係があるからです。ですから、その AZ からアクティブなワークロードを別の AZ に移行する必要があります。 その移行には私たちの側で何らかの努力が必要です。では、私たちは何をするのでしょうか?AZ buffer があります。 これは非常に標準的です。処理能力があり、ワークを移動する必要がある場合、ワークを受け入れることができるようにバッファが必要です。2番目は私たちにとっての別のバランス行為です。 1つの AZ が影響を受けた場合、ワークを別の AZ に移動する必要がありますが、その障害で別の AZ にも影響を与えたくありません。ですから、私たちはハードウェアベースのメカニズムを持っていて、リアクティブにリースを期限切れにして、それが利用可能になり、共有されますが、より長い時間がかかります。

Thumbnail 3520

Lambda が復旧して、他のワーカーが不健全な AZ から健全な AZ に移動したとしましょう。そうなると、マイグレーションに時間がかかるわけです。そしてマイグレーションに時間をかけるなら、復旧の意味がないですよね。ですから、もっとプロアクティブに対応する必要があります。これを3つの部分に分けました:detection、 evacuation、そして observation です。まず AZ の障害があることを確認して、それについて非常に確信を持つ必要があります。その後、evacuation を行います。そして、ワークが新しい AZ に移行したことを継続的に監視します。復旧が起こったら、それを戻す必要があります。

Thumbnail 3550

重要なポイントは、アーキテクチャの観点からは、予測不可能な環境での堅牢性を設計し、permeability を減らすことです。Isolate することです。そして運用の観点からの重要なポイントは、multi-tenancy がデフォルトであること、 障害は常に起こるもので、severity が scale より優先されるということです。では Julian に引き継ぎます。

Thumbnail 3570

Thumbnail 3580

Thumbnail 3590

Lambdaの新機能と今後のロードマップ:managed instances、durable functions、そして未来

ありがとうございます。時間がそこまでないのですが、Lambda の新しい機能についてさっと説明したいと思います。Lambda managed instances について話しました。Lambda のすべての柔軟性と EC2 control plane も一緒に使えます。Lambda durable functions を使うと、お気に入りのプログラミング言語でマルチステップのワークフローを構築できます。 今日の午後、別のセッションがあります。 Tenant isolation は、テナント間の実行の分離を提供します。テナント ID を指定するだけです。これは、数十万個の関数を持つ可能性のある SaaS カスタマーにとって素晴らしいもので、彼らにとってはずっと良くなります。

Thumbnail 3600

Thumbnail 3630

Thumbnail 3640

Event Source Mapping の拡張には、すでにリリースされた schema registry、私が言及した SQS の provisioned mode、そして ESM metrics の大量が含まれており、これらはアプリケーションを構築するのに役立ちます。 もちろん、今後もやることはたくさんあります。observability を常にサポートし、新しい updates と新しい runtimes をできるだけ早く提供していきます。Lambda をすべてのワークロードに適用できるようにするために価格に取り組んでいます。AWS とサードパーティサービスとのより深い統合、そして常にお客様のフィードバックに耳を傾けています。何を構築してほしいか、ぜひ教えてください。Lambda roadmap は現在公開されており GitHub で利用可能ですので、Lambda の未来を一緒に定義するのを手伝ってください。 managed instances、durable functions、そして明日のサーバーレスの未来についての他のセッションがたくさんあります。昨日、ベストプラクティスについてのトークをしました。それが YouTube に上がっていれば、皆さんにとって役に立つと思います。

ここに、サーバーレスの学習を続けるためのサービスについての情報がたくさん書いてあるスライドがあります。しかし最も重要なのは、朝早くから来てくれて、本当にありがとうございました。Lambda についてもっと学ぶ時間を取ってくれて感謝します。Lambda の内部で起こっていることの膨大な量があることを理解していただけたと思います。そのおかげで、flow control や Rajesh と彼のチームが構築している他のすべてのことについて心配することなく、Lambda を素晴らしいサービスとして利用できるわけです。本当にありがとうございました。カンファレンスの残りの時間を楽しんでください。


※ こちらの記事は Amazon Bedrock を利用し、元動画の情報をできる限り維持しつつ自動で作成しています。

Discussion