Ruby スクリプトを wasmtime 上で走らせるまで
TL;DR;
次のステップで Ruby スクリプトを wasmtime 上で動かせます:
- wasmtime をインストールします
- WASI 対応 ruby.wasm をダウンロードします
- プロジェクトフォルダを作ります
- 仮装環境と実環境の対応を設定し、wasmtime を起動します
背景
- CRuby がベースの Wasm 化された Ruby 処理系 ruby.wasm が存在します
- WASI preview 1 に対応した ruby.wasm もリリースされています
- Wasm 処理系の wasmtime には CLI が存在し、コマンドライン上で WASI preview 1 に対応した Wasm モジュールを実行できます
実行の手順
上述の通り、次の手順で Ruby スクリプトを wasmtime 上で動作する Ruby 処理系で実行できます。
- wasmtime をインストールします。
- WASI 対応 ruby.wasm をダウンロードします。
- プロジェクトフォルダを作ります。
- 仮装環境と実環境の対応を設定し、wasmtime を起動します。
wasmtime をインストール
wamtime に書かれている通りに wasmtime の CLI をインストールします。
% curl https://wasmtime.dev/install.sh -sSf | bash
WASI 対応 ruby.wasm をダウンロード
GitHub からリリースバージョンをダウンロードします。今回は ruby-3_2-wasm32-unknown-wasi-full.tar.gz
を利用します。
プロジェクトフォルダを作成する
プロジェクト用のフォルダを作成します。次の例では hello-ruby-wasm
というフォルダを作成しています。
% mkdir hello-ruby-wasm
プロジェクトフォルダ内に lib
フォルダも作成します
% mkdir -p hello-ruby-wasm/lib
ruby.wasm の展開
プロジェクトフォルダ内に、ダウンロードした ruby.wasm を展開します。次の例では ~/Download
にダウンロードしたファイルを展開しています。
% cd hello-ruby-wasm
% tar zxf ~/Download/ruby-3_2-wasm32-unknown-wasi-full.tar.gz
現在のディレクトリ構成は次のようになっています:
% tree -L 3
.
├── 3_2-wasm32-unknown-wasi-full
│ └── usr
│ └── local
└── lib
ruby.wasm の動作確認
Ruby 処理系を起動し、動作することを確認します。3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby
が実行する Wasm 化された Ruby 処理系です。
% wasmtime 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby -- --version
ruby 3.2.0 (2022-12-25 revision a528908271) [wasm32-wasi]
--
を引数に指定し忘れると次のようなエラーが出るので注意してください。--
は wasmtime 用のコマンドライン引数と、Ruby 処理系に用のコマンドライン引数の区切りを表します。--
がないと --version
オプションも wasmtime 向けの引数であると解釈されるため、エラーが表示されます。
% wasmtime 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby --version
error: unexpected argument '--version' found
tip: to pass '--version' as a value, use '-- --version'
Usage: wasmtime <MODULE> [ARGS]...
hello.rb を作成
スクリプトを作成します。次のような hello world スクリプトを lib
フォルダ内に作ります。
puts "content-type: text/plain"
puts ""
puts "Hello, World"
プロジェクトフォルダは次のようになっています。
% tree -L 3
.
├── 3_2-wasm32-unknown-wasi-full
│ └── usr
│ └── local
└── lib
└── hello.rb
ネイティブ版の Ruby 処理系で実行すると、次のように出力されます:
% ruby lib/hello.rb
content-type: text/plain
Hello, World
wasmtime 上で実行
ただ wasmtime で実行すると、次のようなエラーが表示されます。これは wasmtime がサンドボックスの中で Wasm モジュールを実行するため、必要なファイルへアクセスできないことが原因です。
% wasmtime 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby -- lib/hello.rb
ruby: Capabilities insufficient -- lib/hello.rb (LoadError)
wasmtime が lib/hello.rb
へアクセスするには、次のように--dir
オプションでプロジェクトフォルダへのアクセスを許可します。
% wasmtime --dir . 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby -- lib/hello.rb
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
`syntax_suggest' was not loaded.
content-type: text/plain
Hello, World
上記の例ではスクリプトが実行されていますが、必要なライブラリーがロードできないためエラーが発生しています。
ruby.wasm は実行に必要なファイルが /usr
以下からも探します。サンドボックスの中から /usr
へアクセスできません。
wasmtime にはフォルダのマッピング機能があります。これは wasmtime の実行している環境(ホスト環境と呼びます)のフォルダを、別のフォルダとして実行されている Wasm モジュールにアクセスさせられます。
例えば、ホスト環境の ./root-for-wasm
フォルダを、Wasm の実行環境(ゲスト環境と呼びます)のルートフォルダ(/
)とすることが可能です。
この機能を使ってプロジェクトフォルダ内にある3_2-wasm32-unknown-wasi-full/usr
フォルダを、ゲスト環境の /usr
と設定します。
設定したコマンドの例は次のようになります:
% wasmtime --dir . --mapdir /usr::./3_2-wasm32-unknown-wasi-full/usr 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby -- lib/hello.rb
content-type: text/plain
Hello, World
--mapdir
オプジョンがフォルダの対応関係を指定するオプションです。::
の前がゲスト環境でのパス、後がホスト環境でのパスになります。
まとめ
「Wasm はサンドボックスの中で実行される」とはよく聞きますが、それを実感できる内容だったように思います。
標準出力を利用する Wasm モジュールの実行が何の障害もなく行えたのも、WASI への対応が成熟してきていることを実感できました。
レファレンス
- Ruby in WebAssembly:WASM モジュールが実行できるサーバサイド実行環境 Spin で ruby.wasm を利用するチュートリアルです
Discussion