Rubyを書けばCloudflare Workersに
そのままデプロイできるってマ⁉️

Uzumibiの紹介, そしてUzumibi2の展望(?)


Cloudflare Tech Talk in Fukuoka

Uchio Kondo / @udzura

自己紹介

Uchio Kondo

Uchio Kondo

@udzura

  • 株式会社SmartHR プロダクトエンジニア
  • Fukuoka.rb オーガナイザー
  • エンジニアカフェ ハッカーサポーター
  • Ruby / mruby / Rust / Wasm
  • 『入門eBPF』(オライリージャパン)翻訳

宣伝その1

Fukuoka.wasm をやります!!!!!!!!1

  • 7月にFukuoka.edge と言う勉強会をやってみたが、
    実はWasmの話をしたい人も多い?(感想)
  • なので11月ぐらいになりますが、何かします!!!!!!!!!
  • 何かしたい方、ぜひお声がけください

宣伝その2

イメージ

9/17 にもくもく会をします。

connpassイベントページのQRコード

今日のトピック

はじめに

Cloudflare WorkersでRubyを動かすモチベって?

前半

Uzumibiの紹介

すぐ試せます

後半

今開発しているもの
「Uzumibi2(仮)」

今日のトピック

はじめに

Cloudflare WorkersでRubyを動かすモチベって?

前半

Uzumibiの紹介

すぐ試せます

後半

今開発しているもの
「Uzumibi2(仮)」

Cloudflare WorkersでRubyを動かしたい!

  • 「何言ってんの?」と思われそうなのでまずその話をしますね...

Cloudflareは激アツプラットフォーム

  • Cloudflare Workersが大流行
  • 速い、軽い、簡単。無料枠も充実

Rubyコミュニティからあまり注目されていない

  • 勿体無い!!!!
  • しかし、なるべくRuby書きたいので、まあ今度...ってなりがち

ところで令和の時代、Rubyアプリケーションを
デプロイする先の選定は大変

  • Railsに限らずそんな感じ
  • 結局ECSとかCloud Runとか大手クラウドを利用
  • Kamal... いや〜

無料でサクッと始められるCloudflare Workersは...

  • 「強い」です by AI
💭  心の声
往時のHer ⭕️  ku ブームを思い出す。だがもっとすごいと個人的には思う

Cloudflare Workersでもruby.wasmはOK

  • RubyをWasmで動かす取り組み自体は結構前からある

しかし、10MB近くの容量に...

ruby+stdlib.wasm はおよそ 9 MB (gzip) 程度あるため、無料プラン (Free) の 3 MiB バンドル上限を超えてしまいます。

もっとサクッとRubyのコードを動かしたい

そこで... Uzumibi

今日のトピック

はじめに

Cloudflare WorkersでRubyを動かすモチベって?

前半

Uzumibiの紹介

すぐ試せます

後半

今開発しているもの
「Uzumibi2(仮)」

Uzumibiの紹介

  • Uzumibi is 何
  • できること
  • 仕組み
  • すぐに試せるよ! (demo)

Uzumibi is 何

  • Cloudflare WorkersでRubyのコードを動かしたい!を叶えるフレームワーク
  • コードジェネレータ付きですぐできる

Uzumibi: Rubyをいろいろな場所へデプロイ

Ruby code
Uzumibi
Cloudflare Workers
Fastly Compute
Google Cloud Run...

プラットフォームごとに adapter / template を用意

できること(Cloudflareでの対応状況)

  • Cloudflare WorkersでRubyアプリケーションを実行する
  • KV, Queue, Accessなどなども使える・連携できる

ライブデモでデプロイしちゃう

  • うまくいくんかな... 😅

仕組み

  • 独自のmruby VM
  • Rust
  • Wasm
  • Cloudflare Workers

独自(自作)のmruby VM(Rust)

  • mruby/edge と言う名前
  • mrubyは、軽量なRuby実装。CRubyとは違うバイトコードマシンを使う。
    • =そのバイトコードを解釈するVMを実装すれば動かせる
  • 今回「Rustでmruby VM作りてぇな〜」と思って自作したやつを利用した

