fbpx

RubyでWebAssemblyを試してみよう #ruby #WebAssembly #WASM #WASI

WebAssembly (WASM)を使うと、ウェブブラウザ上でJavaScript以外の言語を動作させることができます。過去記事にPythonを動作させた例もありました。本記事ではRubyを動かしてみます。

ruby.wasm

RubyでのWASMには、そのものずばりな名称の ruby.wasm があります。これを使ってブラウザ上で簡単なRubyスクリプトを動かしてみましょう。

puts "Hello, world!"

おなじみ Hello, world! を出力するだけのRubyスクリプトです。これをブラウザ上で動かすには Quick Example: Ruby on browser に従い、次のHTMLファイルを作成します。


<script src="https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@1.0.1/dist/browser.script.iife.js"></script>
<script type="text/ruby"><br />
    puts "Hello, world!"<br />
  </script>

このファイルをGoogle ChromeやFirefoxで開いてみると…何も起こりません。ブランクページが開かれるだけです。何か間違っているのでしょうか? ここでGoogle Chromeならデベロッパーツール、Firefoxならウェブ開発ツールを開き、コンソールを見てみましょう。

Chrome:

Firefox:

このように、ブラウザのウィンドウ内ではなく、コンソールに Hello, world! が出力されていました!

次はブラウザのウィンドウ内に Hello, world! を出力してみましょう。 ruby.wasmのJSモジュール を利用することで、Rubyスクリプト中からJavaScriptを操作できます。


<script src="https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@1.0.1/dist/browser.script.iife.js"></script>
<script type="text/ruby"><br />
    require 'js'<br />
    JS.global[:document].call(:write, "Hello, world!")<br />
  </script>


<script src="https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@1.0.1/dist/browser.script.iife.js"></script>
<script type="text/ruby"><br />
    require 'js'<br />
    message = JS.global[:document].call(:getElementById, 'message')<br />
    message[:innerText] = "Hello, world!"<br />
  </script>
<div id="message"></div>

このどちらも、ブラウザのウィンドウ内に Hello, world! を描画します。

WASI (WebAssembly System Interface)

WASI (WebAssembly System Interface)とは簡単に言うと、WASMをブラウザ外で動作させるための仕組みです。WASIは「コンテナの次」となるポータブルでセキュアな仕組みとして、にわかに注目を集めています。ここからはRubyスクリプトを WASI で動かしてみましょう。

ruby.wasi 公式ページには Quick Example: How to package your Ruby application as a WASI application として、おなじみ Hello, world! を出力するだけのRubyスクリプトを WASI アプリケーションに変換して動かす方法が記載されています。これに従って進めていきましょう。

まず、Hello, world! を出力するだけのRubyスクリプトを作成します。

% mkdir src
% echo 'puts "Hello, world!"' > src/hello.rb
% ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux-gnu]
% ruby src/hello.rb
Hello, world!
%

このように x86_64-linux 上で動作しています。

次に wasi-vfs を用意します。これはRubyスクリプトを WASI アプリケーションに変換する際に利用します。

% export WASI_VFS_VERSION=0.2.0
% curl -LO "https://github.com/kateinoigakukun/wasi-vfs/releases/download/v${WASI_VFS_VERSION}/wasi-vfs-cli-x86_64-unknown-linux-gnu.zip"
% unzip wasi-vfs-cli-x86_64-unknown-linux-gnu.zip
% ./wasi-vfs
wasi-vfs-cli 0.2.0

USAGE:
wasi-vfs

FLAGS:
-h, --help Prints help information
-V, --version Prints version information

SUBCOMMANDS:
help Prints this message or the help of the given subcommand(s)
pack Package directories into Wasm module

そして ruby.wasm の WASI 互換環境を準備します。

% curl -LO https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-3_2-wasm32-unknown-wasi-full.tar.gz
% tar xf ruby-3_2-wasm32-unknown-wasi-full.tar.gz
% ls ./3_2-wasm32-unknown-wasi-full/usr/local/bin/
bundle bundler erb gem irb racc rake rbs rdbg rdoc ri ruby typeprof
%

この ruby は WASI 互換環境なので Linux 上では実行できません。

% ./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby
zsh: 実行形式エラー: ./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby
% file ./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby
./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby: WebAssembly (wasm) binary module version 0x1 (MVP)
%

ここで Wasmtime を準備しましょう。これは WASI アプリケーションを実行できるスタンドアローンランタイムです。

% curl -LO https://github.com/bytecodealliance/wasmtime/releases/download/v7.0.0/wasmtime-v7.0.0-x86_64-linux.tar.xz
% tar xf wasmtime-v7.0.0-x86_64-linux.tar.xz
% ./wasmtime-v7.0.0-x86_64-linux/wasmtime 3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby -- -v
ruby 3.2.0 (2022-12-25 revision a528908271) [wasm32-wasi]
%

このように wasmtime を噛ませることによって、 WASI 互換環境の ruby を実行することができました。

では、wasi-vfs と WASI 互換環境の ruby を使って、Rubyスクリプトを WASI アプリケーションに変換してみましょう。

% ./wasi-vfs pack ./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby --mapdir /src::./src --mapdir /usr::./3_2-wasm32-unknown-wasi-full/usr -o hello.wasm
% file ./hello.wasm
./hello.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
%

Hello, world! を出力する WASI アプリケーションを wasmtime で実行してみます。

% ./wasmtime-v7.0.0-x86_64-linux/wasmtime hello.wasm -- /src/hello.rb
Hello, world!
%

このようにHello, world!を出力できました。が、「 -- /src/hello.rb」のように WASM パッケージ内で実行するスクリプトを引数に指定する必要があるので、本当に WASI アプリケーションとして動いているの? ローカルファイルを実行しているんじゃないの? という疑問があるでしょう。

% echo "puts RUBY_DESCRIPTION" >> src/hello.rb
% ruby ./src/hello.rb
Hello, world!
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux-gnu]
% ./wasmtime-v7.0.0-x86_64-linux/wasmtime hello.wasm -- /src/hello.rb
Hello, world!
%

ソースコードを変更して実行しても反映されていないので、どうやら実際に WASI アプリケーションが動作しているようです。もう一度ビルドして確認してみましょう。

% ./wasi-vfs pack ./3_2-wasm32-unknown-wasi-full/usr/local/bin/ruby --mapdir /src::./src --mapdir /usr::./3_2-wasm32-unknown-wasi-full/usr -o hello.wasm
% ./wasmtime-v7.0.0-x86_64-linux/wasmtime hello.wasm -- /src/hello.rb
Hello, world!
ruby 3.2.0 (2022-12-25 revision a528908271) [wasm32-wasi]
%

問題なく WASI アプリケーションとして動作していました。

また、wasmtime 以外のWASMスタンドアローンランタイムとして Wasmer があります。こちらも試してみましょう。

% curl -LO https://github.com/wasmerio/wasmer/releases/download/v3.2.0/wasmer-linux-amd64.tar.gz
% tar xf wasmer-linux-amd64.tar.gz
% ./bin/wasmer hello.wasm -- /src/hello.rb
Hello, world!
ruby 3.2.0 (2022-12-25 revision a528908271) [wasm32-wasi]
%

wasmtime と同様に wasmer でも実行できました。

まとめ

本稿では Ruby で WASM / WASI を試してみました。文中で述べたように、WASIは「コンテナの次」となるポータブルでセキュアな仕組みとして注目を集めています。クリエーションラインでは引き続き WebAssmbly について調査していきたいと思います。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA