WebAssembly コンポーネントモデル徹底解説(2026):WIT、WASI 0.3 と言語横断構成の実践

边缘计算ブラウザ AI と WebAssembly(更新: 2026年7月20日)

なぜコンポーネントモデルが WASM の「第二幕」なのか

WASM の「第一幕」がブラウザ内で C++ ゲームエンジンを動かすことだったなら(2017–2021)、「第二幕」はコンポーネントモデル——任意の言語で書かれたコードを型安全なインタフェースで構成し、シリアライズオーバーヘッドも手書きの糊コードも不要にする。

2026 年、WASI 0.2(preview2)は安定し、WASI 0.3 は凍結中。つまり WASM はもはや「1 つのモジュールの中で 1 つの Rust 関数を走らせる」ものではない。npm から Go 製の暗号ライブラリをインストールし、Python サービスから Rust 推論エンジンを呼び出し、CDN エッジノードで JS を使って多言語バックエンドを編成できる。


モジュール vs コンポーネント:1 つの表で全てを説明

WASM モジュール WASM コンポーネント
インタフェース記述 フラットな import/export 関数シグネチャ WIT(WebAssembly Interface Type)強型インタフェース
データ型 整数・浮動小数点・メモリポインタのみ String、Record、Variant、List、Resource 等の高級型
メモリモデル 共有線形メモリ(安全でない、手動管理) 分離線形メモリ+インタフェース層による自動シリアライズ
言語横断バインディング 手書き JS/Rust/Go 糊コード wit-bindgen が自動生成
構成可能性 なし——モジュール間の構成は不可能 コンポーネントをリンクして新コンポーネントに
ランタイム依存 ホストが具体的実装を提供する必要がある world で import を宣言し、ランタイムが注入

WIT:コンポーネントに「同じ言語を話させる」

WIT はコンポーネントモデルの IDL(インタフェース定義言語) である。Protocol Buffers の .proto や Apache Thrift の .thrift に相当する。

WIT 基本構文

/// 数学演算ライブラリのインタフェース
package math:operations@0.1.0;

interface calculator {
  /// 複素数型
  record complex-number {
    real: float64,
    imag: float64,
  }

  /// 安全な除算——成功かエラー文字列
  variant division-result {
    ok(float64),
    err(string),
  }

  divide: func(a: float64, b: float64) -> division-result;
  exp: func(base: float64, exponent: float64) -> float64;
  fft: func(samples: list<complex-number>) -> list<complex-number>;
}

World:コンポーネントの完全な契約

world はコンポーネントが何を必要とし(imports)何を提供するか(exports) を記述する:

package my-app:service@1.0.0;

/// HTTP 能力をインポート(WASI 0.2 から)
world app {
  import wasi:http/handler@0.2.0;
  import wasi:io/streams@0.2.0;
  /// 業務インタフェースをエクスポート
  export calculator: interface {
    compute: func(input: string) -> string;
  };
}

WASI 0.2 / 0.3:ファイルシステムから HTTP サービスへ

WASI バージョン 中核能力 状態(2026 Q2)
0.1(preview1) ファイル I/O、環境変数、時計、乱数 ⚠️ 廃止予定、移行専用
0.2(preview2) 上記+HTTP(wasi:http)、IO ストリーム、Socket、CLI ✅ 安定、Wasmtime 19+ で完全サポート
0.3(preview3) 統一能力モデル、非同期ストリーム、リソース所有権 🔄 凍結中、2026 Q3 予定

WASI 0.2 の最重要追加は wasi:http——これにより WASM コンポーネントは Lambda 関数のように直接 HTTP リクエストを処理できる:

// Rust コンポーネント:HTTP リクエストハンドラ
use wasmtime::component::*;
use wasi::http::types::*;

impl Handler for MyHandler {
    fn handle(&self, request: IncomingRequest) -> Result<OutgoingResponse> {
        let method = request.method();
        let path = request.path_with_query();
        // ビジネスロジック...
        Ok(OutgoingResponse::new(200, "Hello from WASM"))
    }
}

WIT からコードへ:四言語ワークフロー

同一の WIT を各言語で使用する方法を示す。

Rust

// wit-bindgen で WIT から Rust trait を生成
wit_bindgen::generate!({
    world: "calculator-world",
});

struct CalculatorImpl;

