3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OptimindAdvent Calendar 2024

Day 14

WebAssemblyでバックエンドAPIを動かそうとしてダメだった話

Last updated at Posted at 2024-12-13

この記事は何?

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分以上待たされました。

image.png

wasm出力をGithub ActionsのCDスクリプトで行う場合は実行時間をかなり取られることになるため、コスト増が懸念点となるかもしれないと感じました。

FastAPI + wasm の実行時エラー

Google CloudRunやAWS ECSでwebサーバとして実行するwasmを作ってみたかったため、
FastAPIで実装したpythonコードをwasm化し、wasmtime及びwasmerで実行してみました。

wasmtime main.wasm

image.png

wasmer run main.wasm

image.png

いずれも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が生成されます。

image.png

cargo newしただけのhello-worldなコードのwasmファイルは63KBでした。

image.png

wasmとして実行する

wasmer target/wasm32-wasip1/release/wasm-handson-rust.wasm
wasmtime target/wasm32-wasip1/release/wasm-handson-rust.wasm

コードはただのhello-worldなので、普通に実行できました。

image.png

RustコードでwebAPIを実装する

cargo add actix-web
main.rs
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として動作していることが確認できました。

image.png

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

AssemblyScript

パッケージのインストール

bun add -d assemblyscript

assembly/配下に以下のようなindex.tsが生成されます。

assembly/index.ts
// The entry file of your WebAssembly module.

export function add(a: i32, b: i32): i32 {
	return a + b;
}

package.jsonにはAssemblyScript用のコマンドが追加されます。

package.json
  "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()として追加します。

assembly/index.ts
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が生成されます。

image.png

wasmファイルサイズは311Bと112Bでした。軽量です。

image.png

wasmを実行

wasmer build/release.wasm

_start()の戻り値がコンソールに出力されました。

image.png

wasmtime build/release.wasm

wasmtimeではwarningが出たもののwasmer同様に実行できました。

image.png

wasmのdockerコンテナ化

Dockerfile
FROM scratch
COPY release.wasm /release.wasm
ENTRYPOINT [ "/release.wasm" ]
docker buildx build --platform wasi/wasm -t username/wasm-handson-ts .

--platformに指定したwasi/wasmでビルドするための要件を満たせずに以下のエラーが出ました。

image.png

DockerDesktopでwasmを利用可能にする

Docker | Wasm workloads (Beta)を参考にDockerDesktopの設定を変更します。

image.png

image.png

再びwasmのdockerコンテナ化

DockerDesktopを再起動した後、docker imageのビルドを実行します。

docker buildx build --platform wasi/wasm -t username/wasm-handson-ts .

docker imagesで確認するとimageが生成されていました。
サイズは僅か11.3KBと軽量です。

image.png

コンテナ化した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に聞いてみました。

image.png

image.png

  • wasmは基本的にOSのファイルシステムやネットワークにはアクセスできない仕様(だからセキュア)
  • OSの機能にアクセスするためにWASI(WebAssemblySystemInterface)の仕様がある
  • wasmer等の各種wasm実行環境がWASIの実装を提供していれば、wasmからWASIを通してOSの機能にアクセスできるようになる
  • 現状ではネットワークソケットや非同期処理、ファイルシステム周りの制約がクリアされておらず、actix-web等を使ったwebサーバ構築コードはwasm化できない
  • これらの制約に対応するためのWASI仕様が策定され、各種wasm実行環境が実装を提供するまで待つ必要がある

image.png

まとめ

  • CloudRunやAWS ECSでwasmをバックエンドAPIとして動かすにはまだ早い...かも
  • WASIの仕様とwasm実行環境の実装が追いつき、webサーバ構築系のパッケージごとwasm化できるようになるまでは難しそう
  • wasmをコンテナとして動かすことができたのはあくまでもDockerDesktop環境においてなので、その他の環境(例えばGithubActionsのCI環境など)で特にカスタムせずとも暗黙に実行できるようになるまでは用途が限定されそう
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?