この記事は何?
WebAssembly(wasm)初学者による「wasmやってみた」系の記事です。
実際にやってみて得られた気づきを共有します。
モチベーション
- いくつかの言語で書いた実装をwasmに変換してみたい
- wasmがdockerコンテナイメージとして扱えるらしいのでやってみたい
- wasmをコンテナイメージとして扱えるなら、Google CloudRunやAWS ECSで動かせたりするのでは? -> やってみたい
WebAssembly?
WebAssembly は現代のウェブブラウザーで実行できる新しい種類のコードです。ネイティブに近いパフォーマンスで動作する、コンパクトなバイナリー形式の低レベルなアセンブリー風言語です。さらに、 C/C++、C# や Rust などの言語のコンパイル先となり、それらの言語をウェブ上で実行することができます。 WebAssembly は JavaScript と並行して動作するように設計されているため、両方を連携させることができます。
WebAssembly(略称Wasm)は、スタックベースの仮想マシン用のバイナリ命令フォーマットである。 Wasmはプログラミング言語の移植可能なコンパイル・ターゲットとして設計されており、クライアントおよびサーバー・アプリケーションのウェブ上での展開を可能にする。
やってみたリポジトリ一覧
python, rust, typescript についてwasm化などを試しました。
リポジトリではdevcontainerを用意しているので
vscode + docker環境 + Remove Development拡張があればすぐに試すことができます。
Python -> wasm 編
Pythonにおけるwasm生成
wasm化には↑で紹介されているpy2wasmを使ってみましたが、
色々引っかかってしまいwasm化できるようになるまで時間が掛かりました。
ハマりポイント
- pythonのバージョンは
3.11
にする(py2wasmが3.12
に対応していない) - py2wasm実行時に
patchelf
のインストールも必要 - wasm実行環境としてwasmerを使う場合は
glibc2.34
以上が必要なのでbullseye
ではなくbookworm
を使う
py2wasmの辛いポイント
↓のようなコマンドでwasmを生成するのですが、かなり時間が掛かります。
py2wasm main.py -o main.wasm
↓の画像の状態で10分以上待たされました。
wasm出力をGithub ActionsのCDスクリプトで行う場合は実行時間をかなり取られることになるため、コスト増が懸念点となるかもしれないと感じました。
FastAPI + wasm の実行時エラー
Google CloudRunやAWS ECSでwebサーバとして実行するwasmを作ってみたかったため、
FastAPIで実装したpythonコードをwasm化し、wasmtime及びwasmerで実行してみました。
wasmtime main.wasm
wasmer run main.wasm
いずれもtextwrap
に対するModuleNotFoundError
に引っかかり、上手く実行できませんでした。
Flask + wasm の実行時エラー
FastAPIでは上手くいかなかったためFlaskも試してみました。
Flask + wasmtime
wasmtime main_flask.wasm
以下のエラーが出て上手く実行できませんでした。
Flask + wasmtime実行時エラーログ
Error: failed to run main module `main_flask.wasm`
Caused by:
0: failed to invoke command default
1: error while executing at wasm backtrace:
0: 0x15a55 - <unknown>!binary_op1
1: 0x16b24 - <unknown>!PyNumber_InPlaceAdd
2: 0x13b78a9 - <unknown>!_nuitka_loader_find_spec
3: 0x80fa5 - <unknown>!cfunction_call
4: 0x318e8 - <unknown>!_PyObject_MakeTpCall
5: 0x32016 - <unknown>!PyObject_Vectorcall
6: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
7: 0x12baa4 - <unknown>!_PyEval_Vector
8: 0x32491 - <unknown>!_PyFunction_Vectorcall
9: 0x336c7 - <unknown>!object_vacall
10: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
11: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
12: 0x124deb - <unknown>!_PyEval_EvalFrameDefault
13: 0x11e34b - <unknown>!PyEval_EvalCode
14: 0x164986 - <unknown>!PyImport_ExecCodeModuleObject
15: 0x164b54 - <unknown>!PyImport_ExecCodeModuleWithPathnames
16: 0x164bd7 - <unknown>!PyImport_ExecCodeModuleEx
17: 0x139b488 - <unknown>!_EXECUTE_EMBEDDED_MODULE
18: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
19: 0x80fa5 - <unknown>!cfunction_call
20: 0x318e8 - <unknown>!_PyObject_MakeTpCall
21: 0x32016 - <unknown>!PyObject_Vectorcall
22: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
23: 0x12baa4 - <unknown>!_PyEval_Vector
24: 0x32491 - <unknown>!_PyFunction_Vectorcall
25: 0x336c7 - <unknown>!object_vacall
26: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
27: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
28: 0x124deb - <unknown>!_PyEval_EvalFrameDefault
29: 0x11e34b - <unknown>!PyEval_EvalCode
30: 0x164986 - <unknown>!PyImport_ExecCodeModuleObject
31: 0x164b54 - <unknown>!PyImport_ExecCodeModuleWithPathnames
32: 0x164bd7 - <unknown>!PyImport_ExecCodeModuleEx
33: 0x139b488 - <unknown>!_EXECUTE_EMBEDDED_MODULE
34: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
35: 0x80fa5 - <unknown>!cfunction_call
36: 0x318e8 - <unknown>!_PyObject_MakeTpCall
37: 0x32016 - <unknown>!PyObject_Vectorcall
38: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
39: 0x12baa4 - <unknown>!_PyEval_Vector
40: 0x32491 - <unknown>!_PyFunction_Vectorcall
41: 0x336c7 - <unknown>!object_vacall
42: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
43: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
44: 0x124deb - <unknown>!_PyEval_EvalFrameDefault
45: 0x11e34b - <unknown>!PyEval_EvalCode
46: 0x164986 - <unknown>!PyImport_ExecCodeModuleObject
47: 0x164b54 - <unknown>!PyImport_ExecCodeModuleWithPathnames
48: 0x164bd7 - <unknown>!PyImport_ExecCodeModuleEx
49: 0x139b488 - <unknown>!_EXECUTE_EMBEDDED_MODULE
50: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
51: 0x80fa5 - <unknown>!cfunction_call
52: 0x318e8 - <unknown>!_PyObject_MakeTpCall
53: 0x32016 - <unknown>!PyObject_Vectorcall
54: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
55: 0x12baa4 - <unknown>!_PyEval_Vector
56: 0x32491 - <unknown>!_PyFunction_Vectorcall
57: 0x336c7 - <unknown>!object_vacall
58: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
59: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
60: 0x119d63 - <unknown>!builtin___import__
61: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
62: 0x31ece - <unknown>!_PyVectorcall_Call
63: 0x31e77 - <unknown>!PyVectorcall_Call
64: 0x80f50 - <unknown>!cfunction_call
65: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
66: 0x389790 - <unknown>!IMPORT_MODULE5
67: 0x125c05d - <unknown>!modulecode_werkzeug$serving
68: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
69: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
70: 0x80fa5 - <unknown>!cfunction_call
71: 0x318e8 - <unknown>!_PyObject_MakeTpCall
72: 0x32016 - <unknown>!PyObject_Vectorcall
73: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
74: 0x12baa4 - <unknown>!_PyEval_Vector
75: 0x32491 - <unknown>!_PyFunction_Vectorcall
76: 0x336c7 - <unknown>!object_vacall
77: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
78: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
79: 0x119d63 - <unknown>!builtin___import__
80: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
81: 0x31ece - <unknown>!_PyVectorcall_Call
82: 0x31e77 - <unknown>!PyVectorcall_Call
83: 0x80f50 - <unknown>!cfunction_call
84: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
85: 0x389790 - <unknown>!IMPORT_MODULE5
86: 0xe50403 - <unknown>!modulecode_werkzeug
87: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
88: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
89: 0x80fa5 - <unknown>!cfunction_call
90: 0x318e8 - <unknown>!_PyObject_MakeTpCall
91: 0x32016 - <unknown>!PyObject_Vectorcall
92: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
93: 0x12baa4 - <unknown>!_PyEval_Vector
94: 0x32491 - <unknown>!_PyFunction_Vectorcall
95: 0x336c7 - <unknown>!object_vacall
96: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
97: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
98: 0x119d63 - <unknown>!builtin___import__
99: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
100: 0x31ece - <unknown>!_PyVectorcall_Call
101: 0x3223c - <unknown>!_PyObject_Call
102: 0x322fc - <unknown>!PyObject_Call
103: 0x129777 - <unknown>!_PyEval_EvalFrameDefault
104: 0x12baa4 - <unknown>!_PyEval_Vector
105: 0x32491 - <unknown>!_PyFunction_Vectorcall
106: 0x336c7 - <unknown>!object_vacall
107: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
108: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
109: 0x119d63 - <unknown>!builtin___import__
110: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
111: 0x31ece - <unknown>!_PyVectorcall_Call
112: 0x31e77 - <unknown>!PyVectorcall_Call
113: 0x80f50 - <unknown>!cfunction_call
114: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
115: 0x7bf629 - <unknown>!modulecode_flask$globals
116: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
117: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
118: 0x80fa5 - <unknown>!cfunction_call
119: 0x318e8 - <unknown>!_PyObject_MakeTpCall
120: 0x32016 - <unknown>!PyObject_Vectorcall
121: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
122: 0x12baa4 - <unknown>!_PyEval_Vector
123: 0x32491 - <unknown>!_PyFunction_Vectorcall
124: 0x336c7 - <unknown>!object_vacall
125: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
126: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
127: 0x119d63 - <unknown>!builtin___import__
128: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
129: 0x31ece - <unknown>!_PyVectorcall_Call
130: 0x31e77 - <unknown>!PyVectorcall_Call
131: 0x80f50 - <unknown>!cfunction_call
132: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
133: 0x389790 - <unknown>!IMPORT_MODULE5
134: 0x7d64c6 - <unknown>!modulecode_flask$json
135: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
136: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
137: 0x80fa5 - <unknown>!cfunction_call
138: 0x318e8 - <unknown>!_PyObject_MakeTpCall
139: 0x32016 - <unknown>!PyObject_Vectorcall
140: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
141: 0x12baa4 - <unknown>!_PyEval_Vector
142: 0x32491 - <unknown>!_PyFunction_Vectorcall
143: 0x336c7 - <unknown>!object_vacall
144: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
145: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
146: 0x119d63 - <unknown>!builtin___import__
147: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
148: 0x31ece - <unknown>!_PyVectorcall_Call
149: 0x3223c - <unknown>!_PyObject_Call
150: 0x322fc - <unknown>!PyObject_Call
151: 0x129777 - <unknown>!_PyEval_EvalFrameDefault
152: 0x12baa4 - <unknown>!_PyEval_Vector
153: 0x32491 - <unknown>!_PyFunction_Vectorcall
154: 0x336c7 - <unknown>!object_vacall
155: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
156: 0x166000 - <unknown>!PyImport_ImportModuleLevelObject
157: 0x119d63 - <unknown>!builtin___import__
158: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
159: 0x31ece - <unknown>!_PyVectorcall_Call
160: 0x31e77 - <unknown>!PyVectorcall_Call
161: 0x80f50 - <unknown>!cfunction_call
162: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
163: 0x389790 - <unknown>!IMPORT_MODULE5
164: 0x72c151 - <unknown>!modulecode_flask
165: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
166: 0x13b7cd4 - <unknown>!_nuitka_loader_exec_module
167: 0x80fa5 - <unknown>!cfunction_call
168: 0x318e8 - <unknown>!_PyObject_MakeTpCall
169: 0x32016 - <unknown>!PyObject_Vectorcall
170: 0x129c61 - <unknown>!_PyEval_EvalFrameDefault
171: 0x12baa4 - <unknown>!_PyEval_Vector
172: 0x32491 - <unknown>!_PyFunction_Vectorcall
173: 0x336c7 - <unknown>!object_vacall
174: 0x3344a - <unknown>!PyObject_CallMethodObjArgs
175: 0x165cb5 - <unknown>!PyImport_ImportModuleLevelObject
176: 0x119d63 - <unknown>!builtin___import__
177: 0x80736 - <unknown>!cfunction_vectorcall_FASTCALL_KEYWORDS
178: 0x31ece - <unknown>!_PyVectorcall_Call
179: 0x31e77 - <unknown>!PyVectorcall_Call
180: 0x80f50 - <unknown>!cfunction_call
181: 0x37538a - <unknown>!CALL_FUNCTION_WITH_ARGS5
182: 0x372a74 - <unknown>!modulecode___main__
183: 0x139b4e1 - <unknown>!_EXECUTE_EMBEDDED_MODULE
184: 0x139ac3d - <unknown>!EXECUTE_MAIN_MODULE
185: 0x1399c5f - <unknown>!main
186: 0x344330 - <unknown>!__main_void
187: 0x12ba5 - <unknown>!_start
note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
2: wasm trap: indirect call type mismatch
Flask + wasmer
実行時エラーは出なかったものの、ブラウザでlocalhost:5000
を開いてもローディングが終わらず...
wasmer run main_flask.wasm
Rust -> wasm 編
Rustにおけるwasm生成
↓のコマンドを1回だけ実行しておき、ビルドターゲットにwasm32-wasip1
を指定できるようにします。
rustup target add wasm32-wasip1
↓のコマンドでwasmを生成します。
cargo build --target wasm32-wasip1 --release
target/wasm32-wasip1/release/wasm-handson-rust.wasm
が生成されます。
cargo new
しただけのhello-worldなコードのwasmファイルは63KBでした。
wasmとして実行する
wasmer target/wasm32-wasip1/release/wasm-handson-rust.wasm
wasmtime target/wasm32-wasip1/release/wasm-handson-rust.wasm
コードはただのhello-worldなので、普通に実行できました。
RustコードでwebAPIを実装する
cargo add actix-web
use actix_web::{get, App, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
"Hello, world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(hello))
.bind("127.0.0.1:8080")?
.run()
.await
}
wasm化する前にRustとしてwebAPIを実行する
cargo run
ブラウザでlocalhost:8080
を開き、webAPIとして動作していることが確認できました。
webAPI化したRustコードをwasm化する
cargo build --target wasm32-wasip1 --release
以下のエラーが出てwasm生成に失敗しました。
ビルド時のエラーログ
error[E0308]: mismatched types
--> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/socket.rs:2351:15
|
2351 | from!(Socket, net::UdpSocket);
| ^^^^^^^^^^^^^^ expected `UdpSocket`, found `()`
|
::: /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/lib.rs:109:16
|
109 | fn from(socket: $from) -> $for {
| ---- implicitly returns `()` as its body has no tail or `return` expression
error[E0599]: the method `ok` exists for enum `Result<SockAddr, Error>`, but its trait bounds were not satisfied
--> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/sockref.rs:119:60
|
119 | .field("local_addr", &self.socket.local_addr().ok())
| ^^ method cannot be called on `Result<SockAddr, Error>` due to unsatisfied trait bounds
|
::: /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/sockaddr.rs:21:1
|
21 | pub struct SockAddr {
| ------------------- doesn't satisfy `SockAddr: Sized`
|
= note: the following trait bounds were not satisfied:
`{type error}: Sized`
which is required by `SockAddr: Sized`
error[E0599]: the method `ok` exists for enum `Result<SockAddr, Error>`, but its trait bounds were not satisfied
--> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/sockref.rs:120:58
|
120 | .field("peer_addr", &self.socket.peer_addr().ok())
| ^^ method cannot be called on `Result<SockAddr, Error>` due to unsatisfied trait bounds
|
::: /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/sockaddr.rs:21:1
|
21 | pub struct SockAddr {
| ------------------- doesn't satisfy `SockAddr: Sized`
|
= note: the following trait bounds were not satisfied:
`{type error}: Sized`
which is required by `SockAddr: Sized`
error[E0599]: the method `as_mut_ptr` exists for mutable reference `&mut [MaybeUninitSlice<'_>]`, but its trait bounds were not satisfied
--> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/lib.rs:710:51
|
390 | pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
| ------------------------------- doesn't satisfy `MaybeUninitSlice<'_>: Sized`
...
710 | sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
| ^^^^^^^^^^ method cannot be called on `&mut [MaybeUninitSlice<'_>]` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`{type error}: Sized`
which is required by `MaybeUninitSlice<'_>: Sized`
error[E0599]: the method `len` exists for mutable reference `&mut [MaybeUninitSlice<'_>]`, but its trait bounds were not satisfied
--> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/socket2-0.5.8/src/lib.rs:710:77
|
390 | pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
| ------------------------------- doesn't satisfy `MaybeUninitSlice<'_>: Sized`
...
710 | sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
| ^^^ method cannot be called on `&mut [MaybeUninitSlice<'_>]` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`[MaybeUninitSlice<'_>]: ExactSizeIterator`
which is required by `&mut [MaybeUninitSlice<'_>]: ExactSizeIterator`
`{type error}: Sized`
which is required by `MaybeUninitSlice<'_>: Sized`
Some errors have detailed explanations: E0061, E0308, E0412, E0422, E0425, E0432, E0433, E0583, E0599.
For more information about an error, try `rustc --explain E0061`.
error: could not compile `socket2` (lib) due to 223 previous errors
error: could not compile `tokio` (lib) due to 1 previous error
Typescript -> wasm 編
Typescriptにおけるwasm生成
Porffor
- Publickey | JavaScript/TypeScriptからWebAssemblyやネイティブバイナリを生成するコンパイラ「Porffor」の開発が加速へ、開発者がフルタイムで取り組み
- 現在はpre-alpha状態で2025年に利用可能になる模様なので様子見です
AssemblyScript
- Zenn | TypeScriptでWebAssemblyに入門する
- 現状だとこれが選択肢になりそうな感じでした。
パッケージのインストール
bun add -d assemblyscript
assembly/
配下に以下のようなindex.ts
が生成されます。
// The entry file of your WebAssembly module.
export function add(a: i32, b: i32): i32 {
return a + b;
}
package.json
にはAssemblyScript用のコマンドが追加されます。
"scripts": {
"asbuild:debug": "asc assembly/index.ts --target debug",
"asbuild:release": "asc assembly/index.ts --target release",
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
}
wasm化するTypescriptコードにエントリポイントを追加する
初期化したままだとwasmファイル自体が実行可能バイナリにならないため、
エントリポイントとなる関数定義を_start()
として追加します。
export function add(a: i32, b: i32): i32 {
return a + b;
}
export function _start(): i32 {
return add(10, 20);
}
wasmビルド
以下のコマンドでassmebly/index.tsを元にwasmを生成します
bun run asbuild
build配下にdebug.wasmとrelease.wasmが生成されます。
wasmファイルサイズは311Bと112Bでした。軽量です。
wasmを実行
wasmer build/release.wasm
_start()の戻り値がコンソールに出力されました。
wasmtime build/release.wasm
wasmtimeではwarningが出たもののwasmer同様に実行できました。
wasmのdockerコンテナ化
FROM scratch
COPY release.wasm /release.wasm
ENTRYPOINT [ "/release.wasm" ]
docker buildx build --platform wasi/wasm -t username/wasm-handson-ts .
--platform
に指定したwasi/wasm
でビルドするための要件を満たせずに以下のエラーが出ました。
DockerDesktopでwasmを利用可能にする
Docker | Wasm workloads (Beta)を参考にDockerDesktopの設定を変更します。
再びwasmのdockerコンテナ化
DockerDesktopを再起動した後、docker imageのビルドを実行します。
docker buildx build --platform wasi/wasm -t username/wasm-handson-ts .
docker images
で確認するとimageが生成されていました。
サイズは僅か11.3KBと軽量です。
コンテナ化したwasmをdocker runで実行する
実行時には--runtimeと--platformの指定が必要です。
DockerDesktopでEnable Wasm
にチェックを入れて再起動した時に wasm実行用のランタイムがインストールされ、--runtime
で指定できるようになります。
docker run --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm username/wasm-handson-ts
wasm + docker のまとめ
- 現状だとwasmをdocker imageとしてビルドするにも、それをコンテナとして実行するにもDockerDesktopの環境が必要です
- GithubActionsなどのCI環境で手軽にwasm製のdocker imageをdocker runするには手間が掛かりそうに感じました
言語毎のwasm化やってみた感想
- Python : 人類には早すぎた気がします、py2wasmの今後に期待
- Rust : wasm生成が簡単・高速なのがgood
- Typescript : 型(
i32
など)や作法が通常のtsと異なる点で戸惑うかも
webサーバを立てるタイプのコードのwasm化が難しい理由
ChatGPTやGithubCopilotに聞いてみました。
- wasmは基本的にOSのファイルシステムやネットワークにはアクセスできない仕様(だからセキュア)
- OSの機能にアクセスするためにWASI(WebAssemblySystemInterface)の仕様がある
- wasmer等の各種wasm実行環境がWASIの実装を提供していれば、wasmからWASIを通してOSの機能にアクセスできるようになる
- 現状ではネットワークソケットや非同期処理、ファイルシステム周りの制約がクリアされておらず、
actix-web
等を使ったwebサーバ構築コードはwasm化できない - これらの制約に対応するためのWASI仕様が策定され、各種wasm実行環境が実装を提供するまで待つ必要がある
まとめ
- CloudRunやAWS ECSでwasmをバックエンドAPIとして動かすにはまだ早い...かも
- WASIの仕様とwasm実行環境の実装が追いつき、webサーバ構築系のパッケージごとwasm化できるようになるまでは難しそう
- wasmをコンテナとして動かすことができたのはあくまでもDockerDesktop環境においてなので、その他の環境(例えばGithubActionsのCI環境など)で特にカスタムせずとも暗黙に実行できるようになるまでは用途が限定されそう