ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

Pure Ruby なWASM Runtimeを型付きで実装してみた

Wardite というWASM Runtimeを作っていました。

github.com

今の所、「RustでWasm Runtimeを実装する」の内容を一通り実装した段階です。

zenn.dev

以下のようなwasmプログラムを動かせます。

(module
  (func $fib (export "fib") (param $n i32) (result i32)
    (if
      (i32.lt_s (local.get $n) (i32.const 2))
      (then (return (i32.const 1)))
    )
    (return
      (i32.add
        (call $fib (i32.sub (local.get $n) (i32.const 2)))
        (call $fib (i32.sub (local.get $n) (i32.const 1)))
      )
    )
  )
)
$ gem install wardite
$ wat2wasm fib.wat 
$ wardite ./fib.wasm fib 20
return value: 10946
続きを読む

手を動かして理解するwasmCloud 〜旅立ち編〜

wasmの可能性を「理解」したくてwasmCloudをさわってみている。動かしてみたメモや感想などを晒しておく

免責事項

wasmCloud公式のGetting Startedに書いてあるレベルのことしかしていないので、これから学ぶ人は直接そっちを読んだほうがいいと思う。

wasmcloud.com

この記事は単に「副読本」というぐらいの認識でいいかなという感じ。


環境

multipassでセットアップした Ubuntu 22.04 (まだ24にしてない) カーネルの更新もサボってる...。

ubuntu@primary:~$ uname -a
Linux primary 5.15.0-118-generic #128-Ubuntu SMP Fri Jul 5 09:30:28 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux

雑に立ち上げ

wash はすぐにインストールできる。

まず必要なプロセスを立ち上げるらしい。

$ wash up

>>> ⡋⠁  Starting wadm ...                                                                                                                
   Downloading new wadm from https://github.com/wasmcloud/wadm/releases/download/v0.14.0/wadm-v0.14.0-linux-aarch64.tar.gz
>>> ⠈⢙  Starting wadm ...                                                                                                                
   Saved wadm to /home/ubuntu/.wash/downloads/wadm
🏃 Running in interactive mode.
🎛️ To start the dashboard, run `wash ui`
🚪 Press `CTRL+c` at any time to exit
2024-08-25T09:19:54.778375Z  INFO async_nats_wrpc: event: connected
2024-08-25T09:19:54.780084Z  INFO async_nats_wrpc: event: connected
2024-08-25T09:19:54.785723Z  INFO wasmcloud_host::wasmbus: created bucket with 1 replica bucket=LATTICEDATA_default
2024-08-25T09:19:54.787211Z  INFO wasmcloud_host::wasmbus: created bucket with 1 replica bucket=CONFIGDATA_default
2024-08-25T09:19:54.790602Z  INFO wasmcloud_host::wasmbus: wasmCloud host started host_id="NAM6TTXMMZNG5VLQTE3JMIPCPNXYGCWC45E4VCEYJGHAGLX4CZ7EW4NA"

ドキュメントの通りNATSと、wadmというプロセスが立ち上がった。

   \_ wash up
       \_ /home/ubuntu/.wash/downloads/nats-server -js --addr 127.0.0.1 --port 4222 --pid /home/ubuntu/.wash/downloads/nats.pid --config /home/ubuntu/.wash/downloads/nats.conf
       \_ /home/ubuntu/.wash/downloads/wadm --nats-server 127.0.0.1:4222
       \_ /home/ubuntu/.wash/downloads/v1.2.0/wasmcloud_host

NATS は汎用メッセージングサーバー。仕事でも使ったことがあるが、極めて安定していて使いやすい(特にGoからだとライブラリが整備されていて使いやすい)、その上パフォーマンスが出る、みたいなOSSのPub/Subサーバで、個人的に今回採用されたのも納得。

ちょっと性質が違うけど例えば昔ZeroMQとかを使ったことがある人は、同じような感じで使えたりするので別途試してもいいと思います。

ちなみに NATS はembedded NATS serverという、組み込んで使うより便利なやり方もあるんだけど、これはGoでしかできなくて(NATS serverはGo製のため)、wadmの言語はRustなのでNATSは別立てになっているのだろう。

多分NATSは本当にメッセージの仲介しかしていないはずで、ポイントはwadmにありそう。

github.com

READMEを見ると、

  • アプリケーションのスペック管理
  • stateの監視
  • Reconcile周り

が責務ですよと言っており、Kubernetesで言うコントロールプレーン+Kubelet相当を1台で引き受ける、簡易版インスタンスマネージャーといったところだろうか。wasmのインスタンス?ワーカー?は lattice(格子模様) という単位でまとめられているらしい。あと、wadm自身だけでクラスタリング等ができるかは調べていない。

あと、 wasmcloud_host と言うプロセスが立ち上がってる。これは何。

docs.rs

それぞれのwasmインスタンスはActorという単位で管理される、HostはそれらとOSをWASIで繋ぎます、みたいに読める。では実はこっちがKubelet相当? ちょっと色々いじりながら理解したい感じ。

Hello world

さて、washを使ってプロジェクトのスケルトンが作れそうなので作る。今回はRust。

