Tutorial Deploy Microservice WebAssembly Pakai Spin & Rust di VPS Linux
Poin Kunci Artikel Ini:
- Overhead Resource Container: Docker vs WebAssembly SandboxDocker standar industri isolasi microservice.
- Container jalankan OS userspace, dependency library, dan container engine runtime.
- Implikasi: overhead konsumsi memory tinggi.
Overhead Resource Container: Docker vs WebAssembly Sandbox
Docker standar industri isolasi microservice. Container jalankan OS userspace, dependency library, dan container engine runtime. Implikasi: overhead konsumsi memory tinggi. VPS spesifikasi entry-level (1 vCPU, 512MB RAM) cepat alami Out Of Memory (OOM) saat jalankan 3-5 container Docker bersamaan.
WebAssembly (Wasm) dengan WASI (WebAssembly System Interface) ubah paradigma eksekusi server-side. Rust kompilasi kode langsung ke bytecode Wasm platform-agnostic. Wasmtime jalankan bytecode langsung di atas kernel host tanpa OS virtualization. Sandboxing berlaku pada level instruction set dan linear memory space. Akses sistem (network, disk, environment variable) diblokir total kecuali diizinkan eksplisit.
| Metric Performa | Docker Container (Minimal App) | WASM Spin Microservice |
|---|---|---|
| Memory Footprint (Idle) | 50 MB - 150 MB | 3 MB - 8 MB |
| Cold Start Time | 1000 ms - 3000 ms | < 1 ms |
| Ukuran Artifact Binary | 100 MB - 500 MB | 1 MB - 5 MB |
| Isolasi Keamanan | Kernel cgroups & namespaces | Capability-based WASI sandbox |
| Density Per Core VPS | 10 - 20 Container | 100 - 500 Instance |
Arsitektur Execution Model: Spin Framework & Wasmtime Engine
Spin framework buatan Fermyon pakai Wasmtime sebagai execution engine underlying. Spin terapkan trigger model untuk handle HTTP request. Request masuk ke Spin HTTP listener, dioper ke instansi Wasm component melalui memory snapshot instant, lalu diproses fungsi handler Rust. Setelah response dikirim, instansi memory di-wipe instan.
Metode instansiasi copy-on-write Wasmtime hilangkan overhead proses booting app. Startup latency turun ke skala sub-milidetik (~100 microsecond). Pola ini izinkan ratusan service aktif bersamaan pada server spesifikasi minim tanpa kuras RAM.
Praktek Build Microservice HTTP Spin Rust & Deploy ke VPS
1. Setup Toolchain Local Machine
Pasang Rust toolchain, target kompilasi WASI, dan Spin CLI pada mesin lokal pengembang.
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
# Tambahkan target wasm32-wasip1
rustup target add wasm32-wasip1
# Install Spin CLI
curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash
sudo mv ./spin /usr/local/bin/2. Inisialisasi Project Spin Rust & Struktur Kode
Buat project baru pakai template http-rust:
spin new -t http-rust my-wasm-api --accept-defaults
cd my-wasm-apiEdit manifest spin.toml untuk konfigurasi routing dan kapabilitas akses:
spin_manifest_version = 2
[application]
name = "my-wasm-api"
version = "1.0.0"
authors = ["Dev Team <dev@example.com>"]
[[trigger.http]]
route = "/..."
component = "my-wasm-api"
[component.my-wasm-api]
source = "target/wasm32-wasip1/release/my_wasm_api.wasm"
[component.my-wasm-api.build]
command = "cargo build --target wasm32-wasip1 --release"
watch = ["src/**/*.rs", "Cargo.toml"]Edit Cargo.toml untuk optimasi ukuran binary Wasm:
[package]
name = "my-wasm-api"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
spin-sdk = "3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"Tulis logika handler HTTP JSON pada src/lib.rs:
use spin_sdk::http::{Fields, Request, Response, IntoResponse};
use spin_sdk::http_component;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct HealthResponse {
status: String,
runtime: String,
framework: String,
memory_usage_approx: String,
}
#[http_component]
fn handle_my_wasm_api(req: Request) -> impl IntoResponse {
println!("LOG: Request masuk URI: {:?}", req.uri());
let payload = HealthResponse {
status: "ok".to_string(),
runtime: "Wasmtime WASI".to_string(),
framework: "Spin 3.0".to_string(),
memory_usage_approx: "< 5MB".to_string(),
};
let json_body = match serde_json::to_string(&payload) {
Ok(body) => body,
Err(_) => return Response::builder()
.status(500)
.header("content-type", "text/plain")
.body("Internal Server Error")
.build(),
};
Response::builder()
.status(200)
.header("content-type", "application/json")
.header("x-wasm-engine", "wasmtime")
.body(json_body)
.build()
}3. Build Artifact WASM & Optimisasi Binary
Jalankan proses kompilasi binary Wasm:
spin buildKompilasi hasilkan file target/wasm32-wasip1/release/my_wasm_api.wasm. Kompresi ukuran binary pakai tool wasm-opt jika binaryen terinstall:
wasm-opt -Oz target/wasm32-wasip1/release/my_wasm_api.wasm -o target/wasm32-wasip1/release/my_wasm_api.wasmKonfigurasi Systemd, Nginx Reverse Proxy, & Benchmark Resource
1. Deploy ke VPS & Setup Systemd Service
Transfer berkas project ke VPS Linux menggunakan rsync:
rsync -avz --exclude='target' ./my-wasm-api user@vps-ip-address:/opt/my-wasm-apiLogin via SSH ke VPS, install Spin CLI di VPS, lalu buat unit file systemd /etc/systemd/system/spin-app.service:
[Unit]
Description=Spin WebAssembly Microservice Engine
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/my-wasm-api
ExecStart=/usr/local/bin/spin up --listen 127.0.0.1:8080
Restart=always
RestartSec=3
LimitNOFILE=65535
CapabilityBoundingSet=
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/my-wasm-api
[Install]
WantedBy=multi-user.targetMuat ulang daemon systemd dan jalankan service:
sudo systemctl daemon-reload
sudo systemctl enable --now spin-app
sudo systemctl status spin-app2. Konfigurasi Nginx Reverse Proxy
Pasang Nginx sebagai reverse proxy front-facing untuk atasi SSL termination dan header routing. Buat file /etc/nginx/sites-available/spin-api:
server {
listen 80;
server_name api.domainAnda.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'keep-alive';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}Aktifkan konfigurasi Nginx dan reload service:
sudo ln -s /etc/nginx/sites-available/spin-api /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx3. Load Testing & Profiling Resource
Uji ketahanan service dari client luar menggunakan tool benchmark hey:
hey -n 100000 -c 500 http://api.domainAnda.com/Pantau penggunaan CPU dan RAM via htop atau systemd-cgtop pada VPS saat pengujian berlangsung.
- Throughput: Mencapai 12.000 - 18.000 Request/second pada VPS 1 vCPU.
- RAM Usage: Alokasi memory total proses Spin tetap stabil pada kisaran 8MB - 12MB meski menangani 500 concurrent connections.
- Error Rate: 0% dropped connection under high concurrency.
Mitigasi Limitas WASI & Keamanan Level Capability
Sistem keamanan WASI bersifat deny-all default. Aplikasi Wasm tidak bisa panggil socket jaringan atau akses sistem berkas VPS tanpa deklarasi tegas di spin.toml.
1. Outbound Network Access
WASI memblokir request HTTP outbound secara bawaan. Izinkan endpoint spesifik di spin.toml:
[component.my-wasm-api]
source = "target/wasm32-wasip1/release/my_wasm_api.wasm"
allowed_outbound_hosts = [
"https://api.stripe.com",
"https://api.sendgrid.com",
"postgres://db.example.com:5432"
]2. Akses Database Persistence
WASI Preview 1 belum mendukung socket TCP arbitrer secara native tanpa abstraction layer. Opsi integrasi database:
- Spin Key-Value Store: Pakai key-value bawaan Spin backed by SQLite/Redis.
key_value_stores = ["default"] - HTTP-based Database API: Akses Supabase, PlanetScale, atau HTTP Relational Proxy.
- WASI Postgres Driver: Gunakan driver PostgreSQL murni Rust yang dikompilasi khusus ke target WASI socket layer.
3. System File Access Sandbox
Petakan direktori lokal yang diizinkan dibaca aplikasi:
[component.my-wasm-api]
files = [ { source = "assets/static", destination = "/static


