前田 稔です。プログラミング言語Rubyの国際会議 RubyKaigi 2024 のセッション「RubyGems on ruby.wasm」で紹介された Mastodon in the browser demo を拝見し、 Redmine も同様にブラウザ内で動作させられるのではないかと考えました。本記事では、その検証結果と Wasm 化の手順についてまとめています。
Mastodon は Ruby on Rails で開発されたアプリケーションであり、Redmine も同じく Ruby on Rails で構築されていることから、ブラウザ内での動作が可能ではないかと期待しました。
結果として、Redmine をブラウザ内で動作させることに成功しました。しかし、ディスクへのアクセスが必要な機能(例:添付ファイルのアップロードなど)は動作しませんでした。この問題は、クラウドストレージを利用するプラグインを導入することで解決できる可能性があります。
なお、今回の検証では開発版のソースコードを使用しているため、将来的なアップデートにより本記事の手順では動作しなくなる可能性があります。
今回の検証では、 Yuta Saito 氏(@kateinoigakukun) が公開している Mastodon in the browser demo
のソースコードを参考にしました。
palkan/wasmify-rails: Tools and extensions to pack and run Rails apps on Wasm という他の選択肢もありましたが、今回は未検証です。
なお、 Wasm 化された Ruby on Rails アプリケーションがブラウザ内でどのように動作するかについては、 [SF Ruby, March 2024] Rails on Wasm - Speaker Deck をご覧ください( 動画 )。
今回の検証には、ruby/ruby.wasm リポジトリ内の Dockerfile を基に環境を構築しました。使用した主要なツールのバージョンは以下の通りです。
$ ruby -v ruby 3.4.0preview1 (2024-05-16 master 9d69619623) [x86_64-linux] $ cargo --version cargo 1.79.0 (ffa9cf99a 2024-06-03) $ node --version v20.17.0 $ cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" $ printenv | grep WASI WASI_SDK_PATH=/usr/local/wasi-sdk-21.0
環境構築の詳細については、パッケージ追加に関するドキュメント [1] [2] [3] を参照してください。
1. WASI SDK の取得が必要です。詳細は https://github.com/WebAssembly/wasi-sdk?tab=readme-ov-file#install や https://github.com/kateinoigakukun/wasi-vfs/blob/v0.5.3/.github/workflows/main.yml#L30-L39 を参照してください。
2. libidn のビルドに必要なパッケージの追加が必要です。詳細は tmp/libidn/CONTRIBUTING.md や https://www.gnu.org/software/libidn/#howtouseit ならびに https://github.com/coreutils/gnulib/blob/master/DEPENDENCIES を参照してください。
3. Ruby のビルドに必要なパッケージの追加が必要です。詳細は https://github.com/rbenv/ruby-build/wiki を参照してください。
今回の検証では、 Mastodon in the browser demo
のソースコードを参考にしました。その中でも、特に重要な役割を果たしたディレクトリやファイルを以下に示します。
以下に、私が理解した内容をまとめます。
Wasm 化の処理が記載されています。大まかには以下の処理を行っています。
rbconfig.rb
を生成する--target-rbconfig
に対応した vendor/rubygems
を使用して web グループの RubyGems を bundle install
する今回の検証では、不要な部分 [5] を削除するだけで問題ありませんでした。
4. https://speakerdeck.com/kateinoigakukun/what-you-can-do-with-ruby-on-webassembly や https://evilmartians.com/events/assembling-the-future-ruby-on-wasm-puzzle-euruko のスライドがわかりやすいです。
5. Mastodon 向けの .env.production や app.json の配置を指しています。
--target-rbconfig
に対応した kateinoigakukun/rubygems です [6] 。
6. 本来は --target-rbconfig に対応した https://github.com/ruby/ruby/tree/7cbe54714ca1b9112e278d2d605cd049a065707e を使用したかったのですが、 default gems の更新を把握できておらず、断念しました。
bundle exec rbwasm build --ruby-version head
で使用する Ruby のリビジョンを指定します。
Mastodon in the browser demo
の ruby/ruby.wasm で使用されている Ruby のリビジョンは 61829eec657e8271f05a74f2067b4203ba0a51a2 です。
今回の検証では 61829eec657e8271f05a74f2067b4203ba0a51a2
に近く、導入しやすい Ruby 3.4.0 preview1 に相当する 9d69619623ec6b86c464b7cac911b7201f74dab7 を使用しています。
gnulib のパッチです。
dist/pglite.rb
や dist/rails.main.rb
など サービスワーカー でやり取りするデータベースサーバーやアプリケーションサーバー相当のものが含まれています。今回の検証では、 dist/rails.main.rb
の不要な部分 [7] を削除するだけで問題ありませんでした。
また package.json
については以下のバージョンに変更しています [8] 。
パッケージ | バージョン |
---|---|
@bytecodealliance/jco | 1.7.0 |
@bytecodealliance/preview2-shim | 0.17.0 |
7. Mastodon 向けの class Status を指しています。
8. src/rails.sw.js に wasi:http/outgoing-handler と wasi:http/types の追記が必要です。
bin/rbwasm
に必要な RubyGems および dist/pglite.rb
や dist/rails.main.rb
で読み込まれる RubyGems が記載されています。
Mastodon in the browser demo
で使用されている ruby/ruby.wasm は https://github.com/ruby/ruby.wasm/tree/576cc3f6d838b0f942e045dd88fb295347d2291c です。
今回の検証も同じものを使用します [9] 。ただし、開発版の ruby/ruby.wasm で対応されている Use compiled API as fallback for rb-sys | ruby/ruby.wasm@1576fd0 を反映した状態にします。
また CONTRIBUTING.md の Install dependencies
に従って ruby_wasm.so
を生成します。
9. 本来は最新の ruby/ruby.wasm を使用したかったのですが、更新内容を十分に把握できなかったため、断念しました。
開発版の 5.1.3.devel を使用し、 require
が必要な RubyGems を web グループに指定します。
また、予期せぬエラーが発生しないように Ruby on Rails のダウングレードや default gems
のバージョンを固定します。なお、 Cross-compile C-Extentions
が必要な RubyGems は個別に Yuta Saito 氏(@kateinoigakukun) が公開されている対応済みのものを指定します。
変更後の Gemfile(変更箇所抜粋):
source 'https://rubygems.org' ruby '>= 3.1.0', '< 3.4.0' gem 'rails', '7.1.4', group: [:default, :web] gem 'rouge', '~> 4.2', group: [:default, :web] gem 'mini_mime', '~> 1.1.0' gem "actionpack-xml_parser" gem 'roadie-rails', '~> 3.2.0', group: [:default, :web] gem 'marcel' gem 'mail', '~> 2.8.1' gem 'nokogiri', github: 'kateinoigakukun/nokogiri', ref: '8e9904e5a891af43ad0c1e8eec467ecbbf55d55f', group: [:default, :web] gem 'i18n', '~> 1.14.1' gem 'rbpdf', '~> 1.21.3', group: [:default, :web] gem 'addressable' gem 'rubyzip', '~> 2.3.0', group: [:default, :web] gem 'propshaft', '~> 0.8.0', group: [:default, :web] gem 'rack', '>= 3.1.3' # Ruby Standard Gems gem 'csv', '~> 3.2.8', group: [:default, :web] gem 'net-imap', '~> 0.4.8' gem 'net-pop', '~> 0.1.2' gem 'net-smtp', '~> 0.4.0' # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', group: [:default, :web] # TOTP-based 2-factor authentication gem 'rotp', '>= 5.0.0' gem 'rqrcode' # HTML pipeline and sanitization gem "html-pipeline", "~> 2.13.2", group: [:default, :web] gem "sanitize", "~> 6.0", group: [:default, :web] # Optional gem for LDAP authentication group :ldap do gem 'net-ldap', '~> 0.17.0' end # Optional gem for exporting the gantt to a PNG file group :minimagick do gem 'mini_magick', '~> 4.13.0' end # Optional Markdown support group :markdown do gem 'redcarpet', '~> 3.6.0', group: [:default, :web] end # Optional CommonMark support, not for JRuby group :common_mark do gem "commonmarker", '~> 0.23.8' gem 'deckar01-task_list', '2.3.2', group: [:default, :web] end # 中略 ... # Load plugins' Gemfiles Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file| eval_gemfile file end # 以下、追加 gem 'psych', '5.1.2' gem 'stringio', '3.1.1' gem 'io-console', '0.7.2' # gem 'io-console', github: 'ruby/io-console', ref: 'ba9bf00184ea7d5fdfb72945c8f42458bafc42aa' gem 'nio4r', github: 'kateinoigakukun/nio4r', ref: 'd219d9bce40435bd993b6ed6e425ae7e76b62d04' group :development do install_if -> { !(RUBY_PLATFORM =~ /wasm/) } do gem 'ruby_wasm', path: "vendor/ruby.wasm" end end gem 'activerecord-nulldb-adapter', group: [:web] gem 'js', git: 'https://github.com/ruby/ruby.wasm', ref: "0ca30636702eb7e1bb2a17b3868a458a03f045a8", # branch: katei/kaigi-staging glob: 'packages/gems/js/*.gemspec', group: [:web]
ENV["RAILS_WEB"]
の値を評価して pglite
を使用する仕組みに変更します。
default: &default adapter: postgresql encoding: unicode host: localhost username: postgres development: <<: *default database: redmine_development production: <<: *default database: redmine_production
Redmine の Wasm 化に伴い、機能しない箇所やエラーが発生する箇所に対して修正を加えました。以下に主な修正箇所を示します。
app/models/auth_source_ldap.rb(変更箇所抜粋):
+if ENV["RAILS_WEB"] + class AuthSourceLdap < AuthSource; end + return +end + require 'net/ldap' require 'net/ldap/dn' require 'timeout'
config/application.rb(変更箇所抜粋):
# Verify validity of user sessions - config.redmine_verify_sessions = true + config.redmine_verify_sessions = ENV["RAILS_WEB"].blank?
lib/redmine/wiki_formatting/html_sanitizer.rb(変更箇所抜粋):
module WikiFormatting # Combination of SanitizationFilter and ExternalLinksFilter class HtmlSanitizer - Pipeline = HTML::Pipeline.new( + Pipeline = ::HTML::Pipeline.new(
以下のコマンドで作成したテキスト形式ダンプファイル [10] の pwa/dist/redmine_development.sql
を配置し、 pwa/src/rails.sw.js
の mastodon_development.sql
から変更します。
10. SET コマンド行のコメントアウトが必要です。
pg_dump -U postgres -h localhost --format=plain --column-inserts --file=/tmp/redmine_development.sql -d redmine_development
./bin/rbwasm
を実行すると、以下のような出力が得られます [11] 。
11. 処理時間は、CPU 性能とディスク I/O に依存します。例えば、Docker を使用しない 8 コア/ 16 スレッドの Debian 環境では、処理が完了するまでにおおよそ20分程度かかります。一方、 Docker のボリューム機能を使用する macOS や Windows 環境では、30分以上かかります(Docker 上でボリューム機能を使用しない場合、実行時にエラーが発生する可能性があります)。
# 中略 ./pwa/node_modules/@bytecodealliance/jco/src/jco.js transpile --instantiation --valid-lifting-optimization tmp/ruby.wasm -o pwa/dist/component Transpiled JS Component Files: - pwa/dist/component/interfaces/ruby-js-js-runtime.d.ts 1.89 KiB - pwa/dist/component/interfaces/ruby-js-ruby-runtime.d.ts 1.17 KiB - pwa/dist/component/interfaces/wasi-cli-environment.d.ts 0.15 KiB (中略 ...) - pwa/dist/component/interfaces/wasi-random-random.d.ts 0.09 KiB - pwa/dist/component/ruby.core.wasm 74.6 KiB - pwa/dist/component/ruby.core10.wasm 35.3 KiB - pwa/dist/component/ruby.core11.wasm 2.36 KiB (中略 ...) - pwa/dist/component/ruby.core39.wasm 482 KiB - pwa/dist/component/ruby.core4.wasm 20.5 MiB - pwa/dist/component/ruby.core40.wasm 66 KiB (中略 ...) - pwa/dist/component/ruby.d.ts 4.04 KiB - pwa/dist/component/ruby.js 578 KiB node ./build.mjs dist/rails.sw.js 831.4kb dist/boot.js 2.2kb dist/rails.sw.js.map 1.3mb dist/boot.js.map 3.6kb ⚡ Done in 236ms
以下のコマンドでウェブサーバーを実行し、 http://localhost:8080/boot.html にアクセスすれば Redmine の読み込みが始まります [12] 。
なお、 pwa/dist
ディレクトリ全体で 78MB 程度(1ファイル最大 20MB 程度)です。
12. HTTP ヘッダーの Set-Cookie や Location が小文字の場合は pwa/src/rails.sw.js の該当箇所を変更してください。
ruby -run -e httpd ./pwa/dist [2024-09-27 13:33:34] INFO WEBrick 1.8.2 [2024-09-27 13:33:34] INFO ruby 3.4.0 (2024-05-16) [arm64-darwin23] [2024-09-27 13:33:34] INFO WEBrick::HTTPServer#start: pid=40115 port=8080 [2024-09-27 13:33:34] INFO To access this server, open this URL in a browser: [2024-09-27 13:33:34] INFO http://[::1]:8080 [2024-09-27 13:33:34] INFO http://127.0.0.1:8080
今回は、 ruby/ruby.wasm を使用して、Redmine をブラウザ内で動作させる検証を行いました。
Ruby on Rails アプリケーション全体を Wasm 化した際の活用場面としては、 RubyGems on ruby.wasm - Speaker Deck によると、静的サイトとしてホスティングできるため、運用コストが低減されることや、セキュリティリスクが抑えられる点が挙げられます。具体例としては、プルリクエストのプレビュー環境などに利用できるのではと考えられています。
個人的な考えとしては、Redmine の Wiki 編集時のプレビュー機能において、ruby/ruby.wasm を利用し、一部の機能を Wasm 化することで、効率の向上が期待できると考えています。このアプローチにより、アプリケーションサーバーやデータベースサーバーとの通信を減らし、ユーザーへの応答速度が向上する可能性があります。
さらに、Redmine のプラグインに関しても、ruby/ruby.wasm を用いてパッケージ化することで、プラグインの配布やアップデートがより簡便になるのではと考えています。
【スタッフ募集中】
弊社ではAWSを活用したソリューションの企画・設計・構築・運用や、Ruby on Rails・JavaScriptフレームワークなどを使用したアプリケーション開発を行うスタッフを募集しています。採用情報の詳細
弊社での勤務に関心をお持ちの方は、知り合いの弊社社員・関係者を通じてご連絡ください。
Turbo FramesでRedmineのフォーラム機能の画面遷移を削減できるか試した話 turbo-rails gemを介してTurbo Framesを利用し、フォーラムのメッセージをシームレスに更新できるかを検証しました。 |
|
続たのしい自作キーボード〜PiPi GherkinビルドログとRedmineショートカットキー〜 PiPi Gherkin + PRK Firmwareの自作キーボードを作りました。 |
|
ゲオあれこれレンタルでカメラをレンタルしてみた 家電の短期間のレンタルは、購入検討中のものを試すのに便利です。 |
|
5年ぶりの台湾訪問とセキュリティカンファレンスHITCON Community 2024に参加した話 アジア最大規模のセキュリティカンファレンスHITCON CMT 2024に参加しました。 |
|
My Redmine Global Edition と My Redmine JP Edition の違い 全世界向けの「My Redmine Global Edition」と⽇本国内向けの「My Redmine JP Edition」のサービスの違いを紹介します。 |
2024年10月19日 オライリー本の全冊公開日のお知らせ(もくもく勉強会も同時開催) ファーエンドテクノロジーが所蔵するオライリー本(全冊)公開日のご案内です。公開日には「もくもく勉強会」も同時開催します。 |
|
RubyWorld Conference 2024 (12/5・6開催) にPlatinumスポンサーとして協賛 ファーエンドテクノロジー株式会社は、2024年12月5日(木)〜6日(金)に島根県松江市で開催される「RubyWorld Conference 2024」にPlatinumスポンサーとして協賛しています。 |
|
プロジェクト管理ツールRedmineのクラウドサービス「My Redmine」の海外向けサービス「My Redmine Global Edition」の提供を開始 「My Redmine」の海外向けサービスとして、新たに「My Redmine Global Edition」の提供を開始しました。 |
|
Redmineの最新情報をメールでお知らせする「Redmine News」配信中 新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け |