ローファイ日記

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

最もプリミティブなやり方(WAT)で入門するWASM Component Model

WebAssembly Advent Calendar 2024の18日目の記事です。

qiita.com

@udzura です。趣味でPure RubyのWASM Runtimeを作ったり、mrubyをベースにRubyスクリプトをWASMバイナリに固めるくんのPoCを作ったりしています(なおいわゆるruby.wasmにはほとんど関わっていません)。

今日は、Component Modelについて何もわからなかったので、素人なりに手を動かしてわかったことのメモをしていこうと思います。Component Modelの肌感覚を身につける上で、本記事が少しでも助けになれば幸甚です。

Component ModelでWASMバイナリを「リンク」する

Component Modelの学習コンテンツの一つとして、「Building a Calculator of Wasm Components」というチュートリアルがあるのをご存知でしょうか?

github.com

このチュートリアルでは、Rustとwit-bindgenをベースに、

  • docs:adder/add@0.1.0 インタフェースを実装したバイナリ
  • docs:adder/add@0.1.0 インタフェースに依存する、 docs:calculator/calculate@0.1.0 インタフェースを実装したバイナリ

をそれぞれ組み合わせて、 wasi:cli/run@0.2.0 を実装したバイナリにリンクして実行させるということができます。リンクには wac というコマンドを使っています。

が、wit-bindgenをベースににしているため、個人的にはどうにも隠蔽されまくっている感がすごくて...、最もフィジカルで、最もプリミティブで、そして最もフェティッシュなやり方でComponent Modelを行かせていただきたいと思いました。

そこで、wat表記のプログラムを組み合わせる形でComponent ModelベースのWASMの「リンク」を実行する手順を作ってみました。

本手順の途中で CanonicalABI のlistとlowerについても少し触れる予定です。

続きを読む

JXAでMacのちょっとしたブラウザ操作を自動化する

SmartHR Advent Calendar 2024、14日目の記事です。昨日のご担当は tnagatomi さんでした!

qiita.com

株式会社SmartHRのプロダクトエンジニア @udzura です。労務基本機能を開発するチームで働いています。

今日は最近知った JXA で、Macのちょっとしたブラウザ操作を自動化する例をご紹介します。

JXA とは?

Macにはマシン上の操作を自動化するためのフレームワークが同梱されています。

例えばAutomatorのようなアプリを使って仕事を自動化するのですが、 AppleScript という言語もよく知られていました。このスクリプト言語を用いて特定のアプリケーションやウィンドウを操作したり、システム設定などの変更を記述することができます。

以下は開いているFinderの最初のウィンドウをドックに格納するスクリプトです。

tell application "Finder"
    set collapsed of Finder window 1 to true
end tell

上記のスクリプトをファイルに保存して osascript というコマンドに渡せば実行ができます。

ですが、ご覧の通りAppleScriptはやや癖があり、覚えるのが結構大変なものでした。そこで満を持してなのか、JavaScriptで同様の操作が可能になる JXA (JavaScript for Automation) が登場しました。2014年に出たOS X Yosemiteから使えるようになったそうです。

以下は、先ほどのFinderをドックに格納するためのスクリプトJavaScriptで書き直したものです。

const finder = Application("Finder");
const window = finder.windows[0];
window.collapsed = true;

これを osascript -l JavaScript とオプション付きのコマンドに渡して実行すればOKです。ちなみに、 osascript -l JavaScript -i というコマンドを打つことでREPLも立ち上げられるので、ちょっとした確認や作業にとても便利です。

JXAでSlackに貼るためのリンクを取得してみる

もう少し具体的な応用例をお見せします。

続きを読む

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って何してるの?から)。


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