
WebAssembly(Wasm)が初めて発表されたとき、ブラウザにおけるJavaScriptの独占を最終的に打ち破る技術として歓迎されました。開発者はC++、Rust、Goをバイナリ形式にコンパイルし、ウェブ上でほぼネイティブの速度で実行できるようになりました。
2026年現在、Wasmの最も大きな影響はブラウザではなく、クラウドにあります。WebAssemblyはウェブ技術から、レイテンシに敏感なマルチテナント環境でコンテナに取って代わる、ユニバーサルでセキュア、かつ高速なコンピューティングランタイムへと進化しました。
この記事では実践的に、RustをWASIにコンパイルし、PythonにWasmランタイムを組み込み、Extismでサンドボックス化されたプラグインシステムを構築し、Wasmが実際にコンテナを凌駕する場面を理解します。
一般的な説明は「Wasmはコンテナよりも高速である」というものです。これは部分的にしか真実ではありません。正確な比較は以下の通りです。
| Dockerコンテナ | WebAssemblyモジュール | |
|---|---|---|
| 起動時間 | 500ms~2秒(コールド) | < 1ms |
| イメージサイズ | 50 MB – 1 GB | 1 MB – 10 MB |
| メモリベースライン | 30–100 MB | < 1 MB |
| セキュリティモデル | Linux名前空間/cgroups | 機能ベースのサンドボックス |
| 言語サポート | 任意(OS経由) | Wasmにコンパイル可能な任意言語 |
| ネットワーク | フルTCP/UDPスタック | WASIソケット(プレビュー2) |
| ファイルシステムアクセス | バインドマウント | 明示的な機能付与 |
起動時間の違いが決定的な要素です。Dockerコンテナの起動はLinuxカーネルが名前空間を設定することによって制限され、これを避けることはできません。Wasmモジュールのインスタンス化はメモリ割り当てとJITコンパイルであり、マイクロ秒単位で測定されます。これがCloudflare Workersがネットワーク上で5000万の同時Wasmインスタンスを実行できる理由です。
Wasmは元々I/Oを持っていませんでした。それは純粋な計算エンジンであり、ホストが明示的に機能を提供しない限り、ファイルを読み込んだり、ネットワークソケットを開いたり、現在の時刻を取得することさえできませんでした。
WASI (WebAssembly System Interface) は標準化されたシステムインターフェースを定義します。WASI Preview 2(2024年安定版)では、異なる言語のWasmモジュールが相互運用できるタイプセーフなABIであるコンポーネントモデルが導入されました。
Rustプログラムをwasm32-wasip2にコンパイルすると、.wasmバイナリが生成され、変更なしで任意のOSおよびアーキテクチャ上の任意のWASI準拠ランタイム(Wasmtime、WasmEdge、Wasmer)で実行できます。
# Install the WASI target
rustup target add wasm32-wasip2
# Create a simple HTTP handler
cargo new wasm-handler
cd wasm-handler
# Cargo.toml
[dependencies]
wasi = "0.13"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
// src/main.rs
use std::io::{self, Read, Write};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct Request {
name: String,
multiplier: u32,
}
#[derive(Serialize)]
struct Response {
message: String,
result: u64,
}
fn main() {
let mut input = String::new();
io::stdin().read_to_string(&mut input).unwrap();
let req: Request = serde_json::from_str(&input).unwrap_or(Request {
name: "world".to_string(),
multiplier: 1,
});
let response = Response {
message: format!("Hello, {}!", req.name),
result: fibonacci(req.multiplier as u64),
};
io::stdout()
.write_all(serde_json::to_string(&response).unwrap().as_bytes())
.unwrap();
}
fn fibonacci(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => {
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=n {
let c = a + b;
a = b;
b = c;
}
b
}
}
}
cargo build --target wasm32-wasip2 --release
# Output: target/wasm32-wasip2/release/wasm-handler.wasm (< 500 KB)
# Run with wasmtime
echo '{"name":"developer","multiplier":30}' | wasmtime target/wasm32-wasip2/release/wasm-handler.wasm
# Output: {"message":"Hello, developer!","result":832040}
結果として得られる.wasmファイルは、Linux x86、macOS ARM、Windows x86、および任意のエッジPoPで実行されます。再コンパイルなしで同一のバイナリです。
Wasmは、任意のホスト言語内でプラグインランタイムとして使用できます。これは最も強力なパターンの一つです。コアアプリケーションはPythonですが、パフォーマンスが重要なロジックやユーザーが送信したロジックはサンドボックス化されたWasmで実行されます。
pip install wasmtime
# host.py
from wasmtime import Store, Module, Instance, Linker, WasiConfig
import json
def run_wasm_handler(wasm_path: str, input_data: dict) -> dict:
store = Store()
# Configure WASI capabilities - deny-by-default
wasi_config = WasiConfig()
# We do NOT grant filesystem or network access - pure computation only
store.set_wasi(wasi_config)
linker = Linker(store.engine)
linker.define_wasi()
module = Module.from_file(store.engine, wasm_path)
instance = linker.instantiate(store, module)
# The WASM module reads from stdin and writes to stdout
# For real production use, consider wasmtime's memory API for direct buffer passing
import subprocess
result = subprocess.run(
["wasmtime", wasm_path],
input=json.dumps(input_data).encode(),
capture_output=True,
timeout=5, # hard timeout - Wasm can't escape it
)
return json.loads(result.stdout)
result = run_wasm_handler(
"target/wasm32-wasip2/release/wasm-handler.wasm",
{"name": "Python host", "multiplier": 40}
)
print(result) # {"message": "Hello, Python host!", "result": 102334155}
timeout=5はOSによって強制されます。悪意のあるWasmモジュールがこれを回避することはできません。また、WasiConfigがそれらの機能を付与しなかったため、モジュールはファイルシステムやネットワークにアクセスすることもできません。
Extismは、Wasmプラグインをホストアプリケーションに組み込む最もクリーンな方法です。ホストとWasm間で文字列/バイトを渡すABIの複雑さを処理します。
cargo install extism-pdk
cargo new --lib my-plugin
cd my-plugin
# Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
extism-pdk = "1"
serde = { version = "1", features = ["derive"] }
// src/lib.rs
use extism_pdk::*;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct Input {
text: String,
max_words: usize,
}
#[derive(Serialize)]
struct Output {
summary: String,
word_count: usize,
}
#[plugin_fn]
pub fn summarize(input: Json<Input>) -> FnResult<Json<Output>> {
let words: Vec<&str> = input.text.split_whitespace().collect();
let truncated = words[..words.len().min(input.max_words)].join(" ");
Ok(Json(Output {
summary: if words.len() > input.max_words {
format!("{}...", truncated)
} else {
truncated
},
word_count: words.len(),
}))
}
cargo build --target wasm32-unknown-unknown --release
# host.py
import extism
import json
manifest = {"wasm": [{"path": "target/wasm32-unknown-unknown/release/my_plugin.wasm"}]}
with extism.Plugin(manifest) as plugin:
result = plugin.call(
"summarize",
json.dumps({"text": "WebAssembly is a portable compilation target...", "max_words": 5})
)
print(json.loads(result))
# {"summary": "WebAssembly is a portable compilation...", "word_count": 8}
プラグインはWasmサンドボックス内で実行されます。以下のことはできません。
これは、ユーザーが送信したコードを大規模に安全に実行するためのパターンです。各テナントのプラグインは他のすべてのプラグインから隔離されて実行されます。
従来のサーバーレス(AWS Lambda、Google Cloud Functions)は、コンテナの初期化に500ms~2秒かかるため、コールドスタートに悩まされていました。Wasmネイティブプラットフォームはこれを解消します。
spin deployはWasmバイナリをマネージドプラットフォームにデプロイします。Dockerfileもコンテナレジストリも不要です。# Deploy a Rust Axum app to Fermyon Spin
spin new -t http-rust my-app
cd my-app
spin build
spin deploy
Stripe、Shopify、Figmaはすべてプラグインを許可しています。従来のやり方では、ユーザーに特定のDSLで記述させるか、外部のWebhookエンドポイントをホストさせる必要がありました。
Wasmを使用すると、ユーザーは任意の言語でプラグインを記述し、.wasmファイルをアップロードするだけで、プラットフォームがサンドボックス内でそれを実行します。管理するインフラも、手動で強制する信頼境界もありません。
ONNX RuntimeはWasmバックエンドを搭載するようになりました。GPUサーバーを起動することなく、量子化されたMLモデルをエッジで実行できます。
# Run inference at the edge
wasm-pack build --target web
# Deploy to Cloudflare Workers - inference runs in each PoP
WASI Preview 2のコンポーネントモデルは、歴史的な課題であった「Wasmモジュールは生バイトしか交換できなかった」という問題を解決します。RustモジュールからPythonモジュールに文字列を渡すには、手動でのポインタ演算が必要でした。
コンポーネントモデルは、タイプセーフなインターフェース記述言語(WIT - WebAssembly Interface Types)を定義します。インターフェースを一度記述するだけで済みます。
// my-interface.wit
package my:plugin;
interface transform {
summarize: func(text: string, max-words: u32) -> string;
}
その後、任意の言語でバインディングを生成します。summarizeを実装するRustプラグインは、Goホスト、Pythonホスト、またはJavaScriptホストから、手動のシリアライズコードなしで呼び出すことができます。
Wasmを使用する場合:
Dockerを使用し続ける場合:
WasmとDockerは相互に排他的ではありません。多くの本番環境アーキテクチャでは、オーケストレーション層にコンテナを使用し、それらのコンテナ内のコンピューティング層にWasmを使用しています。
あるスタートアップが、高並行でイベント駆動型のマイクロサービス向けに新しいサーバーレスプラットフォームを構築しています。彼らは、数百万の短命な関数に対するコールドスタート時間とリソースオーバーヘッドを最小限に抑える必要があります。提供された比較に基づいて、彼らの計算ランタイムに最も適切な技術と、その理由は何ですか?
AI CLIログの整形、Cron式の作成、JWTの解析、chmod権限の計算をオフラインで実行。

WebAssembly (Wasm)とWASI 0.2が、マイクロ秒のコールドスタート、能力ベースのセキュリティ、Rustコンポーネントにより、エッジコンピューティングをどのように再定義しているかを探ります。
続きを読む
WebAssembly SIMDによる高性能Rustのアーキテクチャガイドで、実証済みの本番環境での例を用いてブラウザ計算を10倍高速化する方法を詳しく解説します。
続きを読む
Wasmtime、WasmEdge、Spinを使ってWebAssemblyをサーバーサイドで活用し、ネイティブに近い速度で言語に依存せず、機能がサンドボックス化されたマイクロサービスを構築する方法を、Dockerコンテナとの実測ベンチマークを交えて解説します。
続きを読む