代表的な面接トピック

総合面接:WebAssembly Component ModelとWASIをどのように説明しますか?

一般難しい
Offer.cc 編集チーム公開日 更新日

質問

あるチームが、ファイルやネットワークへのアクセスを制限しつつ、Rust、Go、その他の言語で書かれたプラグインを単一のサービス内で実行したいと考えています。WebAssembly Component Model、WASI、WITがどのように連携するか、またそれらを使用すべきではない場面について説明してください。

プロンプトと適用範囲

SaaSプラットフォームにおいて、サードパーティ製のルールプラグインをロードする必要があります。プラグインは異なる言語で記述されている可能性があり、ホスト側は明示的なクロック、ロギング、オブジェクトストレージのインターフェースのみを付与した上で、単一プロセスまたは軽量ランタイム内でそれらを再利用したいと考えています。コアWebAssemblyモジュール、Component Model、WIT、WASIの関係性を説明し、バージョニング、パフォーマンス、セキュリティ、および運用の制御策を提案してください。

ここでは「ネイティブに近い速度」といった決まり文句ではなく、クロスランゲージランタイムとセキュリティ境界が試されます。WebAssembly 3.0仕様では、コアWasmを検証済みでサンドボックス化された仮想命令セットとして定義しています。Component Modelは型付きインターフェース、合成(composition)、および呼び出し規約を追加し、WITはインターフェースを記述し、WASIはWIT形式でシステム機能を提供します。適切な回答では、「モジュールが実行されること」と「プラグインがホスト機能を安全に使用できること」を明確に区別します。

面接官がテストしていること

優れた回答は、最小限の機能セットから開始し、プラグインインターフェースを定義した上で、すべてのプラグインにホストプロセスを割り当てる代わりにComponentを採用することが妥当かどうかを判断します。コアモジュールにおける共有リニアメモリと型定義されたComponentの境界を区別し、worldがインポートとエクスポートをどのように宣言するかを説明し、宣言されていないインポートをホストがどのように拒否するかを示します。

また、現実的な側面にも踏み込みます。ホストがデータエクスポートAPIを公開していればサンドボックスはビジネス上のセキュリティにはならないこと、境界を越える際に大きな値がコピーされる可能性があること、そしてComponent ModelとWASIのバージョンには固定されたランタイムマトリクスが必要であることなどです。面接の準備資料では通常、単一のツールに関するトリビアではなく、ポータビリティ、分離、インターフェース設計、リカバリがエンジニアリングスキルとしての評価基準と見なされます。

最初に明確にすべき質問

プラグインの信頼性と分離の目標

プラグインがファーストパーティ製か、パートナー所有か、あるいは完全に信頼できないものかを確認します。攻撃者がホストプロセスからエスケープする可能性がある場合、単一のWasmサンドボックスでは不十分であり、プロセス分離や専用サービスを使用します。ネットワーク、ファイル、乱数、クロックが必要かどうかを確認してください。これらはそれぞれ機能境界を拡大するためです。

呼び出しモデルとデータサイズ

呼び出しが短い同期的ルール評価なのか、長時間実行ジョブなのか、ストリームなのかを明確にします。小さな構造化データはインターフェース境界に適していますが、大きなファイルの場合は繰り返しのコピーを避けるために制御されたハンドルやホスト管理オブジェクトを使用すべきです。レイテンシと並行性の目標によって、プーリング、ウォームアップ、バックプレッシャーの設計が決まります。

バージョンと障害時の契約

どちらが先にアップグレードするのか、古いインターフェースの共存が必要か、失敗した呼び出しはリトライ可能か、副作用は冪等(idempotent)かどうかを確認します。プラグインが外部システムに書き込みを行う場合は、タイムアウト後の不明な結果、冪等性キー、および監査ログを定義します。

30秒の回答フレームワーク

「プラグインは、宣言されたインターフェースを通じてのみ対話するComponentとして扱います。evaluateのエクスポートと、ロギングおよび制限されたオブジェクト読み取りのインポートを行う最小限のWIT worldを定義します。ホストは許可リストに基づいてこれをインスタンス化し、それ以外の機能は一切提供しません。WASIは標準化されたシステムインターフェース群であり、ファイルやネットワークへの自動的なアクセス権付与ではありません。コアWasmが検証済みの実行環境を提供するのに対し、Component Modelは型付きのクロスランゲージ合成を提供します。インターフェースのバージョニングを行い、リソースを制限し、権限、互換性、パフォーマンス、リカバリをテストします。プラグインが完全に信頼できない場合や大規模な共有状態が必要な場合は、別サービスへの分離を選択します。」

