Wippy에서 Rust 실행하기
Wippy에서 Rust 실행하기
섹션 제목: “Wippy에서 Rust 실행하기”Rust WebAssembly 컴포넌트를 빌드하고 함수, CLI 명령, HTTP 엔드포인트로 실행합니다.
빌드할 내용
섹션 제목: “빌드할 내용”네 개의 내보내기 함수를 가진 Rust 컴포넌트:
- greet - 이름을 받아 인사말을 반환
- add - 두 정수를 더함
- fibonacci - n번째 피보나치 수를 계산
- list-files - 마운트된 디렉터리의 파일 목록을 표시
이를 호출 가능한 함수, CLI 명령, HTTP 엔드포인트로 노출합니다.
사전 요구사항
섹션 제목: “사전 요구사항”wasm32-wasip1타겟이 포함된 Rust 툴체인- cargo-component
rustup target add wasm32-wasip1cargo install cargo-component프로젝트 구조
섹션 제목: “프로젝트 구조”rust-wasm-demo/├── demo/ # Rust component│ ├── Cargo.toml│ ├── wit/│ │ └── world.wit # WIT interface│ └── src/│ └── lib.rs # Implementation└── app/ # Wippy application ├── wippy.lock └── src/ ├── _index.yaml # Infrastructure └── demo/ ├── _index.yaml # CLI processes └── wasm/ ├── _index.yaml # WASM entries └── demo_component.wasm # Compiled binary1단계: WIT 인터페이스 생성
섹션 제목: “1단계: WIT 인터페이스 생성”WIT (WebAssembly Interface Types)는 호스트와 게스트 간의 계약을 정의합니다:
demo/wit/world.wit를 생성합니다:
package component:demo;
world demo { export greet: func(name: string) -> string; export add: func(a: s32, b: s32) -> s32; export fibonacci: func(n: u32) -> u64; export list-files: func(path: string) -> string;}각 export는 Wippy가 호출할 수 있는 함수가 됩니다.
2단계: Rust로 구현
섹션 제목: “2단계: Rust로 구현”demo/Cargo.toml을 생성합니다:
[package]name = "demo"version = "0.1.0"edition = "2024"
[dependencies]wit-bindgen-rt = { version = "0.44.0", features = ["bitflags"] }
[lib]crate-type = ["cdylib"]
[profile.release]opt-level = "s"lto = true
[package.metadata.component]package = "component:demo"demo/src/lib.rs를 생성합니다:
#[allow(warnings)]mod bindings;
use bindings::Guest;
struct Component;
impl Guest for Component { fn greet(name: String) -> String { format!("Hello, {}!", name) }
fn add(a: i32, b: i32) -> i32 { a + b }
fn fibonacci(n: u32) -> u64 { if n <= 1 { return n as u64; } let (mut a, mut b) = (0u64, 1u64); for _ in 2..=n { let next = a + b; a = b; b = next; } b }
fn list_files(path: String) -> String { let mut result = String::new(); match std::fs::read_dir(&path) { Ok(entries) => { for entry in entries { match entry { Ok(e) => { let name = e.file_name().to_string_lossy().to_string(); let meta = e.metadata(); let (kind, size) = match meta { Ok(m) => { let kind = if m.is_dir() { "dir" } else { "file" }; (kind, m.len()) } Err(_) => ("?", 0), }; let line = format!("{:<6} {:>8} {}", kind, size, name); println!("{}", line); result.push_str(&line); result.push('\n'); } Err(e) => { let line = format!("error: {}", e); eprintln!("{}", line); result.push_str(&line); result.push('\n'); } } } } Err(e) => { let line = format!("cannot read {}: {}", path, e); eprintln!("{}", line); result.push_str(&line); result.push('\n'); } } result }}
bindings::export!(Component with_types_in bindings);bindings 모듈은 WIT 정의에서 cargo-component에 의해 생성됩니다.
3단계: 컴포넌트 빌드
섹션 제목: “3단계: 컴포넌트 빌드”cd democargo component build --releasetarget/wasm32-wasip1/release/demo.wasm이 생성됩니다. Wippy 앱으로 복사합니다:
mkdir -p ../app/src/demo/wasmcp target/wasm32-wasip1/release/demo.wasm ../app/src/demo/wasm/demo_component.wasm무결성 검증을 위한 SHA-256 해시를 가져옵니다:
sha256sum ../app/src/demo/wasm/demo_component.wasm4단계: Wippy 애플리케이션
섹션 제목: “4단계: Wippy 애플리케이션”인프라
섹션 제목: “인프라”app/src/_index.yaml을 생성합니다:
version: "1.0"namespace: demo
entries: - name: gateway kind: http.service meta: comment: HTTP server addr: ":8090" lifecycle: auto_start: true
- name: api kind: http.router meta: comment: Public API router server: demo:gateway prefix: /
- name: processes kind: process.host lifecycle: auto_start: true
- name: terminal kind: terminal.host lifecycle: auto_start: trueWASM 함수
섹션 제목: “WASM 함수”app/src/demo/wasm/_index.yaml을 생성합니다:
version: "1.0"namespace: demo.wasm
entries: - name: assets kind: fs.directory meta: comment: Filesystem with WASM binaries directory: ./src/demo/wasm
- name: greet_function kind: function.wasm meta: comment: Greet function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet pool: type: inline
- name: add_function kind: function.wasm meta: comment: Add function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: add pool: type: inline
- name: fibonacci_function kind: function.wasm meta: comment: Fibonacci function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: fibonacci pool: type: inline핵심 사항:
- 단일
fs.directory엔트리가 WASM 바이너리를 제공합니다 - 여러 함수가 서로 다른
method값으로 동일한 바이너리를 참조합니다 hash필드는 로드 시 바이너리 무결성을 검증합니다inline풀은 호출마다 새 인스턴스를 생성합니다
WASI를 사용하는 함수
섹션 제목: “WASI를 사용하는 함수”list-files 함수는 파일시스템에 접근하므로 WASI 임포트가 필요합니다:
- name: list_files_function kind: function.wasm meta: comment: Filesystem listing with WASI mounts fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: list-files imports: - wasi:cli - wasi:io - wasi:clocks - wasi:filesystem wasi: mounts: - fs: demo.wasm:assets guest: /data pool: type: inlinewasi.mounts 섹션은 Wippy 파일시스템 엔트리를 게스트 경로에 매핑합니다. WASM 모듈 내부에서 /data는 demo.wasm:assets 디렉터리를 가리킵니다.
CLI 명령
섹션 제목: “CLI 명령”app/src/demo/_index.yaml을 생성합니다:
version: "1.0"namespace: demo.cli
entries: - name: greet kind: process.wasm meta: comment: Greet someone via WASM command: name: greet short: Greet someone via WASM fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet
- name: ls kind: process.wasm meta: comment: List files from mounted WASI filesystem command: name: ls short: List files from mounted directory fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: list-files imports: - wasi:cli - wasi:io - wasi:clocks - wasi:filesystem wasi: mounts: - fs: demo.wasm:assets guest: /datameta.command 블록은 프로세스를 이름이 있는 CLI 명령으로 등록합니다. greet 명령은 문자열 연산만 사용하므로 WASI 임포트가 필요 없습니다. ls 명령은 파일시스템 접근이 필요합니다.
HTTP 엔드포인트
섹션 제목: “HTTP 엔드포인트”app/src/demo/wasm/_index.yaml에 추가합니다:
- name: http_greet kind: function.wasm meta: comment: Greet exposed via wasi-http transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet transport: wasi-http pool: type: inline
- name: http_greet_endpoint kind: http.endpoint meta: comment: HTTP POST endpoint for WASM greet router: demo:api method: POST path: /greet func: http_greetwasi-http 트랜스포트는 HTTP 요청/응답 컨텍스트를 WASM 인자 및 결과에 매핑합니다.
5단계: 초기화 및 실행
섹션 제목: “5단계: 초기화 및 실행”cd appwippy initCLI 명령 실행
섹션 제목: “CLI 명령 실행”# List available commandswippy run listAvailable commands: greet Greet someone via WASM ls List files from mounted directory# Run greetwippy run greet# Run ls to list mounted directorywippy run ls서비스로 실행
섹션 제목: “서비스로 실행”wippy run포트 8090에서 HTTP 서버가 시작됩니다. 엔드포인트를 테스트합니다:
curl -X POST http://localhost:8090/greetLua에서 호출
섹션 제목: “Lua에서 호출”WASM 함수는 Lua 함수와 동일한 방식으로 호출됩니다:
local funcs = require("funcs")
local greeting, err = funcs.call("demo.wasm:greet_function", "World")-- greeting: "Hello, World!"
local sum, err = funcs.call("demo.wasm:add_function", 6, 7)-- sum: 13
local fib, err = funcs.call("demo.wasm:fibonacci_function", 10)-- fib: 55