$ wash new component hello --template-name hello-world-rust
🔧   Cloning template from repo wasmCloud/wasmCloud subfolder examples/rust/components/http-hello-world...
🔧   Using template subfolder examples/rust/components/http-hello-world...
🔧   Generating template...
[ 1/62]   Done: .gitignore
[ 2/62]   Done: Cargo.toml
[ 3/62]   Done: README.md
[ 4/62]   Done: local.wadm.yaml
[ 5/62]   Skipped: project-generate.toml
[ 6/62]   Done: src/lib.rs
[ 8/62]   Done: wadm.yaml
[ 9/62]   Done: wasmcloud.toml
[10/62]   Done: wit/deps/cli/command.wit
[11/62]   Done: wit/deps/cli/environment.wit
[12/62]   Done: wit/deps/cli/exit.wit
✨   Done! New project created /home/ubuntu/hello

結構色々なものがつくられるのだが、Cargo.tomlを見るとdependencywit-bindgen = "0.30" が勝手に入ることとなる。

生成された src.rs はこういう感じで:

wit_bindgen::generate!({
    generate_all
});

use exports::wasi::http::incoming_handler::Guest;
use wasi::http::types::*;

struct HttpServer;

impl Guest for HttpServer {
    fn handle(_request: IncomingRequest, response_out: ResponseOutparam) {
        let response = OutgoingResponse::new(Fields::new());
        response.set_status_code(200).unwrap();
        let response_body = response.body().unwrap();
        ResponseOutparam::set(response_out, Ok(response));
        response_body
            .write()
            .unwrap()
            .blocking_write_and_flush(b"Hello from Rust!\n")
            .unwrap();
        OutgoingBody::finish(response_body, None).expect("failed to finish response body");
    }
}

export!(HttpServer);

これはパブリッククラウドのFunction as a Serviceのコードを彷彿とさせる。こういう感じのプラットフォームをOSSで動かせるというのは期待が持てるなと思う。

で、wit_bindgenの generate_all は多分生成されたwitを自動で読んで色々やってくれる感じか? 例えばこういうファイルが生成されている。

package wasi:cli@0.2.0;

world command {
  include imports;

  export run;
}

あと生成された wasmcloud.toml には

[component]
wit_world = "hello"
wasm_target = "wasm32-wasi-preview2"

こういう記述もある。なのでここもビルド時に? 参照しそう。

一旦ビルドして動かしてみる(cargoはセットアップすること)。

$ wash build
...
Component built and signed and can be found at "/home/ubuntu/hello/build/http_hello_world_s.wasm"

wit/world.wit というのが生成されたっぽい、これが「関数」のエントリポイントとなりそう。

package wasmcloud:hello;

world hello {
  export wasi:http/incoming-handler@0.2.0;
}

デプロイはこう。即で終わる。

$ wash app deploy wadm.yaml

Deployed application "rust-hello-world", version "01J64FG1JQ4Y0YRAH4NGK7N636"

$ wash app list

                                                                                                                   
  Name                                  Deployed Version                      Status                               
  rust-hello-world                      01J64FG1JQ4Y0YRAH4NGK7N636            Deployed                             
    └ HTTP hello world demo in Rust, using the WebAssembly Component Model and WebAssembly Interfaces Types (WIT)  
$ curl localhost:8080
Hello from Rust!

できました。

wadm.yaml をみると、レプリカ数などのKube感あるパラメータを発見できるので、今度色々いじってみよう。


もう一つポイントは、Pythonはすでにサポートされている。これはcomponentize-pyベースでやってるらしいのでバイナリサイズが大きいのかもしれないが、とにかく動くには動くらしい(次回試します)。

Rubyは?Ruby...

If you prefer working in a language that isn't listed here, let us know!

そうなりますよね。

正直、 mruby/edge 的なアプローチじゃなく、 MRI のコア部分(コアとは)をwasmにした後でcomponentize-pyみたいなアプローチでくっつける方も掘り進みがいがあるんだろうなと思うが、いずれにせよ理解の解像度が低い(componentize-pyって何してるの?から)。


なんかちょっと調べるだけでやるべきことが多く、前途多難ですが、オーエスエスにリハビリすべくやっていきます。

RubyKaigi 2024で話そうと思ってること

RubyKaigi 2024二日目(16th, May)に登壇します。

rubykaigi.org

本ブログで前々から言っている mruby/edge に関する発表です。

udzura.hatenablog.jp

構想自体ははっきり言って去年のRubyKaigi CfPオープンぐらいからあって、地道な素振りをしていた。懐かしい。

udzura.hatenablog.jp

udzura.hatenablog.jp

発表は英語で行うらしい。日本語で事前情報がある方が頭に入りやすい方も多いかと思うので、メモがてら話そうとしていることを残しておく。無論当日までに変わる可能性もある*1

*1:翻訳者向け事前提出がない英語発表者のアドを全力で活かすスタイル

続きを読む

mruby/edge でimport/exportする関数における、文字列の仕様の話

はじめに

  • この文字列の扱いの仕様はアルファ版であり、もっといいアイデアが出たら大胆に変更します
  • 何もわかっていないんですが、 WASM Component Model では string の扱いも言及されており 、それに合わせたいい感じのやつにきっと将来なるでしょう
  • ご意見ご感想を歓迎します。特に、私はRubyKaigiに出現するので、そこでリアタイでコミュニケーションとれると飛び跳ねて喜びます。

mec v0.3.0 より、mruby/edgeでimportするJavaScript(など)の関数、あるいはexportするRuby側のトップレベルメソッドの引数や戻り値で、Stringを扱えるようにした。

ここで、WebAssembly(WASM)においては現状文字列を扱うのはトリックが必要なので、その辺踏まえた仕様と使い方をまとめておきたい。

English version will show up on RubyKaigi...

続きを読む