ステップごとの解決策

ステップ1:機能(Capability)と信頼性から始める

各プラグインの要件を機能リストとして書き出します(ロギング、現在時刻、設定の読み取り、オブジェクトの読み取り、キューへの送信など)。機能とはコメントではなく、実際のインポートです。純粋な計算処理のworldには、ファイルやネットワークのインターフェースを持たせるべきではありません。信頼できないプラグインの場合、Wasmを一層の防御策として扱い、プロセス制限、署名付きリリース、依存関係のレビューを追加します。

ステップ2:コアモジュールとComponentを区別する

コアモジュールは低レベルの関数、テーブル、リニアメモリ、インポートとエクスポートを定義し、これは単一のランタイムまたは言語バインディング内ではうまく機能します。モジュール間で文字列やリストを受け渡すには、多くの場合、共有メモリレイアウトと手書きのABIが必要となり、言語間の結合が生じます。Componentは、1つ以上のコアモジュールを自己記述型のコンテナにパッケージ化し、文字列、レコード、バリアント、結果型(result)にインターフェース型を使用し、境界適応のためにCanonical ABIを適用します。

text
world rules {
  import logger: interface { log: func(level: string, message: string) }
  import objects: interface { read: func(key: string) -> result<list<u8>, not-found> }
  export evaluate: func(input: list<u8>) -> result<list<u8>, rule-error>
}

このWIT形式の宣言は境界を表現するものであり、実装言語を表すものではありません。ホストはコンポーネント宣言が許可されたworldと一致することを検証し、余分なインポートを拒否します。プラグイン側にはホストのポインタやファイルディスクリプタではなく、インターフェースが見えます。

ステップ3:WIT worldとWASIを説明する

WITはインターフェースとインポート/エクスポートの方向を定義します。worldはそれらのインターフェースを含む閉じた契約です。ホストは共有メモリなしで、あるコンポーネントのエクスポートを別のコンポーネントのインポートに接続できます。WASIは、ファイル、ネットワーク、クロック、その他のシステム機能に対して同じインターフェース記述スタイルを使用しますが、ランタイム側でディレクトリマッピング、ネットワークポリシー、リソース制限を設定する必要があります。WASIを実装したからといって、すべての機能が無条件に開放されるわけではありません。

ステップ4:ライフサイクルとリソース制御を設計する

インスタンスごとのメモリ、呼び出し時間、Fuelまたは実行ステップ数、並行性、出力バイト数の制限を設定します。短時間の呼び出しではプールを使用してリクエスト間で状態をクリアできますが、ステートフルなプラグインでは明示的なホストオブジェクト内に状態を保持する必要があります。キャンセル時は新規の呼び出しを停止し、インスタンスの待機または終了を行い、外部への副作用が発生した可能性があるかどうかを記録します。エラーが返されたとしても、外部への書き込みがロールバックされたことの証明にはなりません。

ステップ5:バージョン管理と言語の相互運用性を処理する

インターフェースにオプションのフィールドを追加することは、通常、既存のセマンティクスを変更するよりも安全です。削除や意味の変更には、新しいworldまたは移行期間が必要です。リリース時にWITパッケージ、ランタイム、アダプタ、機能マニフェストを固定し、コンポーネントのハッシュと依存関係のバージョンを記録します。シャドー実行を行い、結果、レイテンシ、リソース使用量を比較した上で、テナントまたはプラグインのバージョンごとにカナリアリリースを実施します。新しいセマンティクスを無理に古いworldに押し込むのではなく、古いプラグインは古いworldのまま維持します。

ステップ6:テストによって境界と価値を証明する

セキュリティテストでは、未宣言のインポート、パストラバーサル、ネットワークエスケープ、リソース枯渇、悪意を持って肥大化させた結果、無効なコンポーネント署名などをカバーします。互換性テストでは、複数の言語で同じWIT値を生成し、オプションフィールド、エラーバリアント、ホストおよびコンポーネントの独立したアップグレードを検証します。パフォーマンステストでは、境界適応、コールドスタート、プールヒット、ビジネス計算処理を分離して計測します。コピーのオーバーヘッドが支配的である場合は、ネイティブライブラリや別サービスへの切り出しの方が優れた設計である可能性があります。

質の高い模範解答