impl Calculator for CalculatorImpl {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    fn multiply(&self, a: i32, b: i32) -> i32 {
        a * b
    }
}
# WASM コンポーネントとしてコンパイル(モジュールではない!)
cargo component build --release

Go

// wit-bindgen-go がバインディングを生成
//go:generate wit-bindgen-go generate ./wit --world calculator-world

import "example/calculator/wit"

type calcImpl struct{}

func (c *calcImpl) Add(a int32, b int32) int32 {
    return a + b
}

func main() {
    wit.SetCalculator(&calcImpl{})
}

Python

# componentize-py で Python バインディングを生成
from calculator import exports

class Calculator(exports.Calculator):
    def add(self, a: int, b: int) -> int:
        return a + b
    def multiply(self, a: int, b: int) -> int:
        return a * b
componentize-py -d ./wit -w calculator-world componentize app -o calculator.wasm

JavaScript(ホスト側からコンポーネントを呼び出す)

import { Calculator } from "./calculator.component.js";

// JS から Rust 製コンポーネントを呼び出す
const calc = await Calculator.instantiate();
console.log(calc.add(2, 3));       // 5
console.log(calc.multiply(4, 5));  // 20

🔥 重要なポイント:JS が Rust コンポーネントの add() を呼ぶ時、引数と戻り値は WIT 定義インタフェースを通じて自動シリアライズされる——手書き糊コードゼロ、JSON シリアライズオーバーヘッドゼロ。これが従来の FFI / REST と比較したコンポーネントモデルの本質的優位性である。


コンポーネント構成:部品を製品に組み立てる

wasm-tools compose はコンポーネントモデルの「npm link」である——複数の独立してコンパイルされたコンポーネントを 1 つの新しいコンポーネントにリンクする:

# 1. 各コンポーネントを個別にコンパイル
cargo component build -p math-core --release     # → math-core.wasm
cargo component build -p http-server --release   # → http-server.wasm

# 2. 構成
wasm-tools compose http-server.wasm \
  -d math-core.wasm \
  -o combined-server.wasm

構成後の combined-server.wasm は外部から見て単一コンポーネントである。内部依存はすべて解決済みで、ランタイムは内部にいくつのサブコンポーネントが存在するか感知しない。


典型的な本番アーキテクチャ

アーキテクチャ 1:プラグイン SaaS プラットフォーム

 ┌─────────────────────────────────────┐
 │            SaaS ホストプラットフォーム   │
 │  ┌──────────┐  ┌──────────┐         │
 │  │ ユーザー   │  │ ユーザー   │         │
 │  │ プラグインA │  │ プラグインB │  ...    │
 │  │ (Rust)   │  │ (Go)     │         │
 │  └──────────┘  └──────────┘         │
 │          ▲   WIT インタフェース        │
 │  ┌──────────────────────────┐        │
 │  │   ホストランタイム           │        │
 │  │   (Wasmtime)              │        │
 │  │   能力注入+サンドボックス隔離 │        │
 │  └──────────────────────────┘        │
 └─────────────────────────────────────┘

プラグイン間は完全に隔離され、各プラグインはホストが注入した特定の能力のみアクセス可能。ユーザーは SaaS プラットフォーム向けのカスタムプラグインを任意の言語で作成できる——プラットフォームがソースコードを審査する必要はなく、WASM サンドボックスが安全性を保障する。

アーキテクチャ 2:エッジコンピューティング Multi-Tenant

 CDN エッジノード
  ├─ テナント A:Rust 画像圧縮コンポーネント
  ├─ テナント B:Go A/B テストルーティングコンポーネント
  ├─ テナント C:Python レコメンデーションアルゴリズムコンポーネント
  └─ 共有:wasi:http、wasi:keyvalue(Redis インタフェース)

同一エッジノード上で、複数テナントのコンポーネントがそれぞれ自身のサンドボックス内で実行され、WASI 0.2 の標準インタフェースを通じて共有リソース(HTTP、キーバリューストア、ログ)にアクセスする。


移行パス:モジュールからコンポーネントへ

既存の WASM モジュール → WASM コンポーネントは、wasm-tools adapt で適応できる:

# 古いモジュール用に適応層を用意
wasm-tools component new old-module.wasm \
  --adapt adapter.wasm \
  -o new-component.wasm

