Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
mrubyにとるRubyのシングルバイナリ運用
Search
take_cheeze
October 07, 2017
Programming
0
1.1k
mrubyにとるRubyのシングルバイナリ運用
take_cheeze
October 07, 2017
Tweet
Share
More Decks by take_cheeze
See All by take_cheeze
goluaをさわってみる
takecheeze
0
290
html5everをスクリプト言語から呼ぶ
takecheeze
0
210
mgemのCIを支える諸々
takecheeze
2
520
Go_2のドラフトを読む__エラー編_.pdf
takecheeze
0
1.5k
fukuoka.rb 祝 #100!
takecheeze
0
560
dep ensure浅掘り
takecheeze
0
370
LuaJIT as a Ruby backend
takecheeze
1
3.3k
Fukuoka Ruby Award 10th
takecheeze
1
230
コンパイル時計算への招待.pdf
takecheeze
1
1.2k
Other Decks in Programming
See All in Programming
みんなでプロポーザルを書いてみた
yuriko1211
0
280
A Journey of Contribution and Collaboration in Open Source
ivargrimstad
0
1k
Less waste, more joy, and a lot more green: How Quarkus makes Java better
hollycummins
0
100
Click-free releases & the making of a CLI app
oheyadam
2
120
「今のプロジェクトいろいろ大変なんですよ、app/services とかもあって……」/After Kaigi on Rails 2024 LT Night
junk0612
5
2.2k
What’s New in Compose Multiplatform - A Live Tour (droidcon London 2024)
zsmb
1
480
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
610
Contemporary Test Cases
maaretp
0
140
受け取る人から提供する人になるということ
little_rubyist
0
250
RubyLSPのマルチバイト文字対応
notfounds
0
120
Nurturing OpenJDK distribution: Eclipse Temurin Success History and plan
ivargrimstad
0
990
ヤプリ新卒SREの オンボーディング
masaki12
0
130
Featured
See All Featured
Optimizing for Happiness
mojombo
376
70k
Faster Mobile Websites
deanohume
305
30k
Designing for humans not robots
tammielis
250
25k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
109
49k
Speed Design
sergeychernyshev
25
620
XXLCSS - How to scale CSS and keep your sanity
sugarenia
246
1.3M
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.3k
Building Adaptive Systems
keathley
38
2.3k
Visualization
eitanlees
145
15k
GitHub's CSS Performance
jonrohan
1030
460k
Rails Girls Zürich Keynote
gr2m
94
13k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Transcript
mrubyによるRubyの シングルバイナリ運用 2017/10 渡辺 丈 (株式会社Fusic)
自己紹介 • 長野出身。今年6月~福岡に • mruby四天王最弱くらい • まつもと姓じゃないですが、長野県松本市に住んでいたことは • 仕事では、主にmockmock •
RailsとmrubyとC書いてます • 好きな言語はC++/Rust • Rubyのことはそれほどでも...(メタプロは楽しいよ(にっこり
mrubyやりだしたきっかけ • RPGツクールXP/VX/VX AceがRuby実行環境 • EasyRPGというRPGツクール2000/2003のクローン • Rubyはおおきいしめんどうなので、mrubyならばと… • 当時のmruby状況(2013年ごろ)
• 無理やり複数スクリプトをバイトコードに変換 • ろくなデバッグ情報がないためバックトレースが信用できない • デバッグ情報のバイナリフォーマットを定義するところから…
話す内容 • mrubyでバイナリを作る方法 • mrubyのつらい話 • 時間余ったら、mrubyについて雑談
まずmrubyのリリース歴(偏見込み • 2014/02: 1.0リリース • それほど事前情報もなくリリース。初の安定版 • 2014/11: 1.1リリース •
1.0であった不具合がだいぶ修正された(ただ、影が薄い) • 2015/11: 1.2リリース • それなりに安定したリリース • 1.3まで期間があったためよく使われている(た?) • 2017/07: 1.3リリース • Shopifyの件で多数の脆弱性やメモリエラーの修正 • いくつか困ったバグが入ったままなので対策が必要 • mruby-sprint, mruby-socketで問題を確認して対策をしている
用語解説 • mrbgem • mrubyにおけるrubygem • mrubyを様々に拡張できる • mgem •
mgem-list に登録されたmrbgem • 上と紛らわしいが、正確に把握してほしい用語 • 登録されるとビルドシステムのDSLで`mgem: ‘mrbgem名’`と書くだけに • build_config.rb • mrubyをビルドするのに使う設定ファイル • CRubyと違い複数のVMを任意の設定で同時ビルドできる
mrubyで実行ファイルを作る方法 • だいたい、以下の3つ • 一、できるlibmruby.(a|lib)をビルドシステムでよろしくやる • 一、Herokuの中の人製のmruby-cli • 一、script only
tool(仮)
作ったlibmruby.(a|lib)でビルド • 最も原始的 • よくないところ • 再ビルド方法がやや煩雑 • mrubyのスクリプトを追加で埋めるのがめんどくさい •
いいところ • Rubyの作法から自由 • Makefileおじさんに(たぶん)好評
mruby-cli • 手法としては最も人気 • 既に素晴らしい発表があるので以下参照: • http://rubykaigi.org/2015/presentations/hone02_zzak • https://www.youtube.com/watch?v=ypoUhjJhGrY •
https://speakerdeck.com/hone/mruby-a-packaging-story-filled-with- freedom-madison-plus-ruby-2015
mruby-cli • 元々は、CRubyベースheroku-cli置き換えのためだった? • システムにインストールされたCRubyでのテストがつらい • CRubyのスクリプトの読み込み遅い • 各環境でのテストがめんどくさい(Linux/Windows/macOS) •
現在のheroku-cliは • node.jsの実装に置き換わり • プラグインもJavaScriptベース • npmみたいなモダンなパッケージマネージャーが優秀? • たぶん、別の用途でmruby-cliは使われている?
mruby-cli • ともかく中身 • mrubyで、バイナリを作るフレームワーク • `__main__`を定義するとエントリポイント的に呼んでくれる • scaffold的なファイル生成 •
Docker使う • クロスコンパイルしたいから • きれいなビルド環境がすぐほしい
mruby-cliでの気をつけるところ • `__main__`がややPythonっぽい • ファイルが自動生成される • バージョンアップ時つらい • どこを触るべきかわかりづらい •
Dockerでの制限 • イメージのビルド方法がやや不透明 • 場合によっては独自Dockerfileの書き直しに • バインドマウントできない環境で使えない
mruby-cli登場の影響など • 登場時既にmrubyのビルドシステムは複雑に • 手をいれるのがかなり難しい状況にはなっていた • build_config.rbで`enable_test`しないとテストがしないように • `./minirake all
test` しただけではテストがされなくなった • mruby-cliがデフォルトでテストをビルドしたくなかったため • 恐らく、様々な競合が発生したため • 既定挙動の変更でテストへのハードルが上がった • ユーザーは既定挙動に従わされる • CIでテストされていたmrbgemが挙動変更でテストされなく • いくつかのCI用のbuild_config.rbは更新された
script only tool • 拙作の黒歴史 • 結局、提案が早すぎたり、名称が悪かったりで取り下げ • mrbgemでmrubyスクリプトのみを用意すれば実行ファイルを作れる •
mrubyのビルドシステムにmruby-cliのバイナリ生成機能 • Docker部分やテンプレート機能のないmruby-cli • minirakeで復活の目が…? • 知名度による力の差をOSSでも感じた
ということでmrubyの実行の現状 • 現状mruby-cliが有力ではあるが、用途によっては不向き • rakeスクリプトをハックできるなら、自前でがんばる • ビルド職人にあなたも! • 他、問題があるならもうlibmruby.aを直に使う •
一緒に`libmruby.flags.mak`を探すと幸せになれるかも
本題: mockmock • 現在、会社での主な仕事 • IoT仮想デバイスを提供するwebサービス • 以下、仮想デバイスのことはmockと呼称 • IoTデバイスのデータ送信先のテストに
• 負荷テストや異常系テストが得意 • IoTのテストに興味のある方ブースにどうぞ! • mock(仮想デバイス)をmrubyで実装 • シングルバイナリと言語がちょうどよかったらしい • Web側はRuby on Rails
mockのビルド方法 • 基本、mruby-cliベース • TLS通信のためにOpenSSL依存 • →実行環境の都合で、静的リンクしなければならない • EC2で動かすためにAmazon Linux向けにクロスコンパイル
• →独自dockerイメージでクロスビルド • amazonlinuxイメージすばらしい
ただ、問題がいくつか… • mruby-cli由来の問題をほぼ引きずっていた • それに加えて増えた依存ライブラリなど • ローカルのビルド環境との共存ができない • mrbgemのメンテナンスがされてないことが多々 •
テストがない
依存ライブラリ • OpenSSLなどのライブラリのビルド時間が長い • ビルド環境をcleanするとライブラリも消える • ビルド環境をcleanする度に長いビルドが必要 • ビルドし直したりCIでビルドするだけで10分以上かかる
ビルド時間は短い方がいい • 開発を早く回せる • 気が重くない • CIも早く終わるので、チーム外と仲良くできる
ビルド時間の解決方法 • Dockerイメージに手を入れる • →事前に、依存ライブラリをDockerfileでビルド • OpenSSL (ご存知) • libuv
(非同期IOライブラリ) • libonig (正規表現ライブラリ) • ローカル環境と完全にdocker環境を分離 • 混ぜるな危険 • バインドマウントをやめてボリュームに中間ファイルを置く
依存するmrbgemのメンテナンス • IoTらしくMQTTでデータを送れる機能が • →mruby-mqttというmrbgemが既にあったが… • マルチスレッドプログラミングは難しい • 使うライブラリの仕様をよく読むのは大変… •
諸々、修正したバージョンを使うことに • バージョン固定がめんどくさい • めんどくさいことはみんなやりません • なんとか、Pull-Request送ったり更新したりする
「テストがない」…? • 正確には、テストが難しくて書けてなかった • まだかなり未開拓分野 • スクリプト言語はとにかくテストを書くことでしか品質が上がらない... • mruby-cliでテストが難しい •
bintestというものもある • 間接的に、自分が加担してて笑う • テストを立ち上げるのだけでも難しくなっていた…
mrbgemでの原則:最小依存の原則 • 影が薄い… • mrbgem依存解決を入れた同時期にわしが入れた • テストを書くハードルを少し上げている • 本来、ツール(バイナリ)もこの原則に従うべきではある •
現状、libmruby.aの`mrb_open`でVM作成 • 既に、ツールを最小依存でビルドはできるようにはできた • テストみたいに最小依存のVMを`mrb_open`代わりに • 多数のツールを一緒にビルドする時の競合を回避できる • `__main__`とかVERSION変数とか
何が最小依存? • mrbgemはmrubyにおけるモジュールシステムである • モジュールは、依存しないモジュールから独立でなければならない • →標準的なmrbgemテストは、以下のmrbgemしか読み込まれない mruby VM上でそれぞれ実行されている •
テスト対象のmrbgem • 依存mrbgem • テスト用mrbgem • →正しく、依存関係を書かないとテストが立ち上がらない
一方そのころCRubyでは • requireに失敗すれば、Gemfile編集なりgem installすればいい • mrubyには固定requireすら無いので何が原因かわかりづらい • 非英語使いの人にはちょっとむつかしいRSpecが流行ってる • mrubyはminitestっぽい(互換ではない)テストツールを利用
• 個人的にはassertionをいっぱい書く方が結局楽だと思っている • テストでメタプロできる! • なんか、もう文化すら違う • Ruby書ける人&テスト書ける人のハードルすら上がってる
そして現在 • Dockerの改善を行ったら、ビルド時間には満足な感じに • CIとか開発マシンのvagrant上で2分前後に • テストを少しずつ書いている • 開発サイクルが早くなって生産性は上がっている
ご傾聴ありがとうございました。 • mrubyのことで何かご相談あれば是非伺います • (自分で蒔いたまずい種がいくつかありそうなので…)
以下、本題のネタ切れ用のネタ • 本題のスライド枚数が明らかに少ないので… • RubyKaigi 2017に会社の支援で行ってこれました (∩´∀`)∩ワーイ • 他のRuby実装の話で、mrubyにフィードバックできそうなネタいっぱい •
ついでに、LT枠で発表してきた • 他、無職期間中にやったことでも • 地味にやったけど伝わってないことも
RubyKaigi LTダイジェスト: mrubyの独立 • https://speakerdeck.com/takecheeze/independence-of-mruby • CRubyなしで、mrubyをビルドできることは確認した • CRuby依存で2の足を踏んでいた方! •
CRuby依存でビルド時間が長くなっている方! • mrubyだけを使いたいという方どうぞ! • Makefileの無駄技巧が身についた • script only toolふっかつ!(まだしてない
Rustとの連携 • mrustyとの連携は既に試してある • https://github.com/take-cheeze/mrusty/tree/mrbgem • html5ever使えたよ: https://github.com/mrbgems/html5ever • Cargo.tomlを生成するので簡単にcrateをとってくることができる
• 夢のパッケージマネージャーの競演!(危険が危ない • 「mrubyの独立」はこれが元ネタ • なんかmrusty作者に気づかれた:https://github.com/take- cheeze/mrusty/issues/2
Gemfile.lock的な仕組み • バージョン固定化がめんどくさいため更新でたまに事故ったり • 最近だとmruby-iijson https://github.com/iij/mruby-iijson/issues/11 • 現状、みんなgithubから取ってくるので以下を保持するように • リポジトリURL
• Gitのコミットハッシュ • 追跡対象ブランチ • ビルドごとに別のバージョンのmrbgemも使えるように • ダウンロードできない環境でビルドに必要なファイルを固める仕組み • Goっぽい仕組みを導入するのが現状親しい
特に必要のないDSL滅ぼし隊 (DSLは特定ドメインのものです派) • DSLのデメリット • 定型的な処理は、記述が煩雑に • 新しいmrbgem作る度に、元になるmrbgem.rakeに引きづられる • わからない人にはわからない(言語なので)
• なんか、ymlやtomlで書く設定ファイルが最近増えた(気がする) • Ansible, Docker Compose, Cargo(Rust), Go Dep • DSLのメリット • 複雑な処理を自由に書ける: mruby-onig-regexp, mruby-uv • →定型的なmrbgem.rakeとbuild_config.rbをymlで書けるようにしたい
mrubyからGCをなくそう! • ※ 半分くらいウソです • 組み込みの人が言う「GCつらい」から • GCはリーク検出用のアルゴリズムの面もあるので • 別に排斥運動ではない(あくまでリリースビルド時の話)
• Array/StringなどのCoWオブジェクトは既に参照カウント管理 • `mrb_irep`も内部的に参照カウント管理 • http://rubykaigi.org/2017/presentations/tenderlove.html • Mark-Compact GCを実装してOKなら! • 不可能と言われていたことに挑戦するのは楽しいれす(^q^)
参照カウント式mruby • 参照: https://github.com/take-cheeze/mruby/tree/ref_count • ひとまず、address sanitizer付きでテストが全部通るところまでは • ただ、細かいリークが出続けるのが直せてない(そして一番たいへん) •
元ネタ: Squirrel http://www.squirrel-lang.org/ • Luaと似たスクリプト言語、APIはLua系っぽい • 文法はJavaScriptっぽい • 基本参照カウント、オプションでGCをなくせる(リリースビルド時用) • 参考: https://www.slideshare.net/melpon/squirrel-7870852 • 参照カウント方式もGCの一種だというのは置いといて • ここでいうGCは、間接的な方(マークするやつ?) • 「ガベージコレクション」本は積んでる…
カバレッジツールほしい • 本家、CRubyのカバレッジツールの話が楽しかった • http://rubykaigi.org/2017/presentations/mametter.html • カバレッジによる可視化はほしい • CRubyだと、どうやらYARVコード生成時に何か追加で生成
mrubyでカバレッジツール • mruby VMにはVM命令hookがある • デバッグ情報さえあれば、スクリプトの再ロードなしでできる • カスタムVMを簡単にリビルドできるのがmrubyの強み • hookが呼ばれた箇所
= 実行された箇所 • デバッグAPIはわしがやったので、行番号とファイルはとれる • #include <mruby/debug.h> • mrb_debug_get_line • mrb_debug_get_filename • あとは、カバレッジ可視化ツールに食わせるデータの蓄積・生成 • 可視化ツールは好きなのを使いたい
キーワード引数 • 次のmrubyリリースあたりの目玉機能になるのでは…? • とりあえず、動く実装はわしが一番乗り: • https://github.com/mruby/mruby/pull/3629 • VMをゴリゴリいじっているので、互換性とかが厳しそう •
FileUtilsを実装するときにとても楽になる • この前Rubyのパパと話したときは、Hash使うのやめたいとか… • 互換性殺殺するならたしかにやりたい • Hashは、よく使われる割に重たいデータ構造 • 互換性の問題で使っている
キーワード引数 続き • 実装自体は終わってるが、まだ取り込まれてない • 数ヶ月ほってあったので、conflictとかつらい • レビュー時期に合わせて直したい(レビューする人が忙しい) • がんばれば1.3に間に合ってたかもしれないけど、機能が重たすぎた…
• 最近なんか要望があった気がする • https://twitter.com/tagomoris/status/915497677193748481 • もっと言ってね? (レビューする人とか、色々決める人に) • おじさん、引数が2つ以上だと覚えられないのを正直に言っていいと思う • SwiftかわいいよSwift
Hashオブジェクトの最適化 • String/Arrayは最適化されているがHashは… • Copy on Writeがない • Compact-GC実装のRubyKaigi2017セッションでも指摘されていた •
アロケーション回数がString/Arrayとくらべて多い • せめてハッシュテーブルの管理オブジェクトは使い回しを • http://rubykaigi.org/2017/presentations/watson1978.html • CRubyの新しいハッシュテーブルの実装を移植したい • https://bugs.ruby-lang.org/issues/12142 • https://developers.redhat.com/blog/2017/02/27/towards-faster-ruby-hash- tables/
Shopify事件をご存知だろうか? • VMのメモリアクセスバグをいっぱい見つけてくれた会社が • 後半は修正めっちゃ大変そうだった • mrubyを安全にRailsアプリにsandboxとして組み込みたかったらしい: • https://github.com/Shopify/mruby-engine •
1.3では大分直っている。Shopifyの貢献です! • http://forum.mruby.org/ という組織があるので、そっちと連動してい れば…
なぜ、VMに大量のメモリエラーが? • →VMスタックのreallocは難しい • VMのスタックを参照するポインタが多数に存在するため • Dangling Pointerこわい (> <)
• FiberやCoroutineの実装が難しい理由 • →最近のclang付属sanitizer(主にaddress)早くて強い • できるなら、デバッグビルドなどで`-fsanitizer=address`使いましょ? • リンカにもオプションを指定するのを忘れずに • →LuaとmrubyのAPIの違いが露呈した • Luaは、スタックアクセスをインデックス(整数値)でする • mrubyは、スタックアクセスにポインタが露出している部分が
安全なmruby VMスタックほしい • スタックのreallocを行わないように • →スタックフレームをその都度確保する • 呼び出し情報の操作をcipush/cipopに限定する • メリット
• Address Sanitizerですぐにアクセス違反がわかる • Shopify事件で見つかったようなバグをかなり減らせる • デメリット • VMスタックのコピーが増加する • 最適化しきれてないためにメモリ利用が増加する • 実装する人がつらい • 実装: https://github.com/take-cheeze/mruby/tree/safer_stack
mruby VMの省メモリ化 • VMの最適化をもっとビルド時に行うように • 参考(IoT向けや省メモリES5エンジン): • 標準クラスやメソッドはリリースビルド時に静的データに埋め込んでいる • https://github.com/cesanta/v7
• https://github.com/jerryscript-project/jerryscript • 現状、まだVM初期化処理で使う動的メモリが多すぎる • シンボル、クラス・メソッド定義はほぼ静的に決定しているのに • →初期化なので定義部分はかなり静的にできるはず • 定義と依存外部モジュール初期化を分離しないといけない
C++例外対応 • 本家CRubyはまだらしい • http://rubykaigi.org/2017/presentations/ktou.html • rust-luaさんの例: https://github.com/kballard/rust-lua/issues/1 • 例外対応はmrubyでわしが既にやってある
• https://github.com/mruby/mruby/pull/1776 • 内部実装的には、C++例外をC++で投げて受けてる • RustのpanicもC++例外と同じ仕組みなので共存できる? • https://github.com/llvm-project/llvm-project- 20170507/blob/1888610314b5e6c58f1f8b19be57052300a19bde/llvm/lib/Analysis/EHPe rsonalities.cpp に同じ仕組みで例外を投げるメジャーなランタイム一覧
地味なmruby-marshal更新 • IOオブジェクトに対応 • https://github.com/take-cheeze/mruby-marshal/issues/17 • テストではStringIOを利用 • depthに対応 •
ちょいちょいバグってたので、更新してない方は更新を… • 正常系と異常系は両方テストしよう… • C++で書いてるので、地味にC++例外を有効にする存在 • 正しい例外処理への楔にしたい • `#include <mruby/throw.h>` してるのは今すぐ`mrb_protect`系APIに移行を
mruby-clang-plugin • https://github.com/take-cheeze/mruby-clang-plugin • clangにプラグインとしてmruby API固有のチェックを追加する • `mrb_get_args`とか`mrb_get_args`とか • 他の可変長引数をとるAPIも
• 文字列リテラルに対する`mrb_*_cstr` API利用時に警告 • Xcodeのclangだとプラグインが無効にされてるので使えない • ビルドがめんどくさい。LLVMを使うと大体そうなる • 一応mrbgemとして追加するだけでいいはず… • かなり危うい作りをしているので、バグがまだありそう
メソッドの静的解決 • 長年のRuby最適化できない問題に挑みたい • メソッドの再定義が自由すぎる • →でも、誰もそこまで無制限なものは望んでない • →でも、どこまで認めるべきかそれぞれのスタンスが •
Ruby Gemのロードがすべて終わったタイミングとか? • mrubyは、mrb_open後にメソッド定義を禁止できればVMを更に最適 化できるかもしれない • 現状のスクリプト言語のJITは動的なC++テンプレートインスタンス化 のようなことをしている
AST機能の強化 • TruffleRuby: http://rubykaigi.org/2017/presentations/nirvdrum.html • 標準ライブラリのRubyコードを予めパースして埋め込んだり • Rubyパーサーについて: http://rubykaigi.org/2017/presentations/aycabta.html •
一度中間コードに変換して最適化するよりも、ASTを直接いじった方 がより相応しい最適化をできるのでは? • mrubyのASTはS式っぽい(完全なリスト構造ではない部分も) • →LISPっぽいことが! • 構文解析時よりもAST走査時にエラーをチェックしてほしい • 構文解析時にエラーをチェックするとパースが止まる…
mruby-uvの強化 • libuv 1.14のAPIに対応 • OS系APIで標準ライブラリを実装するのに必要なものがだいぶ揃った • 長年のバグをいくつか直した • addrinfo酷かったけど誰も直さんという…
• 使っている間に見つけたテストのバグをきれいにした • できないテストはおとなしくskipしといた方がよかった • Auto Fiber化したい • http://rubykaigi.org/2017/presentations/ko1.html • mruby-uvの上で標準ライブラリの実装を行いたい • ※ という妄想です
メタメソッド最適化 • また、Squirrelからのネタ • `method_missing`, `to_s`, 演算子などの特殊メソッドを別のメソット テーブルに持ち、特殊なVM命令やAPIで呼ぶ方法 • ハッシュテーブル探索をただの配列アクセスにできるのでかなりの
高速化が期待できる • メソッドキャッシュよりも決定的で性能が出しやすそう • メタメソッドテーブルでややクラスによるメモリ消費が増える
mrubyにもっと必要なこと • 忙しい偉い人に今更、プロジェクト管理やってとか言いづらい • もっと開発環境を整え続ける人が必要 • ツール • テスト •
Travis CIでは新しい言語のサポートを追加できる: https://docs.travis- ci.com/user/languages/community-supported-languages/ • サポート • それなりに貢献をしても、それが後進に伝わらずよく断絶してる • VMの(簡単な)バグ潰せるレベルの人がもっと必要 • 現状、多忙な人が1人でやってる…