Ruby 3.2 で WASI の WebAssembly を Docker で動かす
www.ruby-lang.org
docs.docker.com
Ruby 3.2 がリリースされて、WebAssembly や WASI が動くようになったのと、Docker Desktop に WebAssembly ランタイム(WasmEdge)が統合されたりしたので両方やってみたメモ。
公式 README
依存
wasi-vfs
Ruby の WebAssembly は Ruby 本体の wasm ファイルも必要になるらしい。wasi-vfs を使ってその Ruby 本体とスクリプトを合体するとのこと。
なので、まずはそれを可能にするツールである wasi-vfs をインストールする。macOS なので手っ取り早く Homebrew 使ってしまった。
$ brew tap kateinoigakukun/wasi-vfs https://github.com/kateinoigakukun/wasi-vfs.git $ brew install kateinoigakukun/wasi-vfs/wasi-vfs
が、Rust での cargo install が走るので普通にバイナリの Zip を解凍する方が早そうだった。
Wasmtime
WASI ランタイムの実装のひとつである Wasmtime も先にインストールする。今のところ、Wasmtime はランタイムの五強の中で僅差で一番使われている位置にいるらしい。
ref. https://blog.scottlogic.com/2022/06/20/state-of-wasm-2022.html
$ brew install wasmtime
Ruby で WASI の WebAssembly を動かす
README どおりやってみる。
$ curl -LO https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-head-wasm32-unknown-wasi-full.tar.gz $ tar xfz ruby-head-wasm32-unknown-wasi-full.tar.gz $ mv head-wasm32-unknown-wasi-full/usr/local/bin/ruby ruby.wasm $ mkdir src $ echo "puts 'Hello'" > src/my_app.rb $ wasi-vfs pack ruby.wasm --mapdir /src::./src --mapdir /usr::./head-wasm32-unknown-wasi-full/usr -o my-ruby-app.wasm $ wasmtime my-ruby-app.wasm /src/my_app.rb Hello
できた。my-ruby-app.wasm は、約 32.8 MB となって、やはり Rust などで出す wasm よりもちょっと大きい印象。
Ruby で WASI の WebAssembly を Docker で動かす
作った my-ruby-app.wasm を Docker Desktop で動かしてみる。
Docker Desktop の v4.15.0 から WASI ランタイムの実装の1つである WasmEdge が同梱されたが、まだデフォルトでは有効になっていないらしく、設定の「Features in development」から「Use containerd for pulling and storing images」のチェックボックスにチェックを入れて再起動する。
※ 注意メッセージにあるとおり、現在は、WebAssembly が扱えるようになる代わり、既存の所持イメージやコンテナが消えてしまう。消えたように見えるが、実際には消えていなくて、再度チェックボックスのチェックを外して再起動すればちゃんと残っている
Dockerfile
FROM scratch COPY ./my-ruby-app.wasm /my-ruby-app.wasm ENTRYPOINT ["my-ruby-app.wasm"]
Docker ビルド
$ docker buildx build \ --platform wasi/wasm32 \ -t wasi-docker:ruby_wasm .
Docker 実行
$ docker container run --rm \ --runtime=io.containerd.wasmedge.v1 \ --platform=wasi/wasm32 \ wasi-docker:ruby_wasm \ /src/my_app.rb Hello
できた。Gemfile で色々ライブラリを使ったときはどうなるんだろう。
Rails のキャッシュストアとセッションストアに Redis を使う
Ruby 2.7.1 + Rails v6.0.3.2 + Redis
Gemfile
... gem 'redis' gem 'hiredis' ...
開発環境用の設定
config/environments/development.rb
# Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true #-------------------------------------------------------------------- # config.cache_store = :memory_store config.cache_store = :redis_cache_store, { url: 'redis://localhost:6379', expires_in: 30.minutes, namespace: 'foo_cache', } config.session_store :cache_store, key: "foo_session", expire_after: 1.day.to_i #-------------------------------------------------------------------- config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :null_store end
キャッシュを有効にする。
$ ./bin/rails dev:cache Development mode is now being cached.
AWS Lambda Ruby 2.7 の puts の返却値は nil ではなく文字数
メモ。
AWS Lambda でランタイムに Ruby 2.7 を選択する。そのとき puts を使うと返却値は nil ではなくて、出力した文字数が返る。
AWS Lambda
# lambda_handler.rb require 'json' def lambda_handler(event:, context:) ret_puts = puts "foo!!!!!" puts "ret_puts: #{ret_puts.inspect}" #=> 8 ("foo!!!!!".length) { statusCode: 200, body: JSON.generate('Hello from Lambda!') } end
ローカルで irb したとき
2.7.1 :001 > ret_puts = puts "foo!!!!!" foo!!!!! 2.7.1 :002 > puts "ret_puts: #{ret_puts.inspect}" ret_puts: nil => nil