詳細はワイのRubyKaigiの発表を見て!

RustなのでWasmにできる

  • RustからWebAssemblyを生成するのはかなり「楽」で「刺さる」用途です。 by AI

WasmならCloudflare WorkersのV8で動く

import wasmModule from "./app.wasm";

const instance = await WebAssembly.instantiate(
  wasmModule,
  { env: { /* Ruby向けのhost functions */ } },
);
const wasm = instance.exports;

リクエストをWasmに渡して、Rubyを実行する

  • ざっくり:
    • HTTPリクエストをエンコードしてWasmに渡す
    • Ruby(mruby)バイトコードを実行する
    • Rubyでリクエストを解釈する
    • レスポンスのRubyオブジェクトを再エンコード、Workersへ返却する

リクエストはWasmの中のRubyへ

HTTP Requestリクエスト
Worker JavaScriptリクエストを
エンコード
Wasmmruby/edge(Ruby VM)
Rubyアプリリクエストを解釈
処理を実行

Rubyアプリは、Wasm化したmruby/edge(Ruby VM)の上で動く

Rubyの実行結果をHTTPレスポンスに戻す

HTTP Responseレスポンス
Worker JavaScriptデコードして
Responseを作る
Wasmmruby/edge(Ruby VM)
RubyアプリRubyオブジェクトを
返す

Rubyの返り値をエンコードしてWasmから渡し、JavaScriptで復元する

すぐに試せますし、無料枠で動きます

  • Uzumibi入門でUzumibiを試せるようにしました!(一部内容編集中)

Uzumibi、よろしくお願いします!

One more thing...

今日のトピック

はじめに

Cloudflare WorkersでRubyを動かすモチベって?

前半

Uzumibiの紹介

すぐ試せます

後半

今開発しているもの
「Uzumibi2(仮)」

Uzumibi2(???)

Uzumibi で問題だな〜と思ってること

  • mrubyのVMが独自実装であること
    • 「いつものRubyかと思ったらそうじゃない」場面が引っ掛かるかも
    • AIがあるとはいえ、ライブラリをひたすら再発明はなんか...
💭  心の声
AI時代、車輪の再発明はすぐできるけど、「本当にユーザに欲しがられて使われるもの」を作るのは難しい。

そこでPicoRuby

  • PicoRubyは、mrubyをより小さくポータブルにしたもの
    • 実践的に組み込みに使えるようなエコシステムが育っている
  • PicoRuby 4.xの主力はmruby VM版。mruby本体やmruby-compilerの資産を使う

エコシステムが強いPicoRubyを動かしたい

  • CRubyは複雑だし、今回の用途に合わせるのはそもそも困難そう
💭  心の声
「組み込み向け」と言うのは本質的にWasm向けだな〜って思う。

PicoRubyをCloudflare Workersで動かしたい

  • PicoRubyには強いエコシステム・ユーザ・ファンがある
  • Cloudflare Workersでも動かせると面白がってもらえそう

やっていること

  • PicoRubyをCloudflare Workersで動くWasmにビルドした
  • 一緒に...
    • Rack互換レイヤを実装した!
💭  心の声
せっかくなのでベターなエコシステムにしたい

PicoRubyをWorkersで動かす概要図

Cloudflare Workers
request / response
↓ ↑
Wasm host
↓ ↑
Rack互換レイヤ
↓ ↑
PicoRuby + app.rb
開発時は自動リロード

Rack互換レイヤを実装した

  • Rack互換なので、こういうアプリが動く:
app = Proc.new do |env|
  [
    200,
    { "content-type" => "text/plain" },
    ["Hello, world!\n"]
  ]
end
Picoruby.run app

Rackとは何か?

  • RubyのWebサーバとWebアプリケーションをつなぐ共通インターフェース
  • 他の言語で言うと
    • PerlのPSGI
    • PythonのWSGI

Rack: サーバとフレームワークの間の規約

Server

Puma
Unicorn
WEBrick
CGI
...
Rack
Framework

