Share
Embed
Transcript
55 slides
-
目次 1. 2. 3. 4. クラウドからサーバーレスへ サーバーレスから Virtual Actor へ
Virtual Actor から Durable Execution へ まとめ -
“Serverless Computing: One Step Forward, Two Steps Back,” CIDR 2019
- Scalable Data Processing: クラウドには巨大なストレージと計算能力があるので、 大規模データを効率的に処理 できる - Distributed Computing: クラウドには数百万規模のコアがあるので、多数の計算主 体を協調させる分散プログラム を書ける -
“Serverless Computing: One Step Forward, Two Steps Back,” CIDR 2019
- Scalable Data Processing: クラウドには巨大なストレージと計算能力があるので、 大規模データを効率的に処理 できる - Distributed Computing: クラウドには数百万規模のコアがあるので、多数の計算主 体を協調させる分散プログラム を書ける -
“Serverless Computing: One Step Forward, Two Steps Back,” CIDR 2019
- Scalable Data Processing: クラウドには巨大なストレージと計算能力があるので、 大規模データを効率的に処理 できる - Distributed Computing: クラウドには数百万規模のコアがあるので、多数の計算主 体を協調させる分散プログラム を書ける サーバーレスになった途端 できなくなったじゃねぇか オイ!!!!! -
筆者らはどのような実験を行ったのか 対象: 2018 年当時の AWS サービス(主に EC2 vs Lambda +
SQS + DynamoDB) 1. 機械学習モデルのトレーニング Lambda版は約21倍遅く、約7.3倍高価 2. バッチ処理 Lambda版は127倍遅かった 3. Lambda インスタンス間でのリーダー選出&合意形成 1回のリーダー選出に16.7秒かかり、1,000ノードを維持する場合のDynamoDB利用 料も非常に高い -
なぜこんなに差が出たのか? 理由① データを読み込む 理由② DynamoDB / S3 処理する Lambda Lambda
Instance A Lambda Instance B 永続化する DynamoDB / S3 データの転送が遅い Lambda インスタンス間で 低遅延で通信する方法がない -
現状の FaaS の限界 データの転送が遅い Why? データとプログラムが 物理的に分離されているから Lambda インスタンス間で 低遅延で通信する方法がない
Why? Lambda インスタンスに ID がないから -
現状の FaaS の限界 データの転送が遅い Why? データとプログラムが 物理的に分離されているから Lambda インスタンス間で 低遅延で通信する方法がない
Why? Lambda インスタンスに ID がないから -
データとプログラムを同じ場所に置く? function-shipping という考え方: 「関数にデータを持ってくるのではなく、データのある ところに関数を持ってきた方が効率がいい」 RDBMS がこの考え方だが、RDBMS とサーバーレスは相性が悪い(2018 年当時は特 に...)
この章では、サーバーレスがどのように「データとプログラムを同じ箇所に置く(Data Locality)を確保できるか?」について1つの解決策を見ていく -
1973年: Actor の誕生 Actor は「将来、数百・数千のマイクロプロセッサから構成され、個々にローカルメモリ を持ち、高性能通信ネットワークで通信を行う並列コンピュータ が登場するだろう」とい う予測に基づいて開発されたアーキテクチャ (… FaaS
かな?) Actor は: - 1 Actor : 1 プロセッサ(メモリも共有しない) 任意の数の「子 Actor」をスポーンできる 他の Actor や子 Actor に、(Actor の ID を使って)メッセージを送信できる 受け取ったメッセージに対して振る舞いを行う という特徴を持つ -
2014 年: Virtual Actor の誕生 Microsoft が出した Orleans というプロダクトのホワイトペーパーで、「Virtual Actor」とい
う概念が初出する。 Virtual Actor とは「任意の ID で区別される、独自のストレージを持つもの」 。さらに、 親子関係や起動/配置/終了の概念がなくなった (物理的なプロセスから、論理的な存 在へ) より具体的には: - Actor は子 Actor をスポーンする代わりに「任意の ID の Actor」にメッセージを送 れる。その ID の Actor がまだ存在しなければ自動で新規作成される 各 Actor は常にメモリ上に読み込まれているわけではなく、しばらくメッセージを受 け取らなければ状態を保存してスリープする -
2014 年: Virtual Actor の誕生 「独自のストレージを持つ」? なぜ? 「プログラムにデータを送るのではなく、データのあるところにプログラムを送る」ため Function-shipping ではなく、Command-shipping
として実現している 「ストレージを持った Actor」自体は Orleans 以前にも「Persisted Actor」など似たような 構成はあった。しかし Orleans はこの特徴を「自動スリープ」「自動ライフサイクル」の特 徴と組み合わせることで、FaaS のような「使った分だけリソースが消費される」特徴と 「データのあるところに関数がある(データ局所性)」を両立させる ことに成功した(具体 的には、コールドスタート時に関連するデータを全て持ってくる) -
2014 年: Virtual Actor の誕生 「Virtual Actor 最高じゃん! …え、Two Steps
Back 論文(2019)では言及なし?」 Orleans は実は Azure のサービスではなく、.NET のライブラリとして開発された。この時 点では全くサーバーレスの文脈では語られていなかった。 しかし6年後... -
2020年: Cloudflare Durable Objects 発表 - DO は、V8 Isolation(Cloudflare Workers
基盤)上で動く JavaScript クラス。1つの ID について常に高々 1 クラスインスタンスが保証されている。 - DO は「任意の ID の DO」にメッセージを送れる。その ID の DO がまだ存在しなけ れば自動で新規作成される - 他の DO とは共有しない、独自の SQLite(2020年発表時は KV Store)を持つ - - これは DO と同じマシン・SSDで読み書きされることが保証される(ネットワークを介しない) 数十秒アイドル状態で自動でメモリから排除される。 非常に Orleans の Virtual Actor と似た特徴を備えたサーバーレス商品 -
2023年: Restate Virtual Objects Apache Flink の開発者が Apache Flink の経験を元にした、Restate
が発表 - 任意のクラウドベンダのコード実行環境(Lambda や Cloud Run など)をVirtual Object 化できる コールドスタート時に、その Virtual Object の状態全体を送信するので、ホット状態 であればメモリ上の読み書きで済む -
なぜこんなに差が出たのか? 理由① データを読み込む 理由② DynamoDB / S3 処理する Lambda Lambda
Instance A Lambda Instance B 永続化する DynamoDB / S3 データの転送が遅い Lambda インスタンス間で 低遅延で通信する方法がない -
なぜこんなに差が出たのか? 理由① データを読み込む 理由② DynamoDB / S3 処理する Lambda Virtual
Actor 系列のサーバーレスが Function-shipping で解決 Lambda Instance A Lambda Instance B 永続化する DynamoDB / S3 データの転送が遅い Lambda インスタンス間で 低遅延で通信する方法がない -
なぜこんなに差が出たのか? 理由① データを読み込む 理由② DynamoDB / S3 処理する Lambda Virtual
Actor 系列のサーバーレスが Function-shipping で解決 Lambda Lambda Virtual Actor 系列のサーバーレスが Instance A Instance B Actor の ID で解決 永続化する DynamoDB / S3 データの転送が遅い Lambda インスタンス間で 低遅延で通信する方法がない -
しかしデメリットも... Cloudflare Durable Objects は > 一つのDOは一台のマシン上の単一スレッドで処理されるため、同じIDへ極端にアクセ スが集中すると、そのDOがボトルネックになります。DO はscale upではなくscale
outを 前提とし、単一オブジェクトの処理能力を超える場合にはシャーディングや集約構造が 必要 という説明をしている 「1つの Durable Object 自体のスケールには限界がある」 「リクエストはデータ間で分散するはず」という予測から、無限のスケーリングを諦めてい る -
Orleans では? 「口座間で資金を移動する」 という Python コード 「複数の口座 Actor を同時に ロックする」ことで実現している
with (yield context.lock([source, destination])): balance = yield context.call_entity(source, "get") if balance < amount: return False yield context.task_all([ context.call_entity(source, "withdraw", amount), context.call_entity(destination, "deposit", amount) ]) return True -
Orleans では? 「口座間で資金を移動する」 という Python コード 「複数の口座 Actor を同時に ロックする」ことで実現している
with (yield context.lock([source, destination])): balance = yield context.call_entity(source, "get") if balance < amount: return False yield context.task_all([ context.call_entity(source, "withdraw", amount), context.call_entity(destination, "deposit", amount) ]) でも Stripe はロックできない...! return True -
Saga パターンを(伝統的な)FaaS で実装しようとすると? Func1 -> SQS -> Func2 -> SQS
-> Func3 … 更に - 定期的なリトライ ローカルトランザクションでのコミット(永続化) ロールバック 上記を自分で実装する必要がある -
2017年: Azure Durable Functions AWS SWF に関わった Samar Abbas が「(JSONではなく)C#
の async/await でワークフ ローを書ける」ものを開発 -
例えばさっきの「送金」の例を Saga でやるなら: try { const transferId = ctx.uuid(); const
reserved = await ctx.run(() => sourceAccount.withdraw({ amount })); compensations.push(() => ctx.objectClient(account, source).cancel(transferId)); await ctx.run(() => destinationAccount.deposit({ transferId, amount })); compensations.push(() => destinationAccount.cancel(transferId)); } catch (e) { // 一時障害は Restateに再試行させ、 // 恒久的な失敗の場合だけ補償する。 if (e instanceof restate.TerminalError) { for (const compensate of compensations.reverse()) { await ctx.run(() => compensate()); } } } -
どういう仕組み? ctx.run(() -> { … }) に渡した関数は、下記の特徴を持つ: - 失敗時にリトライされる 成功時は、結果が保存され、2度実行されることはない
また、ctx.sleep(1 days)で 1 日待ったりすることもできる。もちろんその間プロセスが持続 することはない。それまでのコードの結果が全て保存されているので、そこまで「リプレ イ」することで、1日後にまた副作用を重複して起こさずにコードをそこまで進めることが できる。 -
“Durable” とは何なのか? 従来型のサーバーレスは「オートスケール」「Pay as-you-go」という特性のために、「短 命のコンピュート」の手法を推し進めた。 しかし、「短命のコンピュート」で「長寿命なアプリケーション」を実現するために、無理が 必要な場面もあった: - 計算資源はスケール可能だが、データのスケールは難しいため、結果的にデータ とプログラムがネットワーク越しに離れたものになってしまった
オートスケーリングのために1インスタンスは「無名」なものになり、インスタンス同士 の連携が難しくなった 「短命なコンピュート」を組み合わせると並列処理と分散コンピューティングと付き合 わなければならないが、分散コンピューティングの本質的な難しさに対処するため の簡単な処方箋は長らく存在しなかった -
“Durable” とは何なのか? Durable とは「長寿命な」という意味で捉えている。つまり、サーバーレスという「短命なコ ンピュート」上で、「長寿命なアプリケーション」を構築するための新しい部品が、Durable Functions や Durable Objects。 Actor
系列の Durable Objects 等は「リクエストはデータ間で分散するはず」という予測 から、無限のスケーリングを一部諦めて、「Actor(JS クラス+データなど)」を長寿命化し た。 Workflow Engine 系列の Lambda Durable Functions 等は、「リプレイ」という仕組みに よって、短時間のコンピュートであたかも長時間1つの関数が実行可能なように見える仕 組みを構築した