Split View: WebAssembly 완전 가이드 2025: 브라우저를 넘어서 — WASI, Component Model, 서버사이드 Wasm
WebAssembly 완전 가이드 2025: 브라우저를 넘어서 — WASI, Component Model, 서버사이드 Wasm
목차
1. WebAssembly란 무엇인가
WebAssembly(Wasm)는 스택 기반 가상 머신을 위한 바이너리 명령어 포맷이다. 원래 웹 브라우저에서 네이티브에 가까운 성능을 제공하기 위해 설계되었지만, 이제는 브라우저를 넘어 서버, 엣지, IoT까지 확장되고 있다.
1.1 Wasm의 핵심 특성
| 특성 | 설명 | 의미 |
|---|---|---|
| 바이너리 포맷 | 컴팩트한 바이너리 인코딩 | 빠른 전송, 빠른 디코딩 |
| 스택 머신 | 스택 기반 명령어 실행 | 단순하고 효율적인 실행 모델 |
| 선형 메모리 | 연속된 바이트 배열 | 안전한 메모리 접근, 샌드박스 |
| 타입 안전 | 강타입 시스템 | 컴파일 시 검증, 런타임 안전 |
| 이식성 | 아키텍처 독립적 | x86, ARM, RISC-V 어디서나 실행 |
| 샌드박스 | 격리된 실행 환경 | 호스트 시스템 보호 |
1.2 Wasm의 역사
2015: WebAssembly 프로젝트 발표 (W3C)
2017: 4대 브라우저에서 Wasm 1.0 지원
2019: WASI 초기 제안 (비브라우저 Wasm)
2020: Wasm 1.0이 W3C 공식 권고안
2021: Component Model 제안, Fermyon 창립
2022: Docker+Wasm 기술 프리뷰, WASI Preview 1
2023: WASI 0.2 안정화, Component Model 성숙
2024: GC proposal 구현, 주요 런타임 최적화
2025: WASI 0.2 Production-ready, Wasm 서버 본격 채택
1.3 Wasm 바이너리 구조
Wasm 모듈 구조:
+------------------+
| Magic Number | 0x00 0x61 0x73 0x6D ("\0asm")
| Version | 0x01 0x00 0x00 0x00 (v1)
+------------------+
| Type Section | 함수 시그니처 정의
| Import Section | 외부 함수/메모리 임포트
| Function Section | 함수 인덱스
| Table Section | 간접 호출 테이블
| Memory Section | 선형 메모리 정의
| Global Section | 전역 변수
| Export Section | 외부 공개 함수/메모리
| Start Section | 자동 실행 함수
| Element Section | 테이블 초기화
| Code Section | 함수 본문 (바이트코드)
| Data Section | 메모리 초기 데이터
+------------------+
1.4 WAT (WebAssembly Text Format)
Wasm의 텍스트 표현 형식인 WAT를 이해하면 내부 동작을 파악할 수 있다.
;; 두 수를 더하는 간단한 Wasm 모듈
(module
;; 함수 타입 정의
(type $add_type (func (param i32 i32) (result i32)))
;; 함수 구현
(func $add (type $add_type) (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
;; 선형 메모리 (1 페이지 = 64KB)
(memory (export "memory") 1)
;; 함수 내보내기
(export "add" (func $add))
)
2. 브라우저 Wasm
2.1 다양한 언어에서 Wasm으로 컴파일
Rust - Wasm 1등 시민
// lib.rs - Rust에서 Wasm으로
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
#[wasm_bindgen]
pub struct ImageProcessor {
width: u32,
height: u32,
pixels: Vec<u8>,
}
#[wasm_bindgen]
impl ImageProcessor {
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> ImageProcessor {
ImageProcessor {
width,
height,
pixels: vec![0; (width * height * 4) as usize],
}
}
pub fn grayscale(&mut self) {
for chunk in self.pixels.chunks_exact_mut(4) {
let gray = (0.299 * chunk[0] as f64
+ 0.587 * chunk[1] as f64
+ 0.114 * chunk[2] as f64) as u8;
chunk[0] = gray;
chunk[1] = gray;
chunk[2] = gray;
}
}
pub fn pixels_ptr(&self) -> *const u8 {
self.pixels.as_ptr()
}
}
# Rust -> Wasm 빌드
wasm-pack build --target web
C/C++ - Emscripten
// image_filter.c - C에서 Wasm으로
#include <emscripten.h>
#include <stdint.h>
EMSCRIPTEN_KEEPALIVE
void apply_blur(uint8_t* pixels, int width, int height) {
uint8_t* temp = (uint8_t*)malloc(width * height * 4);
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
for (int c = 0; c < 3; c++) {
int sum = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
sum += pixels[((y+dy)*width + (x+dx))*4 + c];
}
}
temp[(y*width + x)*4 + c] = sum / 9;
}
temp[(y*width + x)*4 + 3] = pixels[(y*width + x)*4 + 3];
}
}
memcpy(pixels, temp, width * height * 4);
free(temp);
}
# C -> Wasm 빌드
emcc image_filter.c -O3 -s WASM=1 \
-s EXPORTED_FUNCTIONS='["_apply_blur"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap"]' \
-o image_filter.js
2.2 JavaScript와의 상호 운용
// Wasm 모듈 로드 및 사용
async function initWasm() {
// 방법 1: fetch + instantiate
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
// 호스트 함수를 Wasm에 제공
log_value: (value) => console.log('Wasm says:', value),
get_time: () => Date.now(),
}
});
// Wasm 함수 호출
const result = instance.exports.add(10, 20);
console.log('Result:', result); // 30
// 선형 메모리 접근
const memory = new Uint8Array(instance.exports.memory.buffer);
// 메모리에서 데이터 읽기/쓰기
return instance;
}
// 방법 2: wasm-bindgen (Rust)
import init, { fibonacci, ImageProcessor } from './pkg/my_wasm.js';
async function main() {
await init();
console.log('fib(40):', fibonacci(40));
const processor = new ImageProcessor(800, 600);
processor.grayscale();
}
2.3 브라우저 Wasm 주요 사용 사례
| 사용 사례 | 예시 | 성능 향상 |
|---|---|---|
| 이미지/비디오 처리 | Photoshop Web, FFmpeg.wasm | 10-50x JS 대비 |
| 게임 엔진 | Unity WebGL, Unreal Engine | 네이티브의 70-90% |
| 코덱/압축 | AV1 디코딩, Brotli 압축 | 5-20x JS 대비 |
| 암호화 | SHA-256, AES 연산 | 3-10x JS 대비 |
| CAD/3D 모델링 | AutoCAD Web, SketchUp | 네이티브 수준 |
| 과학 계산 | 시뮬레이션, 데이터 분석 | 10-100x JS 대비 |
| PDF 처리 | pdf.js 가속, 렌더링 | 3-5x 향상 |
3. WASI 0.2: 시스템 인터페이스
WASI(WebAssembly System Interface)는 Wasm이 브라우저 밖에서 파일시스템, 네트워크 등 시스템 리소스에 안전하게 접근하기 위한 표준이다.
3.1 WASI 0.2 인터페이스
WASI 0.2 구조:
+----------------------------------+
| wasi:cli (CLI 앱 지원) |
| wasi:http (HTTP 클라이언트/서버) |
| wasi:filesystem (파일시스템 접근) |
| wasi:sockets (네트워크 소켓) |
| wasi:clocks (시간 관련) |
| wasi:random (난수 생성) |
| wasi:io (스트림 I/O) |
+----------------------------------+
| Component Model (기반 레이어) |
+----------------------------------+
| Wasm Core (실행 엔진) |
+----------------------------------+
3.2 WASI HTTP 서버 (Rust)
// Rust + WASI HTTP 서버
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct TodoItem {
id: u32,
title: String,
completed: bool,
}
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
let method = req.method().as_str();
let path = req.path();
match (method, path) {
("GET", "/api/todos") => {
let todos = vec![
TodoItem { id: 1, title: "Learn WASI".into(), completed: false },
TodoItem { id: 2, title: "Build Wasm app".into(), completed: false },
];
Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(serde_json::to_string(&todos)?)
.build())
},
("POST", "/api/todos") => {
let body: TodoItem = serde_json::from_slice(req.body())?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(serde_json::to_string(&body)?)
.build())
},
_ => Ok(Response::builder()
.status(404)
.body("Not Found")
.build())
}
}
3.3 Capability-Based 보안 모델
WASI는 Capability-Based Security를 핵심으로 한다. 프로그램은 명시적으로 부여받은 권한만 사용할 수 있다.
# WASI 실행 시 권한 부여 예시
# 특정 디렉터리만 읽기 허용
wasmtime run --dir /data::/data:readonly my-app.wasm
# 네트워크 접근 허용
wasmtime run --tcplisten 0.0.0.0:8080 my-server.wasm
# 환경 변수 전달
wasmtime run --env KEY=VALUE my-app.wasm
기존 보안 모델 (Ambient Authority):
프로세스 -> [전체 파일시스템 접근 가능]
-> [모든 네트워크 접근 가능]
-> [환경 변수 모두 읽기 가능]
WASI 보안 모델 (Capability-Based):
Wasm 모듈 -> [/data 디렉터리만 읽기 가능]
-> [localhost:8080만 리슨 가능]
-> [지정된 환경 변수만 접근 가능]
4. Component Model: 모듈 조합
Component Model은 Wasm 모듈을 조합하여 복잡한 애플리케이션을 구성하는 표준이다.
4.1 WIT (Wasm Interface Type)
// todo.wit - 인터페이스 정의
package example:todo@0.1.0;
interface types {
record todo-item {
id: u32,
title: string,
completed: bool,
}
variant todo-error {
not-found(string),
validation-error(string),
storage-error(string),
}
}
interface todo-store {
use types.{todo-item, todo-error};
list-todos: func() -> result<list<todo-item>, todo-error>;
get-todo: func(id: u32) -> result<todo-item, todo-error>;
create-todo: func(title: string) -> result<todo-item, todo-error>;
update-todo: func(id: u32, title: string, completed: bool) -> result<todo-item, todo-error>;
delete-todo: func(id: u32) -> result<_, todo-error>;
}
world todo-app {
import wasi:http/outgoing-handler@0.2.0;
import wasi:logging/logging;
export todo-store;
}
4.2 Component 조합
# 여러 컴포넌트를 조합하여 하나의 앱 구성
# 1. 각 컴포넌트 빌드
cargo component build --release # Rust 컴포넌트
componentize-py -d todo.wit -w todo-app app.py # Python 컴포넌트
# 2. 컴포넌트 조합
wasm-tools compose \
--definitions auth-component.wasm \
--definitions db-component.wasm \
app-component.wasm \
-o composed-app.wasm
# 3. 조합된 컴포넌트 실행
wasmtime serve composed-app.wasm
4.3 Component Model의 장점
| 장점 | 설명 |
|---|---|
| 언어 독립적 조합 | Rust 모듈 + Python 모듈 + Go 모듈을 하나로 |
| 타입 안전한 인터페이스 | WIT로 정의된 계약 보장 |
| 샌드박스 격리 | 컴포넌트 간 메모리 격리 |
| 경량 | 컨테이너 대비 수십 배 가벼움 |
| 빠른 시작 | 마이크로초 단위 초기화 |
| 이식성 | 어디서나 실행 가능 |
5. 서버사이드 Wasm 프레임워크
5.1 Fermyon Spin
Spin은 Wasm 기반 마이크로서비스를 빌드하기 위한 프레임워크다.
# spin.toml - Spin 앱 설정
spin_manifest_version = 2
[application]
name = "my-api"
version = "1.0.0"
description = "Wasm 기반 API 서버"
[[trigger.http]]
route = "/api/hello"
component = "hello"
[[trigger.http]]
route = "/api/users/..."
component = "users"
[component.hello]
source = "target/wasm32-wasi/release/hello.wasm"
allowed_outbound_hosts = []
[component.users]
source = "target/wasm32-wasi/release/users.wasm"
allowed_outbound_hosts = ["https://api.example.com"]
key_value_stores = ["default"]
sqlite_databases = ["default"]
// Spin HTTP 핸들러 (Rust)
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use spin_sdk::key_value::Store;
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
// Key-Value 스토어 사용
let store = Store::open_default()?;
match req.method().as_str() {
"GET" => {
let key = req.path().trim_start_matches("/api/users/");
match store.get(key)? {
Some(value) => Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(value)
.build()),
None => Ok(Response::builder()
.status(404)
.body("Not found")
.build()),
}
},
"POST" => {
let body = req.body().to_vec();
let key = format!("user:{}", uuid::Uuid::new_v4());
store.set(&key, &body)?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(body)
.build())
},
_ => Ok(Response::builder()
.status(405)
.body("Method not allowed")
.build()),
}
}
# Spin 앱 빌드 및 실행
spin build
spin up # 로컬 실행 (포트 3000)
# Fermyon Cloud에 배포
spin deploy
5.2 wasmCloud
wasmCloud는 분산 Wasm 애플리케이션 플랫폼이다.
# wadm.yaml - wasmCloud 앱 매니페스트
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: my-api
annotations:
description: "wasmCloud HTTP API"
spec:
components:
- name: api-handler
type: component
properties:
image: ghcr.io/myorg/api-handler:0.1.0
traits:
- type: spreadscaler
properties:
instances: 5
- type: link
properties:
target: httpserver
namespace: wasi
package: http
interfaces: [incoming-handler]
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.22.0
traits:
- type: link
properties:
target: api-handler
namespace: wasi
package: http
interfaces: [incoming-handler]
source_config:
- name: default-http
properties:
address: 0.0.0.0:8080
5.3 서버사이드 Wasm 프레임워크 비교
| 특성 | Fermyon Spin | wasmCloud | Lunatic |
|---|---|---|---|
| 초점 | HTTP 마이크로서비스 | 분산 애플리케이션 | 액터 모델 |
| 배포 모델 | 단일 노드 / 클라우드 | 분산 격자 (lattice) | 클러스터 |
| 스토리지 | KV, SQLite, Redis | Capability Provider | 내장 |
| 언어 지원 | Rust, Go, JS, Python | Rust, Go, AssemblyScript | Rust, AssemblyScript |
| 콜드 스타트 | 1ms 미만 | 1ms 미만 | 수 ms |
| 성숙도 | GA | GA | 초기 |
| 클라우드 서비스 | Fermyon Cloud | Cosmonic | 없음 |
6. Docker + Wasm
Docker Desktop은 Wasm 컨테이너를 네이티브로 지원한다.
6.1 Docker Wasm 아키텍처
기존 Linux 컨테이너:
Docker Engine -> containerd -> runc -> Linux Container
(full OS, 수십~수백 MB)
Wasm 컨테이너:
Docker Engine -> containerd -> runwasi -> Wasm Runtime (wasmtime/wasmedge)
(Wasm 모듈, 수 MB)
6.2 Wasm Docker 이미지 빌드
# Dockerfile.wasm - Wasm 컨테이너 이미지
FROM scratch
COPY target/wasm32-wasi/release/my-app.wasm /my-app.wasm
ENTRYPOINT ["/my-app.wasm"]
# Wasm 이미지 빌드 및 실행
docker buildx build --platform wasi/wasm -t my-wasm-app .
docker run --runtime=io.containerd.wasmtime.v1 \
--platform wasi/wasm \
-p 8080:8080 \
my-wasm-app
6.3 Docker Compose with Wasm
# docker-compose.yml - Wasm + Linux 컨테이너 혼합
version: "3"
services:
# Wasm 서비스 (초경량)
api:
image: my-wasm-api:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
# 전통적인 Linux 컨테이너
postgres:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
# Wasm 워커 서비스
worker:
image: my-wasm-worker:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
environment:
- QUEUE_URL=redis://redis:6379
redis:
image: redis:7-alpine
volumes:
pgdata:
6.4 Wasm vs Linux 컨테이너 비교
| 특성 | Linux 컨테이너 | Wasm 컨테이너 |
|---|---|---|
| 이미지 크기 | 50MB-1GB | 1-10MB |
| 시작 시간 | 100ms-수초 | 1ms 미만 |
| 메모리 사용 | 50MB-수GB | 1-50MB |
| 보안 모델 | 네임스페이스/cgroup | 샌드박스/Capability |
| 이식성 | Linux 커널 필요 | 아키텍처 독립 |
| 프로세스 격리 | OS 수준 | 언어 수준 |
| 네트워킹 | 완전 지원 | WASI 소켓 (제한적) |
| 파일시스템 | 완전 지원 | WASI FS (제한적) |
7. Edge Wasm
7.1 Cloudflare Workers
// Cloudflare Worker (Wasm 기반)
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === '/api/compute') {
// Wasm 모듈로 무거운 계산 수행
const wasmModule = await import('./compute.wasm');
const result = wasmModule.heavy_computation(42);
return new Response(JSON.stringify({ result }), {
headers: { 'Content-Type': 'application/json' }
});
}
// KV 스토어 사용
if (url.pathname.startsWith('/api/cache/')) {
const key = url.pathname.split('/').pop();
if (request.method === 'GET') {
const value = await env.MY_KV.get(key);
return new Response(value || 'Not found', {
status: value ? 200 : 404
});
}
if (request.method === 'PUT') {
const body = await request.text();
await env.MY_KV.put(key, body, { expirationTtl: 3600 });
return new Response('OK');
}
}
return new Response('Not found', { status: 404 });
}
};
7.2 Fastly Compute
// Fastly Compute (Rust + Wasm)
use fastly::{Error, Request, Response};
use fastly::http::StatusCode;
#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
let path = req.get_path().to_string();
match path.as_str() {
"/api/hello" => {
Ok(Response::from_status(StatusCode::OK)
.with_body_text_plain("Hello from Fastly Compute!"))
},
path if path.starts_with("/api/proxy/") => {
// 백엔드로 프록시
let backend_path = path.trim_start_matches("/api/proxy");
let mut backend_req = req;
backend_req.set_path(backend_path);
backend_req.send("origin_backend")
},
_ => {
Ok(Response::from_status(StatusCode::NOT_FOUND)
.with_body_text_plain("Not found"))
}
}
}
7.3 Edge Wasm 플랫폼 비교
| 플랫폼 | 런타임 | 언어 | 콜드 스타트 | 무료 티어 |
|---|---|---|---|---|
| Cloudflare Workers | V8 + Wasm | JS, Rust, C++ | 0ms (상시 로드) | 10만 요청/일 |
| Fastly Compute | Wasmtime | Rust, Go, JS | 수 ms | 없음 |
| Vercel Edge Functions | V8 + Wasm | JS, TS | 0ms (상시 로드) | 100만 실행/월 |
| Deno Deploy | V8 + Wasm | JS, TS, Wasm | 0ms | 10만 요청/일 |
| Fermyon Cloud | Spin | Rust, Go, JS, Python | 1ms 미만 | 무료 베타 |
8. 언어별 Wasm 지원
8.1 Rust - 1등 시민
Rust는 Wasm의 가장 강력한 언어 지원을 제공한다.
// Cargo.toml
[package]
name = "my-wasm-app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.25"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# 빌드 타겟
rustup target add wasm32-wasi # WASI 타겟
rustup target add wasm32-unknown-unknown # 브라우저 타겟
# 빌드
cargo build --target wasm32-wasi --release
# Component로 변환
wasm-tools component new target/wasm32-wasi/release/my_app.wasm \
-o my_app.component.wasm
8.2 Go - TinyGo
// main.go - TinyGo Wasm
package main
import (
"encoding/json"
"net/http"
spinhttp "github.com/fermyon/spin/sdk/go/v2/http"
)
type Response struct {
Message string `json:"message"`
Count int `json:"count"`
}
func init() {
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
resp := Response{
Message: "Hello from Go Wasm!",
Count: 42,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
})
}
func main() {}
# TinyGo로 Wasm 빌드
tinygo build -target=wasi -o main.wasm main.go
8.3 Python - componentize-py
# app.py - Python Wasm (Spin)
from spin_sdk.http import IncomingHandler, Request, Response
import json
class IncomingHandler(IncomingHandler):
def handle_request(self, request: Request) -> Response:
if request.method == "GET" and request.uri == "/api/hello":
body = json.dumps({
"message": "Hello from Python Wasm!",
"path": request.uri,
})
return Response(
200,
{"content-type": "application/json"},
bytes(body, "utf-8")
)
return Response(404, {}, b"Not found")
# Python -> Wasm 컴포넌트
componentize-py -d spin-http.wit -w spin-http-trigger app -o app.wasm
8.4 JavaScript - jco
// app.js - JavaScript Wasm Component
import { handle } from 'wasi:http/incoming-handler@0.2.0';
export const incomingHandler = {
handle(request, responseOut) {
const headers = new Headers();
headers.set('Content-Type', 'application/json');
const body = JSON.stringify({
message: 'Hello from JS Wasm!',
method: request.method(),
path: request.pathWithQuery(),
});
const response = new OutgoingResponse(headers);
response.setStatusCode(200);
const bodyStream = response.body();
bodyStream.write(new TextEncoder().encode(body));
bodyStream.close();
responseOut.set(response);
}
};
# JavaScript -> Wasm Component
jco componentize app.js -w wasi-http.wit -o app.wasm
8.5 언어 지원 비교
| 언어 | Wasm 성숙도 | WASI 지원 | Component Model | 바이너리 크기 | 비고 |
|---|---|---|---|---|---|
| Rust | 매우 높음 | 완전 | 완전 | 1-5MB | 1등 시민 |
| C/C++ | 높음 | 완전 | 부분적 | 0.5-3MB | Emscripten |
| Go (TinyGo) | 중간 | 완전 | 완전 | 2-10MB | 표준 Go 일부 미지원 |
| Python | 중간 | 완전 | 완전 | 10-30MB | componentize-py |
| JavaScript | 중간 | 완전 | 완전 | 5-15MB | jco |
| C# (.NET) | 중간 | 부분적 | 초기 | 10-50MB | NativeAOT-LLVM |
| Swift | 초기 | 부분적 | 초기 | 5-20MB | SwiftWasm |
| Kotlin | 초기 | 부분적 | 초기 | 10-30MB | Kotlin/Wasm |
9. 성능 비교
9.1 콜드 스타트 비교
| 플랫폼 | 콜드 스타트 | 메모리 사용 | 비고 |
|---|---|---|---|
| Wasm (Spin) | 0.5-1ms | 2-10MB | 마이크로초 수준 |
| Wasm (wasmtime) | 1-5ms | 5-20MB | 일반적 |
| Lambda (Python) | 150-300ms | 50-128MB | VPC 추가 시 더 느림 |
| Lambda (Java) | 800-3000ms | 128-512MB | SnapStart로 개선 가능 |
| Docker Container | 500-5000ms | 50MB-1GB | 이미지 크기 의존 |
| Cloud Run | 300-2000ms | 128MB-8GB | 인스턴스 타입 의존 |
9.2 처리량 비교
HTTP "Hello World" 벤치마크 (단일 인스턴스):
Spin (Rust/Wasm): ~150,000 req/s
Native Rust (Actix): ~200,000 req/s
Go (net/http): ~120,000 req/s
Node.js (Express): ~30,000 req/s
Python (FastAPI): ~10,000 req/s
Wasm은 네이티브의 70-90% 성능을 달성
9.3 메모리 효율성
단일 HTTP 서비스 메모리 사용:
Wasm (Spin): 2-5 MB
Wasm (wasmtime): 5-15 MB
Node.js: 30-80 MB
Python: 30-60 MB
Java (JVM): 100-300 MB
.NET: 50-150 MB
Go: 10-30 MB
1GB 메모리에서 동시 서비스 수:
Wasm: ~200개 서비스
Node.js: ~15개 서비스
Java: ~5개 서비스
10. 보안 모델
10.1 Wasm 샌드박스
전통적인 프로세스:
+------------------------------------------+
| 프로세스 |
| - 전체 파일시스템 접근 |
| - 모든 네트워크 접근 |
| - 시스템 콜 무제한 |
| - 다른 프로세스 메모리 접근 (exploit 시) |
+------------------------------------------+
Wasm 샌드박스:
+------------------------------------------+
| Wasm 모듈 |
| +------------------------------------+ |
| | 선형 메모리 (모듈 전용) | |
| | - 경계 검사 (bounds checking) | |
| | - 호스트 메모리 접근 불가 | |
| +------------------------------------+ |
| | 허가된 시스템 인터페이스만 사용 | |
| | - wasi:filesystem (지정 경로만) | |
| | - wasi:sockets (지정 주소만) | |
| +------------------------------------+ |
+------------------------------------------+
10.2 Capability-Based Security 원칙
| 원칙 | 설명 | 예시 |
|---|---|---|
| No Ambient Authority | 기본 권한 없음 | 파일시스템, 네트워크 접근 불가 |
| Principle of Least Privilege | 최소 권한만 부여 | 특정 디렉터리만 접근 허용 |
| Explicit Capability Passing | 명시적 권한 전달 | 호스트가 핸들을 전달 |
| Revocable | 권한 회수 가능 | 런타임에 권한 제거 |
10.3 보안 비교
| 보안 측면 | 컨테이너 | Wasm |
|---|---|---|
| 격리 수준 | OS 수준 (cgroup/namespace) | 언어 수준 (바이트코드 검증) |
| 메모리 안전 | 커널에 의존 | 경계 검사 내장 |
| 시스템 콜 | seccomp으로 필터링 | WASI로 제한 |
| 탈출 가능성 | 커널 취약점 시 가능 | 매우 어려움 |
| 권한 모델 | 유저/그룹 기반 | Capability 기반 |
| 코드 검증 | 없음 | 로드 시 검증 |
11. 실전 사용 사례
11.1 플러그인 시스템
// 호스트 애플리케이션 - 플러그인 로더
use wasmtime::*;
use wasmtime_wasi::WasiCtxBuilder;
fn load_plugin(plugin_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
// 플러그인에 최소 권한만 부여
let wasi = WasiCtxBuilder::new()
.inherit_stdout() // stdout만 허용
// 파일시스템 접근 없음
// 네트워크 접근 없음
.build();
let mut store = Store::new(&engine, wasi);
let module = Module::from_file(&engine, plugin_path)?;
let instance = linker.instantiate(&mut store, &module)?;
// 플러그인 함수 호출
let process = instance
.get_typed_func::<(i32,), i32>(&mut store, "process")?;
let result = process.call(&mut store, (42,))?;
println!("Plugin result: {}", result);
Ok(())
}
11.2 Edge Computing
Edge Wasm 아키텍처:
사용자 요청 -> [CDN Edge (300+ PoP)]
|
v
[Wasm Worker]
|
+-> 캐시 히트: 즉시 응답 (0ms)
|
+-> 캐시 미스: 원본 서버에 요청
| |
| v
| [Origin Server]
| |
v v
[응답 + 캐시 저장]
장점:
- 사용자에게 가장 가까운 위치에서 실행
- 콜드 스타트 거의 없음
- 글로벌 분산 자동 배포
11.3 Serverless Functions
Lambda vs Wasm Serverless:
AWS Lambda:
요청 -> API Gateway -> Lambda Runtime -> 함수 실행
콜드 스타트: 100ms-8s
메모리: 128MB-10GB
과금: 1ms 단위
Wasm Serverless (Spin/Fermyon):
요청 -> Spin Runtime -> Wasm 모듈 실행
콜드 스타트: 0.5ms 이하
메모리: 2-50MB
과금: 요청 단위
11.4 블록체인 스마트 컨트랙트
여러 블록체인이 Wasm을 스마트 컨트랙트 실행 엔진으로 채택했다.
| 블록체인 | Wasm 런타임 | 언어 지원 |
|---|---|---|
| Polkadot | Substrate | Rust (ink!) |
| Near | Wasmer | Rust, AssemblyScript |
| Cosmos (CosmWasm) | Wasmer | Rust |
| Dfinity (ICP) | 커스텀 | Rust, Motoko |
12. Wasm vs Container 비교
12.1 종합 비교
| 특성 | Wasm | Container |
|---|---|---|
| 시작 시간 | 마이크로초-밀리초 | 밀리초-초 |
| 이미지 크기 | 1-50MB | 50MB-수GB |
| 메모리 사용 | 1-50MB | 50MB-수GB |
| CPU 오버헤드 | 0-30% | 0-5% |
| 보안 | 강한 샌드박스 | OS 수준 격리 |
| 네트워킹 | WASI (제한적) | 완전 지원 |
| 파일시스템 | WASI (제한적) | 완전 지원 |
| GPU 지원 | 실험적 | 완전 지원 |
| 에코시스템 | 성장 중 | 매우 성숙 |
| 디버깅 | 발전 중 | 성숙 |
| 상태 관리 | Stateless 권장 | Stateful 가능 |
12.2 언제 Wasm을 선택하나
Wasm 선택 기준:
- 밀리초 이하 콜드 스타트 필요
- 극한의 경량 서비스
- 플러그인/확장 시스템
- Edge 컴퓨팅
- 다중 언어 모듈 조합
- 강한 샌드박스 격리 필요
- 제한된 리소스 환경 (IoT)
Container 선택 기준:
- 복잡한 네트워킹
- GPU 사용
- 레거시 애플리케이션
- 풍부한 에코시스템 필요
- 상태 보존 필요
- 대용량 메모리/스토리지
13. 미래 전망
13.1 진행 중인 Wasm 제안
| 제안 | 상태 | 설명 |
|---|---|---|
| GC (Garbage Collection) | Phase 4 | Java, Kotlin, Dart 등 GC 언어 지원 |
| Threads | Phase 3 | 공유 메모리 멀티스레딩 |
| SIMD | Phase 4 (완료) | 128비트 SIMD 연산 |
| Exception Handling | Phase 4 | try/catch 네이티브 지원 |
| Stack Switching | Phase 2 | 코루틴, async/await 지원 |
| Memory64 | Phase 3 | 64비트 메모리 주소 |
| Branch Hinting | Phase 3 | 분기 예측 힌트 |
| Tail Call | Phase 4 (완료) | 꼬리 호출 최적화 |
13.2 Wasm 미래 로드맵
2025-2026 전망:
- WASI 0.2 완전 안정화 및 주요 클라우드 채택
- Component Model 생태계 성숙
- Docker Wasm 프로덕션 레디
- GC proposal 주요 런타임 구현
- Threads proposal 안정화
2027+ 전망:
- Wasm이 컨테이너와 동등한 위상
- Edge-first 아키텍처의 기본 런타임
- IoT/임베디드 표준 실행 환경
- 다중 언어 컴포넌트 생태계 확립
14. 퀴즈
Q1. WASI의 핵심 보안 모델은 무엇인가?
정답: Capability-Based Security (권한 기반 보안)
WASI는 "No Ambient Authority" 원칙을 따른다. Wasm 모듈은 기본적으로 아무 권한도 없으며, 호스트가 명시적으로 부여한 capability(파일 핸들, 네트워크 소켓 등)만 사용할 수 있다. 이는 전통적인 Unix의 ambient authority 모델과 대비된다.
Q2. Wasm Component Model의 장점은?
정답:
- 언어 독립적 조합: Rust, Python, Go 등 서로 다른 언어로 작성된 모듈을 하나로 조합 가능
- WIT 인터페이스: 타입 안전한 계약으로 모듈 간 통신
- 샌드박스 격리: 각 컴포넌트는 자신만의 메모리 공간을 가짐
- 경량: 컨테이너 대비 수십 배 가벼움
Q3. Wasm의 콜드 스타트가 Lambda보다 빠른 이유는?
정답:
- Wasm 바이너리는 1-10MB로 매우 작아 로딩이 빠름
- 런타임 초기화가 마이크로초 수준 (JVM이나 Python 인터프리터 같은 무거운 초기화 없음)
- AOT 컴파일된 바이너리 직접 실행
- 선형 메모리 모델로 간단한 메모리 설정
- 프로세스/컨테이너 생성 오버헤드 없음
Q4. Docker + Wasm은 어떻게 동작하는가?
정답:
Docker Desktop은 containerd의 runwasi shim을 사용하여 Wasm 컨테이너를 실행한다. 기존의 runc 대신 wasmtime이나 wasmedge 같은 Wasm 런타임이 컨테이너를 실행한다. FROM scratch 기반 이미지에 .wasm 파일만 포함하여 초경량 이미지를 만들 수 있고, docker-compose에서 Linux 컨테이너와 Wasm 컨테이너를 혼합 운용할 수 있다.
Q5. Wasm이 아직 컨테이너를 대체할 수 없는 이유는?
정답:
- WASI의 시스템 인터페이스가 아직 제한적 (GPU, 복잡한 네트워킹 등)
- 에코시스템이 컨테이너에 비해 미성숙
- 레거시 애플리케이션 마이그레이션이 어려움
- 상태 관리가 기본적으로 stateless
- 디버깅 도구가 아직 발전 중
- 일부 언어의 Wasm 지원이 초기 단계
단, Wasm은 컨테이너를 "대체"하기보다 "보완"하는 기술로, Edge와 경량 서비스 영역에서 컨테이너와 공존할 전망이다.
15. 참고 자료
- WebAssembly 공식 사이트 - https://webassembly.org/
- WASI 공식 문서 - https://wasi.dev/
- Component Model 명세 - https://component-model.bytecodealliance.org/
- Fermyon Spin 문서 - https://developer.fermyon.com/spin/
- wasmCloud 문서 - https://wasmcloud.com/docs/
- Bytecode Alliance - https://bytecodealliance.org/
- wasmtime 런타임 - https://wasmtime.dev/
- Docker Wasm 가이드 - https://docs.docker.com/desktop/wasm/
- Cloudflare Workers - https://developers.cloudflare.com/workers/
- Fastly Compute - https://developer.fastly.com/learning/compute/
- wasm-tools - https://github.com/bytecodealliance/wasm-tools
- WIT 명세 - https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
- componentize-py - https://github.com/bytecodealliance/componentize-py
WebAssembly Complete Guide 2025: Beyond the Browser — WASI, Component Model, Server-Side Wasm
Table of Contents
1. What is WebAssembly
WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine. Originally designed to provide near-native performance in web browsers, it has now expanded beyond the browser to servers, edge computing, and IoT.
1.1 Core Characteristics of Wasm
| Characteristic | Description | Significance |
|---|---|---|
| Binary Format | Compact binary encoding | Fast transmission, fast decoding |
| Stack Machine | Stack-based instruction execution | Simple and efficient execution model |
| Linear Memory | Contiguous byte array | Safe memory access, sandboxing |
| Type Safety | Strong type system | Compile-time verification, runtime safety |
| Portability | Architecture independent | Runs on x86, ARM, RISC-V anywhere |
| Sandbox | Isolated execution environment | Host system protection |
1.2 History of Wasm
2015: WebAssembly project announced (W3C)
2017: Wasm 1.0 supported in all 4 major browsers
2019: WASI initial proposal (non-browser Wasm)
2020: Wasm 1.0 becomes W3C official recommendation
2021: Component Model proposal, Fermyon founded
2022: Docker+Wasm tech preview, WASI Preview 1
2023: WASI 0.2 stabilized, Component Model matures
2024: GC proposal implemented, major runtime optimizations
2025: WASI 0.2 production-ready, server-side Wasm adoption accelerates
1.3 Wasm Binary Structure
Wasm Module Structure:
+------------------+
| Magic Number | 0x00 0x61 0x73 0x6D ("\0asm")
| Version | 0x01 0x00 0x00 0x00 (v1)
+------------------+
| Type Section | Function signature definitions
| Import Section | External function/memory imports
| Function Section | Function indices
| Table Section | Indirect call tables
| Memory Section | Linear memory definition
| Global Section | Global variables
| Export Section | Publicly exposed functions/memory
| Start Section | Auto-execution function
| Element Section | Table initialization
| Code Section | Function bodies (bytecode)
| Data Section | Initial memory data
+------------------+
1.4 WAT (WebAssembly Text Format)
Understanding WAT, the text representation of Wasm, helps grasp its internal workings.
;; A simple Wasm module that adds two numbers
(module
;; Function type definition
(type $add_type (func (param i32 i32) (result i32)))
;; Function implementation
(func $add (type $add_type) (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
;; Linear memory (1 page = 64KB)
(memory (export "memory") 1)
;; Export function
(export "add" (func $add))
)
2. Browser Wasm
2.1 Compiling to Wasm from Various Languages
Rust - First-Class Wasm Citizen
// lib.rs - Rust to Wasm
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
#[wasm_bindgen]
pub struct ImageProcessor {
width: u32,
height: u32,
pixels: Vec<u8>,
}
#[wasm_bindgen]
impl ImageProcessor {
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> ImageProcessor {
ImageProcessor {
width,
height,
pixels: vec![0; (width * height * 4) as usize],
}
}
pub fn grayscale(&mut self) {
for chunk in self.pixels.chunks_exact_mut(4) {
let gray = (0.299 * chunk[0] as f64
+ 0.587 * chunk[1] as f64
+ 0.114 * chunk[2] as f64) as u8;
chunk[0] = gray;
chunk[1] = gray;
chunk[2] = gray;
}
}
pub fn pixels_ptr(&self) -> *const u8 {
self.pixels.as_ptr()
}
}
# Rust -> Wasm build
wasm-pack build --target web
C/C++ - Emscripten
// image_filter.c - C to Wasm
#include <emscripten.h>
#include <stdint.h>
EMSCRIPTEN_KEEPALIVE
void apply_blur(uint8_t* pixels, int width, int height) {
uint8_t* temp = (uint8_t*)malloc(width * height * 4);
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
for (int c = 0; c < 3; c++) {
int sum = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
sum += pixels[((y+dy)*width + (x+dx))*4 + c];
}
}
temp[(y*width + x)*4 + c] = sum / 9;
}
temp[(y*width + x)*4 + 3] = pixels[(y*width + x)*4 + 3];
}
}
memcpy(pixels, temp, width * height * 4);
free(temp);
}
# C -> Wasm build
emcc image_filter.c -O3 -s WASM=1 \
-s EXPORTED_FUNCTIONS='["_apply_blur"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap"]' \
-o image_filter.js
2.2 JavaScript Interop
// Loading and using Wasm modules
async function initWasm() {
// Method 1: fetch + instantiate
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
// Provide host functions to Wasm
log_value: (value) => console.log('Wasm says:', value),
get_time: () => Date.now(),
}
});
// Call Wasm function
const result = instance.exports.add(10, 20);
console.log('Result:', result); // 30
// Access linear memory
const memory = new Uint8Array(instance.exports.memory.buffer);
// Read/write data in memory
return instance;
}
// Method 2: wasm-bindgen (Rust)
import init, { fibonacci, ImageProcessor } from './pkg/my_wasm.js';
async function main() {
await init();
console.log('fib(40):', fibonacci(40));
const processor = new ImageProcessor(800, 600);
processor.grayscale();
}
2.3 Key Browser Wasm Use Cases
| Use Case | Examples | Performance Gain |
|---|---|---|
| Image/Video Processing | Photoshop Web, FFmpeg.wasm | 10-50x vs JS |
| Game Engines | Unity WebGL, Unreal Engine | 70-90% of native |
| Codecs/Compression | AV1 decoding, Brotli compression | 5-20x vs JS |
| Cryptography | SHA-256, AES operations | 3-10x vs JS |
| CAD/3D Modeling | AutoCAD Web, SketchUp | Near-native |
| Scientific Computing | Simulations, data analysis | 10-100x vs JS |
| PDF Processing | pdf.js acceleration, rendering | 3-5x improvement |
3. WASI 0.2: System Interface
WASI (WebAssembly System Interface) is a standard for Wasm to safely access system resources like filesystems and networks outside the browser.
3.1 WASI 0.2 Interfaces
WASI 0.2 Structure:
+----------------------------------+
| wasi:cli (CLI app support)|
| wasi:http (HTTP client/server) |
| wasi:filesystem (Filesystem access) |
| wasi:sockets (Network sockets) |
| wasi:clocks (Time-related) |
| wasi:random (Random generation) |
| wasi:io (Stream I/O) |
+----------------------------------+
| Component Model (Foundation layer) |
+----------------------------------+
| Wasm Core (Execution engine) |
+----------------------------------+
3.2 WASI HTTP Server (Rust)
// Rust + WASI HTTP Server
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct TodoItem {
id: u32,
title: String,
completed: bool,
}
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
let method = req.method().as_str();
let path = req.path();
match (method, path) {
("GET", "/api/todos") => {
let todos = vec![
TodoItem { id: 1, title: "Learn WASI".into(), completed: false },
TodoItem { id: 2, title: "Build Wasm app".into(), completed: false },
];
Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(serde_json::to_string(&todos)?)
.build())
},
("POST", "/api/todos") => {
let body: TodoItem = serde_json::from_slice(req.body())?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(serde_json::to_string(&body)?)
.build())
},
_ => Ok(Response::builder()
.status(404)
.body("Not Found")
.build())
}
}
3.3 Capability-Based Security Model
WASI's core security model is capability-based. Programs can only use permissions explicitly granted to them.
# WASI runtime capability examples
# Allow read-only access to specific directory
wasmtime run --dir /data::/data:readonly my-app.wasm
# Allow network access
wasmtime run --tcplisten 0.0.0.0:8080 my-server.wasm
# Pass environment variables
wasmtime run --env KEY=VALUE my-app.wasm
Traditional Security Model (Ambient Authority):
Process -> [Full filesystem access]
-> [All network access]
-> [All env variables readable]
WASI Security Model (Capability-Based):
Wasm Module -> [Only /data directory, read-only]
-> [Only listen on localhost:8080]
-> [Only specified env variables accessible]
4. Component Model: Module Composition
The Component Model is a standard for composing Wasm modules into complex applications.
4.1 WIT (Wasm Interface Type)
// todo.wit - Interface definition
package example:todo@0.1.0;
interface types {
record todo-item {
id: u32,
title: string,
completed: bool,
}
variant todo-error {
not-found(string),
validation-error(string),
storage-error(string),
}
}
interface todo-store {
use types.{todo-item, todo-error};
list-todos: func() -> result<list<todo-item>, todo-error>;
get-todo: func(id: u32) -> result<todo-item, todo-error>;
create-todo: func(title: string) -> result<todo-item, todo-error>;
update-todo: func(id: u32, title: string, completed: bool) -> result<todo-item, todo-error>;
delete-todo: func(id: u32) -> result<_, todo-error>;
}
world todo-app {
import wasi:http/outgoing-handler@0.2.0;
import wasi:logging/logging;
export todo-store;
}
4.2 Component Composition
# Compose multiple components into a single app
# 1. Build each component
cargo component build --release # Rust component
componentize-py -d todo.wit -w todo-app app.py # Python component
# 2. Compose components
wasm-tools compose \
--definitions auth-component.wasm \
--definitions db-component.wasm \
app-component.wasm \
-o composed-app.wasm
# 3. Run composed component
wasmtime serve composed-app.wasm
4.3 Benefits of the Component Model
| Benefit | Description |
|---|---|
| Language-Independent Composition | Combine Rust + Python + Go modules into one |
| Type-Safe Interfaces | Contracts guaranteed by WIT definitions |
| Sandbox Isolation | Memory isolation between components |
| Lightweight | Tens of times lighter than containers |
| Fast Startup | Microsecond-level initialization |
| Portability | Runs anywhere |
5. Server-Side Wasm Frameworks
5.1 Fermyon Spin
Spin is a framework for building Wasm-based microservices.
# spin.toml - Spin app configuration
spin_manifest_version = 2
[application]
name = "my-api"
version = "1.0.0"
description = "Wasm-based API server"
[[trigger.http]]
route = "/api/hello"
component = "hello"
[[trigger.http]]
route = "/api/users/..."
component = "users"
[component.hello]
source = "target/wasm32-wasi/release/hello.wasm"
allowed_outbound_hosts = []
[component.users]
source = "target/wasm32-wasi/release/users.wasm"
allowed_outbound_hosts = ["https://api.example.com"]
key_value_stores = ["default"]
sqlite_databases = ["default"]
// Spin HTTP handler (Rust)
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use spin_sdk::key_value::Store;
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
// Use Key-Value store
let store = Store::open_default()?;
match req.method().as_str() {
"GET" => {
let key = req.path().trim_start_matches("/api/users/");
match store.get(key)? {
Some(value) => Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(value)
.build()),
None => Ok(Response::builder()
.status(404)
.body("Not found")
.build()),
}
},
"POST" => {
let body = req.body().to_vec();
let key = format!("user:{}", uuid::Uuid::new_v4());
store.set(&key, &body)?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(body)
.build())
},
_ => Ok(Response::builder()
.status(405)
.body("Method not allowed")
.build()),
}
}
# Build and run Spin app
spin build
spin up # Local run (port 3000)
# Deploy to Fermyon Cloud
spin deploy
5.2 wasmCloud
wasmCloud is a distributed Wasm application platform.
# wadm.yaml - wasmCloud app manifest
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: my-api
annotations:
description: "wasmCloud HTTP API"
spec:
components:
- name: api-handler
type: component
properties:
image: ghcr.io/myorg/api-handler:0.1.0
traits:
- type: spreadscaler
properties:
instances: 5
- type: link
properties:
target: httpserver
namespace: wasi
package: http
interfaces: [incoming-handler]
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.22.0
traits:
- type: link
properties:
target: api-handler
namespace: wasi
package: http
interfaces: [incoming-handler]
source_config:
- name: default-http
properties:
address: 0.0.0.0:8080
5.3 Server-Side Wasm Framework Comparison
| Feature | Fermyon Spin | wasmCloud | Lunatic |
|---|---|---|---|
| Focus | HTTP microservices | Distributed applications | Actor model |
| Deploy Model | Single node / Cloud | Distributed lattice | Cluster |
| Storage | KV, SQLite, Redis | Capability Providers | Built-in |
| Language Support | Rust, Go, JS, Python | Rust, Go, AssemblyScript | Rust, AssemblyScript |
| Cold Start | Sub-1ms | Sub-1ms | Few ms |
| Maturity | GA | GA | Early |
| Cloud Service | Fermyon Cloud | Cosmonic | None |
6. Docker + Wasm
Docker Desktop natively supports Wasm containers.
6.1 Docker Wasm Architecture
Traditional Linux Container:
Docker Engine -> containerd -> runc -> Linux Container
(full OS, tens-hundreds MB)
Wasm Container:
Docker Engine -> containerd -> runwasi -> Wasm Runtime (wasmtime/wasmedge)
(Wasm module, few MB)
6.2 Building Wasm Docker Images
# Dockerfile.wasm - Wasm container image
FROM scratch
COPY target/wasm32-wasi/release/my-app.wasm /my-app.wasm
ENTRYPOINT ["/my-app.wasm"]
# Build and run Wasm image
docker buildx build --platform wasi/wasm -t my-wasm-app .
docker run --runtime=io.containerd.wasmtime.v1 \
--platform wasi/wasm \
-p 8080:8080 \
my-wasm-app
6.3 Docker Compose with Wasm
# docker-compose.yml - Mixed Wasm + Linux containers
version: "3"
services:
# Wasm service (ultra-lightweight)
api:
image: my-wasm-api:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
# Traditional Linux container
postgres:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
# Wasm worker service
worker:
image: my-wasm-worker:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
environment:
- QUEUE_URL=redis://redis:6379
redis:
image: redis:7-alpine
volumes:
pgdata:
6.4 Wasm vs Linux Container Comparison
| Feature | Linux Container | Wasm Container |
|---|---|---|
| Image Size | 50MB-1GB | 1-10MB |
| Startup Time | 100ms-seconds | Sub-1ms |
| Memory Usage | 50MB-GBs | 1-50MB |
| Security Model | Namespaces/cgroups | Sandbox/Capability |
| Portability | Requires Linux kernel | Architecture independent |
| Process Isolation | OS level | Language level |
| Networking | Full support | WASI sockets (limited) |
| Filesystem | Full support | WASI FS (limited) |
7. Edge Wasm
7.1 Cloudflare Workers
// Cloudflare Worker (Wasm-based)
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === '/api/compute') {
// Perform heavy computation with Wasm module
const wasmModule = await import('./compute.wasm');
const result = wasmModule.heavy_computation(42);
return new Response(JSON.stringify({ result }), {
headers: { 'Content-Type': 'application/json' }
});
}
// Use KV store
if (url.pathname.startsWith('/api/cache/')) {
const key = url.pathname.split('/').pop();
if (request.method === 'GET') {
const value = await env.MY_KV.get(key);
return new Response(value || 'Not found', {
status: value ? 200 : 404
});
}
if (request.method === 'PUT') {
const body = await request.text();
await env.MY_KV.put(key, body, { expirationTtl: 3600 });
return new Response('OK');
}
}
return new Response('Not found', { status: 404 });
}
};
7.2 Fastly Compute
// Fastly Compute (Rust + Wasm)
use fastly::{Error, Request, Response};
use fastly::http::StatusCode;
#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
let path = req.get_path().to_string();
match path.as_str() {
"/api/hello" => {
Ok(Response::from_status(StatusCode::OK)
.with_body_text_plain("Hello from Fastly Compute!"))
},
path if path.starts_with("/api/proxy/") => {
// Proxy to backend
let backend_path = path.trim_start_matches("/api/proxy");
let mut backend_req = req;
backend_req.set_path(backend_path);
backend_req.send("origin_backend")
},
_ => {
Ok(Response::from_status(StatusCode::NOT_FOUND)
.with_body_text_plain("Not found"))
}
}
}
7.3 Edge Wasm Platform Comparison
| Platform | Runtime | Languages | Cold Start | Free Tier |
|---|---|---|---|---|
| Cloudflare Workers | V8 + Wasm | JS, Rust, C++ | 0ms (always loaded) | 100K req/day |
| Fastly Compute | Wasmtime | Rust, Go, JS | Few ms | None |
| Vercel Edge Functions | V8 + Wasm | JS, TS | 0ms (always loaded) | 1M exec/month |
| Deno Deploy | V8 + Wasm | JS, TS, Wasm | 0ms | 100K req/day |
| Fermyon Cloud | Spin | Rust, Go, JS, Python | Sub-1ms | Free beta |
8. Language Support for Wasm
8.1 Rust - First-Class Citizen
Rust provides the strongest language support for Wasm.
// Cargo.toml
[package]
name = "my-wasm-app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.25"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Build targets
rustup target add wasm32-wasi # WASI target
rustup target add wasm32-unknown-unknown # Browser target
# Build
cargo build --target wasm32-wasi --release
# Convert to Component
wasm-tools component new target/wasm32-wasi/release/my_app.wasm \
-o my_app.component.wasm
8.2 Go - TinyGo
// main.go - TinyGo Wasm
package main
import (
"encoding/json"
"net/http"
spinhttp "github.com/fermyon/spin/sdk/go/v2/http"
)
type Response struct {
Message string `json:"message"`
Count int `json:"count"`
}
func init() {
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
resp := Response{
Message: "Hello from Go Wasm!",
Count: 42,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
})
}
func main() {}
# Build Wasm with TinyGo
tinygo build -target=wasi -o main.wasm main.go
8.3 Python - componentize-py
# app.py - Python Wasm (Spin)
from spin_sdk.http import IncomingHandler, Request, Response
import json
class IncomingHandler(IncomingHandler):
def handle_request(self, request: Request) -> Response:
if request.method == "GET" and request.uri == "/api/hello":
body = json.dumps({
"message": "Hello from Python Wasm!",
"path": request.uri,
})
return Response(
200,
{"content-type": "application/json"},
bytes(body, "utf-8")
)
return Response(404, {}, b"Not found")
# Python -> Wasm component
componentize-py -d spin-http.wit -w spin-http-trigger app -o app.wasm
8.4 JavaScript - jco
// app.js - JavaScript Wasm Component
import { handle } from 'wasi:http/incoming-handler@0.2.0';
export const incomingHandler = {
handle(request, responseOut) {
const headers = new Headers();
headers.set('Content-Type', 'application/json');
const body = JSON.stringify({
message: 'Hello from JS Wasm!',
method: request.method(),
path: request.pathWithQuery(),
});
const response = new OutgoingResponse(headers);
response.setStatusCode(200);
const bodyStream = response.body();
bodyStream.write(new TextEncoder().encode(body));
bodyStream.close();
responseOut.set(response);
}
};
# JavaScript -> Wasm Component
jco componentize app.js -w wasi-http.wit -o app.wasm
8.5 Language Support Comparison
| Language | Wasm Maturity | WASI Support | Component Model | Binary Size | Notes |
|---|---|---|---|---|---|
| Rust | Very High | Full | Full | 1-5MB | First-class citizen |
| C/C++ | High | Full | Partial | 0.5-3MB | Emscripten |
| Go (TinyGo) | Medium | Full | Full | 2-10MB | Some std Go unsupported |
| Python | Medium | Full | Full | 10-30MB | componentize-py |
| JavaScript | Medium | Full | Full | 5-15MB | jco |
| C# (.NET) | Medium | Partial | Early | 10-50MB | NativeAOT-LLVM |
| Swift | Early | Partial | Early | 5-20MB | SwiftWasm |
| Kotlin | Early | Partial | Early | 10-30MB | Kotlin/Wasm |
9. Performance Comparison
9.1 Cold Start Comparison
| Platform | Cold Start | Memory Usage | Notes |
|---|---|---|---|
| Wasm (Spin) | 0.5-1ms | 2-10MB | Microsecond level |
| Wasm (wasmtime) | 1-5ms | 5-20MB | General purpose |
| Lambda (Python) | 150-300ms | 50-128MB | Slower with VPC |
| Lambda (Java) | 800-3000ms | 128-512MB | Improvable with SnapStart |
| Docker Container | 500-5000ms | 50MB-1GB | Image size dependent |
| Cloud Run | 300-2000ms | 128MB-8GB | Instance type dependent |
9.2 Throughput Comparison
HTTP "Hello World" benchmark (single instance):
Spin (Rust/Wasm): ~150,000 req/s
Native Rust (Actix): ~200,000 req/s
Go (net/http): ~120,000 req/s
Node.js (Express): ~30,000 req/s
Python (FastAPI): ~10,000 req/s
Wasm achieves 70-90% of native performance
9.3 Memory Efficiency
Single HTTP service memory usage:
Wasm (Spin): 2-5 MB
Wasm (wasmtime): 5-15 MB
Node.js: 30-80 MB
Python: 30-60 MB
Java (JVM): 100-300 MB
.NET: 50-150 MB
Go: 10-30 MB
Concurrent services in 1GB memory:
Wasm: ~200 services
Node.js: ~15 services
Java: ~5 services
10. Security Model
10.1 Wasm Sandbox
Traditional Process:
+------------------------------------------+
| Process |
| - Full filesystem access |
| - All network access |
| - Unlimited system calls |
| - Other process memory (via exploit) |
+------------------------------------------+
Wasm Sandbox:
+------------------------------------------+
| Wasm Module |
| +------------------------------------+ |
| | Linear Memory (module-private) | |
| | - Bounds checking | |
| | - No host memory access | |
| +------------------------------------+ |
| | Only permitted system interfaces | |
| | - wasi:filesystem (specific paths) | |
| | - wasi:sockets (specific addresses) | |
| +------------------------------------+ |
+------------------------------------------+
10.2 Capability-Based Security Principles
| Principle | Description | Example |
|---|---|---|
| No Ambient Authority | No default permissions | No filesystem or network access |
| Principle of Least Privilege | Grant only minimal permissions | Only specific directory access |
| Explicit Capability Passing | Explicit permission transfer | Host passes handles |
| Revocable | Permissions can be revoked | Runtime permission removal |
10.3 Security Comparison
| Security Aspect | Containers | Wasm |
|---|---|---|
| Isolation Level | OS level (cgroup/namespace) | Language level (bytecode verification) |
| Memory Safety | Depends on kernel | Built-in bounds checking |
| System Calls | Filtered via seccomp | Limited via WASI |
| Escape Potential | Possible via kernel vulnerability | Extremely difficult |
| Permission Model | User/group based | Capability based |
| Code Verification | None | Verified at load time |
11. Real-World Use Cases
11.1 Plugin Systems
// Host application - plugin loader
use wasmtime::*;
use wasmtime_wasi::WasiCtxBuilder;
fn load_plugin(plugin_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
// Grant minimal permissions to plugin
let wasi = WasiCtxBuilder::new()
.inherit_stdout() // Only stdout allowed
// No filesystem access
// No network access
.build();
let mut store = Store::new(&engine, wasi);
let module = Module::from_file(&engine, plugin_path)?;
let instance = linker.instantiate(&mut store, &module)?;
// Call plugin function
let process = instance
.get_typed_func::<(i32,), i32>(&mut store, "process")?;
let result = process.call(&mut store, (42,))?;
println!("Plugin result: {}", result);
Ok(())
}
11.2 Edge Computing
Edge Wasm Architecture:
User Request -> [CDN Edge (300+ PoPs)]
|
v
[Wasm Worker]
|
+-> Cache Hit: Instant response (0ms)
|
+-> Cache Miss: Request to origin server
| |
| v
| [Origin Server]
| |
v v
[Response + Cache Store]
Benefits:
- Executes at the location closest to the user
- Nearly zero cold start
- Automatic global distributed deployment
11.3 Serverless Functions
Lambda vs Wasm Serverless:
AWS Lambda:
Request -> API Gateway -> Lambda Runtime -> Function Execution
Cold Start: 100ms-8s
Memory: 128MB-10GB
Billing: Per 1ms
Wasm Serverless (Spin/Fermyon):
Request -> Spin Runtime -> Wasm Module Execution
Cold Start: Sub-0.5ms
Memory: 2-50MB
Billing: Per request
11.4 Blockchain Smart Contracts
Multiple blockchains have adopted Wasm as their smart contract execution engine.
| Blockchain | Wasm Runtime | Language Support |
|---|---|---|
| Polkadot | Substrate | Rust (ink!) |
| Near | Wasmer | Rust, AssemblyScript |
| Cosmos (CosmWasm) | Wasmer | Rust |
| Dfinity (ICP) | Custom | Rust, Motoko |
12. Wasm vs Containers
12.1 Comprehensive Comparison
| Feature | Wasm | Containers |
|---|---|---|
| Startup Time | Microseconds-milliseconds | Milliseconds-seconds |
| Image Size | 1-50MB | 50MB-GBs |
| Memory Usage | 1-50MB | 50MB-GBs |
| CPU Overhead | 0-30% | 0-5% |
| Security | Strong sandbox | OS-level isolation |
| Networking | WASI (limited) | Full support |
| Filesystem | WASI (limited) | Full support |
| GPU Support | Experimental | Full support |
| Ecosystem | Growing | Very mature |
| Debugging | Evolving | Mature |
| State Management | Stateless recommended | Stateful possible |
12.2 When to Choose Wasm
Choose Wasm when:
- Sub-millisecond cold starts needed
- Ultra-lightweight services required
- Building plugin/extension systems
- Edge computing
- Multi-language module composition
- Strong sandbox isolation needed
- Resource-constrained environments (IoT)
Choose Containers when:
- Complex networking required
- GPU usage needed
- Legacy application support
- Rich ecosystem needed
- State preservation required
- Large memory/storage needs
13. Future Outlook
13.1 In-Progress Wasm Proposals
| Proposal | Status | Description |
|---|---|---|
| GC (Garbage Collection) | Phase 4 | Support for Java, Kotlin, Dart GC languages |
| Threads | Phase 3 | Shared memory multithreading |
| SIMD | Phase 4 (Complete) | 128-bit SIMD operations |
| Exception Handling | Phase 4 | Native try/catch support |
| Stack Switching | Phase 2 | Coroutines, async/await support |
| Memory64 | Phase 3 | 64-bit memory addressing |
| Branch Hinting | Phase 3 | Branch prediction hints |
| Tail Call | Phase 4 (Complete) | Tail call optimization |
13.2 Wasm Future Roadmap
2025-2026 Outlook:
- WASI 0.2 fully stabilized and adopted by major clouds
- Component Model ecosystem matures
- Docker Wasm production-ready
- GC proposal implemented in major runtimes
- Threads proposal stabilized
2027+ Outlook:
- Wasm reaches parity with containers
- Default runtime for edge-first architectures
- Standard execution environment for IoT/embedded
- Multi-language component ecosystem established
14. Quiz
Q1. What is the core security model of WASI?
Answer: Capability-Based Security
WASI follows the "No Ambient Authority" principle. Wasm modules have no permissions by default, and can only use capabilities (file handles, network sockets, etc.) explicitly granted by the host. This contrasts with the traditional Unix ambient authority model.
Q2. What are the advantages of the Wasm Component Model?
Answer:
- Language-independent composition: Combine modules written in Rust, Python, Go, etc. into one application
- WIT interfaces: Type-safe contracts for inter-module communication
- Sandbox isolation: Each component has its own memory space
- Lightweight: Tens of times lighter than containers
Q3. Why are Wasm cold starts faster than Lambda?
Answer:
- Wasm binaries are very small (1-10MB), making loading fast
- Runtime initialization is at the microsecond level (no heavy initialization like JVM or Python interpreter)
- AOT-compiled binaries execute directly
- Linear memory model allows simple memory setup
- No process/container creation overhead
Q4. How does Docker + Wasm work?
Answer:
Docker Desktop uses containerd's runwasi shim to run Wasm containers. Instead of the traditional runc, Wasm runtimes like wasmtime or wasmedge execute the container. Ultra-lightweight images can be built with a FROM scratch base containing only the .wasm file, and docker-compose can mix Linux containers and Wasm containers.
Q5. Why can Wasm not yet replace containers?
Answer:
- WASI system interfaces are still limited (GPU, complex networking, etc.)
- Ecosystem is immature compared to containers
- Migrating legacy applications is difficult
- State management is fundamentally stateless
- Debugging tools are still evolving
- Some languages have only early-stage Wasm support
However, Wasm is not meant to "replace" containers but rather to "complement" them, coexisting with containers in edge and lightweight service domains.
15. References
- WebAssembly Official Site - https://webassembly.org/
- WASI Official Documentation - https://wasi.dev/
- Component Model Specification - https://component-model.bytecodealliance.org/
- Fermyon Spin Documentation - https://developer.fermyon.com/spin/
- wasmCloud Documentation - https://wasmcloud.com/docs/
- Bytecode Alliance - https://bytecodealliance.org/
- wasmtime Runtime - https://wasmtime.dev/
- Docker Wasm Guide - https://docs.docker.com/desktop/wasm/
- Cloudflare Workers - https://developers.cloudflare.com/workers/
- Fastly Compute - https://developer.fastly.com/learning/compute/
- wasm-tools - https://github.com/bytecodealliance/wasm-tools
- WIT Specification - https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
- componentize-py - https://github.com/bytecodealliance/componentize-py