2026年のWebAssembly: WASI、Component Model、そして本番環境でのWasm実行

Table of Contents
「WebAssembly everywhere」という話は何度も繰り返されてきたので、聞き飽きた人もいるかもしれません。そこで、今回はマニフェストは抜きにして、実際に何が出荷され、実際のトレードオフは何で、2026年にはどのような実用的なWasmデプロイメントが実現しているのかを見ていきましょう。
実際に何が変わったのか:WASI Preview 2とコンポーネントモデル
サーバーサイドWasmを実用的にしたのは、WASIとコンポーネントモデルの2つです。これらはよく一緒に言及されますが、解決する問題は異なります。
WASI (WebAssembly System Interface) は、Wasmモジュールがホストと通信する方法を提供します。ファイル読み込み、ネットワーク接続、環境変数の取得などです。WASI Preview 1はフラットなPOSIX風の名前空間でした。WASI Preview 2(2025年初頭から安定版)は、WIT (WebAssembly Interface Types) で定義された型付きケーパビリティインターフェースにすべてを再構築しました。
// http-handler.wit
package my:service;
interface handler {
use wasi:http/types@0.2.0.{incoming-request, outgoing-response, response-outparam};
handle: func(request: incoming-request, response-out: response-outparam);
}
world http-service {
export handler;
}
このWITファイルが契約書となります。ツールはホストとゲストのバインディングを自動的に生成します。モジュールは、Wasmtime、Cloudflareのランタイム、Spinのいずれで実行されているかを知りません。ホストがインターフェースを満たします。
コンポーネントモデルは、生のWasmモジュールを、互いにリンクできるコンポーネントにラップします。異なる言語でコンパイルされた2つのコンポーネント、例えばRustの画像処理ライブラリとPythonのオーケストレーションレイヤーは、JSONやHTTPを介さずにWITインターフェースを介して直接呼び出すことができます。ランタイムがそれらを接続します。
Cloudflare Workers:本番環境でのリアルWasm
Cloudflare WorkersはWasmをネイティブで実行し、おそらく今日最も広く使われている本番Wasmランタイムです。すべてのWorkerはアイソレートで実行されるWasmモジュールです。コールドスタートは秒単位ではなくマイクロ秒単位で測定されます。
// worker.js — loading a compiled Rust Wasm module
import wasm from './my-lib_bg.wasm';
import { init, process_payload } from './my-lib.js';
export default {
async fetch(request) {
await init(wasm);
const body = await request.arrayBuffer();
const result = process_payload(new Uint8Array(body));
return new Response(result, {
headers: { 'content-type': 'application/json' }
});
}
};
Rust側はwasm32-unknown-unknownとwasm-bindgenでコンパイルされます。
use wasm_bindgen::prelude::*;
use serde_json::Value;
#[wasm_bindgen]
pub fn process_payload(data: &[u8]) -> String {
let parsed: Value = serde_json::from_slice(data)
.unwrap_or(Value::Null);
// ... processing logic
serde_json::to_string(&parsed).unwrap()
}
ビルドとデプロイ:
wasm-pack build --target bundler
wrangler deploy
モジュールはコールドリクエストで約0.3msで起動します。比較として、Node.js Lambdaのコールドスタートは通常200-800msです。
Fermyon Spin:WasmでHTTPマイクロサービスを構築する
Spin は、Wasmネイティブなバックエンドフレームワークの最も明確な例です。Rust、Python、Go、またはJSでコンポーネントを記述します。各コンポーネントは特定のルートを処理します。
# spin.toml
spin_manifest_version = 2
[application]
name = "api-service"
version = "0.1.0"
[[trigger.http]]
route = "/users/..."
component = "user-handler"
[[trigger.http]]
route = "/auth/..."
component = "auth-handler"
[component.user-handler]
source = "target/wasm32-wasip1/release/user_handler.wasm"
allowed_outbound_hosts = ["https://db.internal"]
[component.auth-handler]
source = "target/wasm32-wasip1/release/auth_handler.wasm"
allowed_outbound_hosts = []
allowed_outbound_hostsフィールドは、ケーパビリティモデルが機能していることを示しています。auth-handlerはネットワークアクセスを一切持ちません。たとえJWT解析ロジックに脆弱性があったとしても、モジュールは外部に情報を送信できません。
ユーザーハンドラーのRustコンポーネント:
use spin_sdk::http::{IncomingRequest, ResponseBuilder};
use spin_sdk::http_component;
use spin_sdk::variables;
#[http_component]
async fn handle(req: IncomingRequest) -> anyhow::Result<impl IntoResponse> {
let db_url = variables::get("db_url")?;
// Spin's built-in SQLite or PostgreSQL via spin_sdk::pg
let conn = spin_sdk::pg::Connection::open(&db_url).await?;
let rows = conn.query("SELECT id, name FROM users LIMIT 20", &[]).await?;
let users: Vec<serde_json::Value> = rows.iter().map(|r| {
serde_json::json!({
"id": r.get::<i64>(0),
"name": r.get::<&str>(1)
})
}).collect();
Ok(ResponseBuilder::new(200)
.header("content-type", "application/json")
.body(serde_json::to_vec(&users)?)
.build())
}
Fermyon Cloudにデプロイするか、spin upでセルフホストします。サービス全体は.wasmファイルとTOMLのセットであり、Dockerfileもコンテナレジストリもありません。
PythonサービスへのWasmtimeの組み込み
既存のPythonサービスがあり、Wasmプラグインを安全に実行したい場合、例えばユーザーが実行する変換ロジックをアップロードできるようにしたい場合、WasmtimeのPythonバインディングが適切なツールです。
from wasmtime import Store, Module, Linker, WasiConfig
from pathlib import Path
class WasmPluginRunner:
def __init__(self, wasm_path: str):
self.store = Store()
wasi = WasiConfig()
wasi.inherit_stdout()
self.store.set_wasi(wasi)
self.linker = Linker(self.store.engine)
self.linker.define_wasi()
module = Module.from_file(self.store.engine, wasm_path)
self.instance = self.linker.instantiate(self.store, module)
def run_transform(self, input_data: bytes) -> bytes:
memory = self.instance.exports(self.store)["memory"]
transform = self.instance.exports(self.store)["transform"]
# Allocate in Wasm linear memory, call the function, read result back
alloc = self.instance.exports(self.store)["alloc"]
ptr = alloc(self.store, len(input_data))
memory.write(self.store, input_data, ptr)
result_ptr = transform(self.store, ptr, len(input_data))
# Read length prefix + data from result_ptr
result_len = int.from_bytes(
memory.read(self.store, result_ptr, result_ptr + 4), 'little'
)
return bytes(memory.read(self.store, result_ptr + 4, result_ptr + 4 + result_len))
プラグインの作成者は、Rustコードをwasm32-wasip2にコンパイルし、.wasmファイルを送ってきます。彼らはあなたの環境変数を読み取ったり、ネットワーク接続を開いたり、ファイルシステムにアクセスしたりすることはできません。サンドボックスがセキュリティモデルです。
パフォーマンスの現実的な確認
| ワークロード | V8 JS | ネイティブ | Wasm (Wasmtime) |
|---|---|---|---|
| SHA-256ハッシュ (1MB) | 12ms | 2.1ms | 2.4ms |
| JSONパース (10MB) | 89ms | — | 94ms (serdeを使用) |
| 画像リサイズ (Lanczos) | 340ms | 41ms | 48ms |
| コールドスタート (HTTPハンドラー) | 180ms (Node) | — | 0.3ms |
Wasmは、間接呼び出しのオーバーヘッドと境界チェックのため、計算量の多いタスクではネイティブよりも約5〜20%遅くなります。I/Oバウンドなほとんどのサーバーワークロードでは、この違いは問題になりません。コールドスタートの利点が、エッジ/サーバーレスデプロイメントにおける真の差別化要因です。
Wasmがまだ苦手なこと
以下の目的でWasmを使用しないでください。
- 長時間接続 — WebSocket、SSE。ほとんどのWasmランタイムはリクエスト/レスポンスパターンを中心に設計されています。
- 重いスレッド処理 — スレッドモデル(
wasm-threads)はブラウザではサポートされていますが、サーバーランタイムではサポートが不安定です。 - メモリを大量に消費するワークロード — 4GBの線形メモリ制限は今日では実用的な問題ではありませんが、単一のアドレス空間は仮想メモリのトリックが使えないことを意味します。
- 迅速なイテレーション — RustからWasmへのコンパイル時間はかなりかかります。毎時間変更されるスクリプトの場合、イテレーション速度ではPythonやJSが依然として優位です。
実用的な意思決定フレームワーク
以下のうち少なくとも2つが必要な場合は、本番環境でWasmを使用してください。
- サブミリ秒のコールドスタート (エッジ/サーバーレス)
- プラグインの分離 (信頼できないユーザーコード、マルチテナント)
- ポリグロットな構成 (Rustのパフォーマンスが重要なコア + Pythonの接着剤)
- 予測可能なパフォーマンス (ホットパスでのGC一時停止なし)
長時間の接続、成熟した可観測性ツール、またはチームの既存のDockerワークフローが必要な場合は、コンテナを使用してください。
こちらもどうぞ
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

WebAssembly (Wasm)がブラウザを超えて切り開く、コンピューティングの新時代
WebAssemblyがサーバーレスコンピューティングをどう変革しているか:Wasmtime、WASI 0.2 components、ミリ秒以下のコールドスタート、コンテナ代替パターンについて解説。
Read more
ブラウザを超えたWebAssembly: 高性能マイクロサービスの構築
Wasmtime、WasmEdge、Spinを使ってWebAssemblyをサーバーサイドで活用し、ネイティブに近い速度で言語に依存せず、機能がサンドボックス化されたマイクロサービスを構築する方法を、Dockerコンテナとの実測ベンチマークを交えて解説します。
Read more
フロントエンド開発者のためのRust: 実践的な移行ガイド
フロントエンド開発者がツールやWebAssemblyのためにRustを採用する理由と、JavaScript/TypeScriptからの思考モデルを移行する方法を解説します。
Read more