WASI로 서버리스 AI 인퍼런스 구현
지금까지 우리는 Rust와 WebAssembly(WASM)를 활용해 브라우저에서 다양한 인식 기능을 구현해왔습니다. 하지만 브라우저 환경에는 여전히 한계가 존재합니다. 예를 들어, 모델이 너무 크거나 WebGPU가 미지원인 경우, 또는 민감한 모델 로직을 클라이언트로 노출시키기 어려운 경우 등이 있습니다.
이번 시리즈에서는 WebAssembly를 브라우저가 아닌 서버리스 환경에서 활용하는 방법을 소개합니다. 특히 핵심은 WASI (WebAssembly System Interface)를 통해 독립 실행형 WASM 바이너리를 실행하고, 이를 서버리스로 배포하여 REST API 형태로 AI 추론을 제공하는 구조입니다.
WASI란 무엇인가?
WASI는 WebAssembly가 브라우저 밖에서도 실행될 수 있도록 시스템 인터페이스를 정의한 표준입니다. POSIX 스타일의 파일 입출력, 환경 변수, 시간, 랜덤 등 기본적인 OS 기능을 WebAssembly 모듈에서 사용할 수 있게 해줍니다.
즉, WASI를 지원하는 런타임에서는 wasm32-wasi
타겟으로 컴파일된 모듈을 마치 리눅스 바이너리처럼 실행할 수 있습니다. 이를 이용하면 경량화된 AI 모델을 포함한 추론 코드를 WASM으로 패키징하고, 서버나 클라우드 환경에서 직접 구동할 수 있게 됩니다.
구성 개요
- Rust로 작성한 AI 추론 로직을
wasm32-wasi
타겟으로 빌드 - WASI 런타임에서 실행 (Wasmtime, Wasmer 등)
- 서버리스 플랫폼(Fastly, Fermyon, Cloudflare Workers 등)에 배포
- HTTP API 요청 → 입력 전달 → WASM 추론 실행 → 결과 반환
1. Rust 프로젝트 준비
WASI 타겟을 활성화합니다.
rustup target add wasm32-wasi
추론 로직은 단순한 예제를 기준으로 작성합니다. 예를 들어, 입력 숫자 배열의 평균을 구하는 추론 로직은 다음과 같습니다.
use std::io::{self, Read};
fn main() {
let mut input = String::new();
io::stdin().read_to_string(&mut input).unwrap();
let values: Vec<f32> = input
.trim()
.split(',')
.filter_map(|s| s.parse().ok())
.collect();
let mean: f32 = values.iter().sum::() / values.len() as f32;
println!("{}", mean);
}
stdin
에서 CSV 형식으로 숫자를 읽고 평균을 출력합니다. 이 코드를 다음 명령어로 빌드합니다.
cargo build --release --target wasm32-wasi
빌드 결과는 target/wasm32-wasi/release/my_infer.wasm
파일에 생성됩니다.
2. 로컬에서 실행해보기
Wasmtime이나 Wasmer 같은 런타임을 이용하면 로컬에서도 WASI 모듈을 테스트할 수 있습니다.
echo "1.0,2.0,3.0" | wasmtime my_infer.wasm
# 출력: 2.0
이 구조는 매우 단순하지만, WASM이 브라우저 없이도 명령줄에서 독립적으로 실행된다는 점을 보여줍니다.
3. 서버리스 환경에 배포
WASM을 지원하는 서버리스 플랫폼이 늘어나고 있습니다. 특히 WASI를 정식 지원하는 플랫폼으로는 다음과 같은 선택지가 있습니다.
- Fermyon Cloud (Spin): WASI 기반 애플리케이션을 HTTP 서비스로 실행
- Fastly Compute@Edge: WASM 모듈을 고성능 CDN 엣지에서 실행
- Cloudflare Workers + WASM: JS 기반 환경에 WASM 모듈을 로드하여 처리
Fermyon Spin 예시
Fermyon의 Spin CLI를 사용하면 WASM 모듈을 간단하게 HTTP 서비스로 만들 수 있습니다.
spin new http-rust my-infer-api
cd my-infer-api
# src/lib.rs 파일에 추론 로직 추가
spin build
spin deploy
lib.rs
에 HTTP POST 요청으로부터 입력을 받고 WASI 모듈로 평균을 계산해 JSON 응답으로 돌려주는 구조를 구현하면 됩니다.
fn handle_request(req: Request) -> Response {
let body = req.body().to_string();
let values: Vec<f32> = body.split(',').map(|s| s.trim().parse().unwrap()).collect();
let avg = values.iter().sum::() / values.len() as f32;
Response::new(200, format!("{{\"mean\": {:.2}}}", avg))
}
이 WASM 서비스는 Fermyon Cloud에 배포되며, 별도 서버 없이도 인퍼런스 API를 구축할 수 있게 됩니다.
장점과 활용 시나리오
WASI 기반 서버리스 구조는 다음과 같은 장점을 가집니다.
- 서버리스 구조로 유지보수 부담이 거의 없음
- 모델과 추론 코드가 분리되어 클라이언트 보안성 향상
- 브라우저 제약 없이 대형 모델도 사용 가능
- 빠른 스케일링 (엣지 또는 함수형 인프라 활용)
예를 들어 IoT 장치에서 센서 값을 보내고, 서버리스 WASM으로 빠르게 인식 또는 경고 판단을 하거나, 사용자가 업로드한 이미지에 대해 서버 측에서 WebAssembly로 필터링 및 분석을 수행하는 구조도 가능합니다.
마무리
이번 글에서는 WASI를 활용해 브라우저 밖에서 WASM을 구동하고, 이를 서버리스 API 형태로 배포하는 과정을 소개했습니다. Rust로 작성된 AI 추론 코드를 WASM으로 패키징하고, 외부 인프라 없이도 완전한 서비스를 운영할 수 있는 기반이 되는 방식입니다.
이후 시리즈에서는 실제 모델을 이 구조에 통합하고, 요청 시간 측정 및 비용 절감을 위한 구조 최적화 방법까지 다뤄보겠습니다. 특히 AI 모델을 슬라이스하거나 캐시 처리하는 부분도 함께 살펴볼 예정이니 계속 지켜봐 주세요.