適応層はモジュールのフラットな import/export を WIT 定義の強型インタフェースにマッピングする。人気のモジュール(SQLite の WASM 版、FFmpeg の WASM 版等)向けには、コミュニティが既製の適応層を提供している。


Docker / コンテナとの関係

次元 WASM コンポーネント Docker コンテナ
コールドスタート < 1 ms 100 ms – 2 s
メモリ占有 数百 KB – 数 MB 数十 MB – 数百 MB
隔離レベル 言語レベルサンドボックス(システムコールなし) カーネルレベル(namespace + cgroup)
クロスプラットフォーム ✅ 1 回コンパイル、全環境で実行 ⚠️ アーキテクチャ別イメージが必要
システム能力 WASI 定義の標準サブセット 完全な Linux システムコール
構成可能性 コンパイル時リンク ランタイムネットワーク呼出

WASM コンポーネントは Docker コンテナを置き換えない——相互補完の関係にある:

  • コンテナ:完全な OS 環境を提供し、長時間稼働するサービスプロセスを管理する。
  • コンポーネント:軽量・安全・ミリ秒起動の関数レベル実行単位を提供する。

Knative / AWS Lambda 等の Serverless プラットフォームでは、コンテナイメージを WASM コンポーネントに置き換えることで、コールドスタート遅延とメモリコストを著しく削減できる。


よくある質問(FAQ)

Q1:全言語で WIT を書く必要がありますか?

不要。WIT を 1 つ書くだけ。各言語チームは各自のツールチェーン(wit-bindgencomponentize-pywit-bindgen-go)で同一の WIT からバインディングを生成する。WIT が唯一の真理源(single source of truth)である。

Q2:コンポーネントモデルと gRPC / REST の違いは?

  • gRPC / REST:プロセス間通信。シリアライズ、ネットワークオーバーヘッド、エラーハンドリングが必要。
  • コンポーネントモデル:プロセス内呼出(同一ホスト内)。WIT が自動的にメモリ受け渡しを管理し、ゼロシリアライズオーバーヘッド(直接線形メモリ共有または copy-on-write)。

コンポーネントモデルは高密度・低遅延・同一ホスト内の多言語協調に適し、gRPC/REST はネットワーク越えのサービス呼出に適する。

Q3:コンポーネントモデルは Rust 専用ですか?

全く違う。WASI 0.2 時代では Rust のツールチェーン(cargo-component)が最も成熟しているが、Go(wit-bindgen-go)、Python(componentize-py)、JS(jco)、C/C++(wit-bindgen ネイティブサポート)はいずれも本番利用可能レベルに達している。Java や .NET のバインディングも急速に追いついている。

Q4:WASI 0.2 と WASI 0.1 は共存可能ですか?

短期的には可能だが、wasi:http 等の 0.2 新インタフェースは preview1 形式では提供されない。新規プロジェクトは直接 0.2 を選択し、レガシープロジェクトは wasm-tools adapt で移行することを推奨。

Q5:コンポーネントモデルは本番で安定していますか?

2026 年、Wasmtime 19+、WasmEdge 0.14+ が WASI 0.2 を完全サポートしている。Shopify、Fermyon、SingleStore 等の企業に本番事例がある。中核業務に使用する場合は、非クリティカルパス(内部ツール、補助サービス)から段階的に移行することを推奨。


結論

コンポーネントモデルは既存の WASM に新しい API 層を追加するものではない——WASM のプログラミングモデルを根本から再発明するものだ。かつて「Python から Rust をどう呼ぶ?」と問われれば、答えは「C FFI ブリッジを書き、whl をコンパイルし、ABI 互換性を処理する」だった。今、同じ質問への答えは:「WIT を 1 つ書き、両側で wit-bindgen を実行し、リンクする——以上。」

推奨学習パス

  1. cargo-component + wasmtime をインストール。
  2. 簡単な WIT(add 関数 1 つ)を書く。
  3. Rust で実装し、コンポーネントとしてコンパイル。
  4. Python または JS から読み込み、呼び出す。
  5. 実感せよ——シリアライズなし、REST なし、FFI コンパイルの苦痛なし——これがコンポーネントモデルがもたらすパラダイムシフトである。
#WASM#组件模型#WIT#WASI#多语言#插件化