Rails
Sinatra
Hanami
Roda
...

アプリケーションとサーバの間の規約。PSGI / WSGI と同じ考え方。

「Cloudflare Workers」対応のイメージ

Server

Puma
Unicorn
WEBrick
CGI
...
Cloudflare Workers
Rack
Framework

Rails
Sinatra
Hanami
Roda
...

Rackをサポートしたからには、な...

  • Rubyの「有名な」フレームワークを動かしたい
  • Railsは流石に無理なので、少しずつな...

現状

ワイの手元ではSinatraが動いている

  • 1行も本体コードにはパッチを当てていない
  • 本物のSinatraを動かしている
    • version 4.2.1
  • Mustermann(ルータライブラリ)もそのまま動かせている

容量も余裕がある

  • デプロイもできていて、動いているのを確認
  • 非圧縮で700KiB台、圧縮後は300KiB以下
$ npm run check
Total Upload: 734.10 KiB / gzip: 292.33 KiB
--dry-run: exiting now.

ただし、まだ若干非互換が

  • mruby 4.0 相当を使っているが、このバージョンでは以下のCRuby非互換がある:
    • 正規表現の非互換(バグ)
    • バイトコードコンパイラの非互換(バグ)
    • bare raise の非互換

正規表現の非互換(バグ)

  • mruby-regexp のバックトラッキングエンジンが \Z を処理していなかった
    • lazy quantifier、後方参照などと組み合わせると、CRubyと異なる結果に
    • Sinatraでは * を使ったルーティングが影響を受ける
match = /\A(?<path>.*?)\Z/.match("a/b")
match["path"] # CRubyでは "a/b", mruby では nil

バイトコードコンパイラの非互換(バグ)

  • ensure 内にローカル変数の ||= &&= があると、不要なVMレジスタが確保され、returnすべきレジスタがズレた
def example
  result = :result
  begin
    result
  ensure
    value ||= []
  end
end
example # CRubyでは :result、修正前mrubyでは nil

upstreamではいずれも修正済み

  • ここまでは、upstreamではいずれも修正済み/picoruby取り込み済み
  • Hasumikinさんは神

bare raise の非互換

class FooError < StandardError; end
begin
  raise FooError, "bar"
rescue
  raise
end
  • CRubyでは、rescue 内の引数なし raise は現在処理している例外を再送出
    • Sinatraのエラーハンドリングがこの仕様に依存する😥
  • mrubyではメッセージのない新しい RuntimeError

これは「意図した非互換」っぽくて悩む

begin
  raise FooError, "bar"
rescue => e
  raise e # これだけで対応できる
end

逆に言えば、今やこれだけの非互換しかない感じ

  • 見つかってる範囲では。
  • mrubyすごくね?

今後のアクション

  • Sinatra のアプリユースケースのコードを書き、その挙動をCRuby / PicoRuby で比較するCI基盤実装を作ってるところ
  • シナリオが増えれば増えるほど非互換がなくなる
  • 何ができるかも見ればわかる
Appコード
シナリオコード
class SinatraCoversApp < Sinatra::Base
  get "/" do
    "Sinatra #{Sinatra::VERSION}"
  end
end
scenario "GET route" do
  get "/"
  assert_status 200
  assert_body "Sinatra 4.2.1"
end

プロジェクトジェネレータも開発中

gem install picoruby-cloudflare-template --pre
picoruby-cloudflare new ./my-mruby-worker
cd ./my-mruby-worker && bundle install && npm install
PICORUBY_ROOT=/path/to/picoruby npm run dev
  • 詳細はREADMEで!

Conclusion

まとめ

  • Wasm最高!
  • Cloudflare Workers最高!
  • 本物のSinatraがCloudflare Workersで動くかもしれない
  • 今でもRubyでそれっぽいDSLベースのアプリを作れます

お試しください

  • Uzumibi
    • すぐにでも触りたい人は、もう触れます
  • PicoRuby on Cloudflare Workers w/ Sinatra!!
    • 期待して待ってて!