このページでは、正しく、パフォーマンスがよく、適切に分離された Service Extensions プラグインを作成するためのヒントを紹介します。プラグインは、API サーフェスが制限された制約付きエンジン サンドボックスで実行されるため、正確性が重要です。プラグインはエンドユーザーのリクエスト中に少量のリソースで実行されるため、パフォーマンスが重要になります。分離はプロジェクト レベルで提供されます。
ご利用にあたって
サンプルから始める
まず、プラグインのコードサンプルをご覧ください。これらは、パスやクエリの解析、ヘッダーの書き換え、カスタム ロギング、カスタム認証など、一般的なプラグイン パターンの例です。
サポートされている API を使用する
プラグインは、Proxy-Wasm バイナリ インターフェース(ABI)に対してコンパイルする必要があります。Service Extensions は、HTTP ヘッダーと本文の変更、ローカル レスポンス、カスタム ロギング、プラグイン構成を含む Proxy-Wasm ABI のサブセットをサポートしています。Proxy-Wasm は、ロギング用の stdout と stderr、clock_time_get と random_get など、WASI プレビュー 1 の小さなサブセットもサポートしています。
Service Extensions は、タイマー、カスタム指標、共有データ、共有キュー、アウトバウンド ネットワーク呼び出しをサポートしていません。Service Extensions は、リクエスト処理の一時停止を試みるプラグイン コールバックの戻り値もサポートしておらず、無視します。
機能テストとベンチマークを実行する
正確性とパフォーマンスを評価するために、プラグインを実行、テスト、ベンチマークできるローカル プラグイン テスター ツールを提供しています。このツールは Docker コンテナで呼び出し、プラグイン バイナリと入力と期待値のテキスト プロトを渡します。ローカル テスターのドキュメントとテスト入力の例をご覧ください。
正確性
時計に頼らない
セキュリティ上の理由から、クロック時間はコンテキストの作成時(プラグインまたはリクエストの場合)に設定され、プラグインの呼び出し中はフリーズされたままになります。つまり、WebAssembly プラグインはスリープ状態にできません。時間が進まないため、スリープはタイムアウトします。また、この情報は Cloud Monitoring で確認できますが、プラグイン自体で実行時間を測定することはできません。
ヘッダー名を大文字と小文字を区別しないものとして扱う
HTTP セマンティクスによると、HTTP ヘッダー フィールド名は、大文字と小文字を区別しないものとして扱う必要があります。プラグインによって記述されたヘッダーの大文字と小文字の区別は、クライアントまたはバックエンドに送信される前に変更できます。
パフォーマンス
プラグインのクラッシュを回避する
プラグインがクラッシュすると、トリガー リクエストでエラーが発生します。この状態が頻繁に発生すると、プラグインの再起動がスロットリングされ、複数のユーザーに影響するエラーが大量に発生します。
プラグインのクラッシュを回避するには、次のことを試してください。
- あらゆる種類の主張を避けてください。代わりに、エラー ロギングまたはユーザーへのローカル レスポンスを構成します。
- Rust では、パニックを引き起こす可能性のある
unwrapなどのメソッドの使用は避けてください。また、panic::set_hookを使用して、クラッシュを処理するようにプラグインを構成することもできます。 - Google が提供するプラグイン テスターを使用してプラグインをテストし、形式が正しくない入力や空の入力がプラグインのクラッシュなしで処理されることを確認します。
実行速度のためにコンパイルする
ビルド オプション -O3(C++ の場合)または opt-level=3(Rust の場合)を使用して WebAssembly コードをコンパイルし、最高の実行速度を実現します。
正規表現を事前コンパイルする
リクエストごとのパス(HTTP ハンドラ内)で、計算負荷の高いオペレーションを回避します。代わりに、リクエスト固有でない作業はプラグイン構成時に行い、事前計算された状態を各 HTTP リクエスト コンテキスト(ストリーム コンテキストとも呼ばれます)に渡します。
特に、プラグイン構成時に正規表現をプリコンパイルします。正規表現のコードサンプルで、この方法を確認できます。
HTTP コンテキストへのデータのコピーを避ける
ルート コンテキストと HTTP コンテキスト間でデータを共有する場合は、コストのかかるコピーやクローン呼び出しを避けてください。代わりに、ポインタまたは参照を共有します。C++ では、ルート コンテキストは HTTP コンテキストよりも長いため、std::shared_ptr または未加工のポインタと参照を使用してこれを行うことができます。Rust では、std::rc::Rc を使用して値を共有できます。Go では、値へのポインタを保存することで値を共有できます。このポインタはガベージ コレクタによって削除されます。
セキュリティ
ワークロードをプロジェクトごとに分離する
Google のインフラストラクチャでは、同じ Google Cloud プロジェクトに属するプラグインは同じ安全なサンドボックスで実行できます。つまり、WebAssembly ランタイムは、同じプロジェクト内のプラグイン間の唯一のセキュリティ バリアです。
セキュリティ分離が必要なワークロードには、個別の Google Cloud プロジェクトを使用します。この方法では、リソースと権限も分離されます。
Media CDN でシークレットを制限する
Media CDN は、Google の物理的な制御外で運用されるハードウェアのフリート上で動作するように設計されています。Media CDN 用のセキュリティ関連のプラグインを作成する場合は、専用のホスト名またはサブドメインを使用し、頻繁にローテーションされる署名鍵を使用してプラグインを構成します。