Web開発
5倍高速なEdge Functions: V8 IsolatesからFirecracker MicroVMsへ
5x faster Edge Functions: V8 isolates to Firecracker MicroVMs (netlify.com)
要約
Netlifyは、毎日のEdge Functionsの実行数を約10億回から5倍高速化するため、インフラストラクチャを再構築しました。この変更により、リクエスト処理、ルーティング、コンピューティング容量の割り当て、コードの起動がミリ秒単位で行われるようになり、レイテンシが大幅に削減されました。新しいアーキテクチャでは、Firecracker MicroVMsが使用され、セキュリティと信頼性の向上、およびエッジでの複雑なコンピューティング実行の可能性が広がっています。
全文翻訳
Netlifyでは毎日約10億回のEdge Functionsが実行されており、Sunwebによるページパーソナライゼーション、Loto-QuébecによるCookieチェックでのトラフィックルーティング、その他数百ものサイトでのパーソナライゼーション、ルーティング、認証など、あらゆる用途に利用されています。これらはすべて、お客様のトラフィックに合わせてスケーリングするフルJavaScriptランタイム上で実行されています。これは、レイテンシを可能な限り低く抑えようとする上で、重大な技術的課題を提示します。毎秒数万、時には数十万のEdge Functionsを実行するには、各リクエストを処理し、正しくルーティングし、コンピューティング容量を割り当て、プラットフォームコードと顧客コードの両方を起動する必要があります。これらすべてをミリ秒単位で行う必要があります。
過去数ヶ月間、私たちのチームはEdge Functionsの基盤となるインフラストラクチャを再構築し、Unikraftのチームと緊密に協力しました。彼らは彼らの側からの経験について書いています。以前は、リクエストはホストされた実行サービスに送られていました。今日、それらは私たちのエッジネットワーク内のMicroVM上で実行されており、中央値で約5倍高速です。この移行は、セキュリティと信頼性も向上させ、エッジで複雑なコンピューティングを実行するためのより多くの可能性を開きます。Edge Functionsの記述方法や使用方法に変更はありません。URLインポート、npmパッケージ、Node組み込み、netlify.toml宣言、ローカル開発など、すべて以前と同じように機能します。今ではより高速で、より回復力があります。この記事では、新しいアーキテクチャと、低パフォーマンスオーバーヘッドで高ボリュームを提供できる新しいコンピューティングプラットフォームの構築に関する学習について、さらに詳しく共有したいと思います。
まず数字です。
Edge Functionは、サイトの前に、それに一致するすべてのリクエストに対して実行されます。かかる時間は、顧客が待機する時間であり、ここでのミリ秒は他のほとんどの場所よりも重要です。
ウォームインボケーション(コンピューティングノードへのルーティング、MicroVMへの入力、関数の実行、レスポンスヘッダーの生成)にかかる時間:中央値(p50)で約5〜6ミリ秒、以前のインフラストラクチャでは25〜40ミリ秒でした。
p99インボケーションは47.4%高速化。
利用可能率は99.998%。
Edge Functionのログ配信は5倍高速化。
コールドインボケーションについても言及する価値があります。リクエストが、コンピューティングノードがまだ認識していないリージョンに到着した場合、何かを実行する前に関連するイメージを取得する必要があります。これは約1.2%のインボケーションで発生し、平均して約9ミリ秒かかります。
リクエスト時の処理内容
以下は、単一のリクエストが順番にたどるパスです。エッジノードに到着し、仕様に変換され、コンピューティングノードにルーティングされ、MicroVMに引き渡されます。MicroVMは、コールドインボケーションかウォームインボケーションかによって、すでに存在する場合があります。
リクエストがエッジノードに到着
すべてのリクエストは、クライアントに最も近いNetlifyエッジノードに着陸します。ノードはTLS接続を終了し、デプロイのエッジファンクションのルートに対してリクエストパスをチェックします。一致するものがない場合、リクエストは通常通りキャッシュに進み、オリジンに進みます。ルートが一致する場合、これはリクエストがネットワークを離れたポイントです。以前のインフラストラクチャでは、インターネット経由で送信され、エッジファンクションを実行し、私たちに戻ってきて渡されていました。新しいコンピューティングプラットフォームでは、リクエストはネットワーク内のコンピューティングノードに転送されます。
Edge Functionサービスの作成
コンピューティングノードがマシン仕様とサービスIDとともにリクエストを受け取ると、まずそのIDを持つサービスがすでに存在するかどうかを確認します。存在する場合、リクエストをサービスに転送してMicroVMに送信します。サービスにより、同じサイトのEdge Functionsに関連付けられた複数のMicroVMを持つことができ、MicroVMのスケーリングイン/アウトのタイミングを設定できます。たとえば、各サービスは、MicroVMが無期限に実行されるのを防ぐために、シャットダウンする前にMicroVMが処理できるリクエスト数を固定数に制限するように構成します。これらの同じパラメータを使用して、MicroVMがシャットダウンされるのを予測して、MicroVMを積極的に起動するタイミングを把握します。
サイトのエッジファンクションのサービスがコンピューティングノードにまだ存在しない場合、1つが作成され、マシン仕様のすべてのイメージがディスク上にあるかどうかがチェックされます。不足しているものがある場合は、エッジノードから取得され、ディスクに書き込まれます。このアプローチは、そのリージョンでトラフィックを受信しているエッジファンクションイメージのみを取得することを意味します。
エッジノードが仕様を書き込む
リクエストがどこかに移動する前に、エッジノードはファンクションを実行するマシンの仕様を書き込みます。仕様は3つのイメージを名前付けします:ランタイム、プラットフォームイメージ、エッジファンクションイメージ。また、CPU、メモリ、接続制限も設定します。仕様は、すべてリクエストとともに移動します。そのハッシュとサイト固有の情報は、サービスIDになるように計算されます。これにより、異なるコードまたは異なる環境変数を持つ2つのデプロイは異なるサービスであり、MicroVMを共有することはないため、分離が可能になります。この分離は、発生してほしくない障害に対して最も重要です。潜在的に侵害されたデプロイは個別のMicroVMで実行され、ランタイムから脱出したとしても、他の顧客やコンピューティングレイヤー自体を汚染することはできません。V8 Isolatesは、その名前に関係なく、このレベルの分離を提供しません。
Edge Functionを実行するコンピューティングノードの選択
各リージョンにはコンピューティングノードのグループがあります。エッジノードは、ランデブーハッシュを使用してサービスを選択します。同じサービスは毎回同じノードに着陸するため、MicroVMがウォーム状態に保たれ、コードがディスク上およびキャッシュに読み込まれた後にキャッシュされます。このスティッキー性は、キャッシング戦略を提供します。ファンクションのすべてのリクエストを同じコンピューティングノードに送信すると、コールドスタートのレベルが高くなります。ファンクションのすべてのリクエストを同じコンピューティングノードに送信することは高速パスですが、ホットスポットを形成する方法でもあります。つまり、1つのビジーなファンクションが、そのボックス上の他のすべてを犠牲にしてリソースを競合します。このバランスを取るために、スティッキー性を緩和します。あるしきい値を超えると、サービスをノードのスライスに分散させます。これにより、同じノードにハッシュされた他のサービスに影響を与えることなく、単一の顧客からのトラフィックの突然のスパイクを吸収できます。
最後に、ノードが選択されると、ファンクションのコードがプルされます。以前にファンクションを提供したことがあるコンピューティングノードは、すでにそれを持っています。初めて見るノードはそれを一度取得してキャッシュするため、最初の1つのリクエストのみがそのコストを負担します。
MicroVMの起動
各ファンクションは、独自のFirecracker MicroVMで実行されます。これらは1ミリ秒未満で作成され、p99で約2ミリ秒で開始されます。これは、VMが完全なオペレーティングシステムではなく、最小限のLinux環境を起動するためです。エッジファンクションのファイルは、非圧縮のEROFSイメージとしてマウントされ、その後メモリマップされるため、VMはすべてをロードするのではなく、実際に使用するバンドルの部分のみを読み取ります。
MicroVMが起動し、JavaScriptサーバーがポートでリッスンを開始すると、MicroVMのスナップショットが取得されます。エッジファンクションが呼び出されていない場合、それを実行しているMicroVMはアイドル状態のままではなく、ゼロにスケーリングされます。次に呼び出されたときに、そのスナップショットから新しいMicroVMが起動されます。スナップショットはメモリマップされるため、VMはスナップショット全体がメモリに読み込まれるのを待たずに実行を開始できます。
VMのライフサイクル(起動、スナップショット、復元、ゼロへのスケーリング)は、Unikraftの製品の作業です。私たちは、リクエスト量とトラフィックパターンでそれが耐えられることを確認するために、移行全体を通して彼らと緊密に協力しました。
実行とレスポンス処理
このプロジェクトを数年間運営してきた結果、パフォーマンスとデバッグ能力を最大化するために組み込んだ多くの学習がありました。大規模になると、仮想スイッチのポート枯渇からDNS(驚きましたが、常にDNSだったわけではありません)まで、あらゆる種類の問題に遭遇しました。このイテレーションでは、コンピューティングノードがローカルDNSリゾルバを実行するようにしました。また、収集するメトリクスを拡張し、記録しています。