まずプラグインの信頼性と必要な機能を確認します。管理下にあるパートナーのプラグインの場合、最小限のWIT worldを定義します。プラグインはルール評価をエクスポートし、構造化ロギングと制限付きのオブジェクト読み取りをインポートします。ホストはそれらのインポートのみを接続し、メモリ、時間、並行性、出力の制限を強制します。純粋な計算処理のworldには、ファイルやネットワークの機能は含めません。

コアWasmは検証済みの低レベル実行を提供しますが、言語をまたぐ複合値は共有メモリABIに依存する場合があります。Component Modelは型付きインターフェースとCanonical ABIでモジュールをラップします。WITは方向性を記述し、WASIは標準システム機能のオプションのファミリとなります。ディレクトリ、ネットワーク、クロックの権限設定は依然として必須です。リリースではWITパッケージ、アダプタ、ランタイム、コンポーネントハッシュを固定します。新しいバージョンをシャドー実行およびカナリアリリースする間も、古いworldは引き続き利用可能にしておきます。

最後に、権限、リソース枯渇、リトライ動作、外部への副作用をテストし、コールドスタート、境界適応、プールヒット、ビジネスロジック実行時間を計測します。プラグインが完全に信頼できない場合、複雑なネットワークを必要とする場合、あるいは大規模な可変状態を共有する場合は、Wasmサンドボックスを完全なセキュリティソリューションとして扱うのではなく、より強力なプロセス分離を備えた別サービスを使用します。

よくある間違い

  • WebAssemblyを直接システムコールを呼べるプロセスとして扱う → コアモジュールには環境APIが存在せず、機能はホストのインポート経由で提供されます → 各インポートをリスト化し、最小権限を設定します。
  • Componentを単なる高速なコアモジュールとして扱う → その主な価値は型付きインターフェース、合成、クロスランゲージABIにあり、適応コストが発生する場合があります → コールドスタート、適応、ビジネス処理を個別にベンチマークします。
  • すべてのプラグインに完全なWASIを許可する → ファイル、ネットワーク、クロックの機能はデータ漏洩やリソース濫用の経路を拡大します → worldおよびテナントごとに機能を付与し、デフォルト拒否とします。
  • 戻り値のみを検証する → タイムアウト前に外部書き込みが発生している可能性があるため、リトライにより副作用が重複する恐れがあります → 冪等性、監査、結果不明ステータスを定義します。
  • 新しいホストインターフェースを古いプラグインに強制する → 型が同じであってもセマンティクスが同じとは限りません → バージョニングされたworldと明示的な移行をサポートします。

フォローアップの質問と回答

フォローアップ1:なぜプラグインをコンテナで実行しないのですか?

コンテナは、独立したファイルシステム、ネットワークスタック、またはプロセスの障害境界を必要とするプラグインに適していますが、通常、起動時間とリソースコストが高くなります。Componentは、短い呼び出し、明示的な機能、高密度なインスタンス配置に適しています。プラグインがカーネル権限、特別なドライバ、またはより強力な分離を必要とする場合は、コンテナまたはサービスを使用します。脅威モデル、起動目標時間、リソース予算、運用の成熟度に基づいて選択します。

フォローアップ2:外部書き込み後にコンポーネントがタイムアウトしました。リトライできますか?

タイムアウトは、書き込みが行出なかったことを証明するものではありません。リクエストIDと冪等性キーを外部システムに渡し、UNKNOWNを記録した上で、クエリまたは補償ワークフローを通じて成否を確認します。実行されていないことが確認できた後、または外部契約が冪等性を保証している場合にのみリトライします。Component Modelはシステム間のトランザクションを提供するものではありません。

フォローアップ3:プラグインがログを通じてデータを持ち出すのを防ぐにはどうすればよいですか?

ロギングも一つの機能(capability)です。フィールドのサイズ、構造、レートを制限し、機密フィールドを分類してテナントを分離します。リスクの高いプラグインには、マスキング用プロキシを使用するか、ロギングのインポートを一切渡さないようにします。ランタイム監査では、ログの内容をセキュリティの証明とするのではなく、バージョン、機能、呼び出し回数を記録します。

フォローアップ4:WASI Previewのバージョンが変更された場合はどうなりますか?

WASIインターフェースパッケージとランタイムバージョンをコンポーネントのリリースマニフェストに記載し、互換性マトリクスをデプロイのゲート(判定基準)とします。新しいランタイムを古いworldに対して実行し、リグレッションテストと結果比較を行います。セマンティクスが異なる場合は、古いランタイムまたはアダプタを保持します。「WASIを実装している」ことは、バージョン間の互換性を保証するものではありません。

公開情報ソース

関連する質問