Upgrade to Pro — share decks privately, control downloads, hide ads and more …

mrubyにとるRubyのシングルバイナリ運用

 mrubyにとるRubyのシングルバイナリ運用

take_cheeze

October 07, 2017
Tweet

More Decks by take_cheeze

Other Decks in Programming

Transcript

  1. 自己紹介 • 長野出身。今年6月~福岡に • mruby四天王最弱くらい • まつもと姓じゃないですが、長野県松本市に住んでいたことは • 仕事では、主にmockmock •

    RailsとmrubyとC書いてます • 好きな言語はC++/Rust • Rubyのことはそれほどでも...(メタプロは楽しいよ(にっこり
  2. mrubyやりだしたきっかけ • RPGツクールXP/VX/VX AceがRuby実行環境 • EasyRPGというRPGツクール2000/2003のクローン • Rubyはおおきいしめんどうなので、mrubyならばと… • 当時のmruby状況(2013年ごろ)

    • 無理やり複数スクリプトをバイトコードに変換 • ろくなデバッグ情報がないためバックトレースが信用できない • デバッグ情報のバイナリフォーマットを定義するところから…
  3. まず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で問題を確認して対策をしている
  4. 用語解説 • mrbgem • mrubyにおけるrubygem • mrubyを様々に拡張できる • mgem •

    mgem-list に登録されたmrbgem • 上と紛らわしいが、正確に把握してほしい用語 • 登録されるとビルドシステムのDSLで`mgem: ‘mrbgem名’`と書くだけに • build_config.rb • mrubyをビルドするのに使う設定ファイル • CRubyと違い複数のVMを任意の設定で同時ビルドできる
  5. mruby-cli • 元々は、CRubyベースheroku-cli置き換えのためだった? • システムにインストールされたCRubyでのテストがつらい • CRubyのスクリプトの読み込み遅い • 各環境でのテストがめんどくさい(Linux/Windows/macOS) •

    現在のheroku-cliは • node.jsの実装に置き換わり • プラグインもJavaScriptベース • npmみたいなモダンなパッケージマネージャーが優秀? • たぶん、別の用途でmruby-cliは使われている?
  6. mruby-cliでの気をつけるところ • `__main__`がややPythonっぽい • ファイルが自動生成される • バージョンアップ時つらい • どこを触るべきかわかりづらい •

    Dockerでの制限 • イメージのビルド方法がやや不透明 • 場合によっては独自Dockerfileの書き直しに • バインドマウントできない環境で使えない
  7. mruby-cli登場の影響など • 登場時既にmrubyのビルドシステムは複雑に • 手をいれるのがかなり難しい状況にはなっていた • build_config.rbで`enable_test`しないとテストがしないように • `./minirake all

    test` しただけではテストがされなくなった • mruby-cliがデフォルトでテストをビルドしたくなかったため • 恐らく、様々な競合が発生したため • 既定挙動の変更でテストへのハードルが上がった • ユーザーは既定挙動に従わされる • CIでテストされていたmrbgemが挙動変更でテストされなく • いくつかのCI用のbuild_config.rbは更新された
  8. script only tool • 拙作の黒歴史 • 結局、提案が早すぎたり、名称が悪かったりで取り下げ • mrbgemでmrubyスクリプトのみを用意すれば実行ファイルを作れる •

    mrubyのビルドシステムにmruby-cliのバイナリ生成機能 • Docker部分やテンプレート機能のないmruby-cli • minirakeで復活の目が…? • 知名度による力の差をOSSでも感じた
  9. 本題: mockmock • 現在、会社での主な仕事 • IoT仮想デバイスを提供するwebサービス • 以下、仮想デバイスのことはmockと呼称 • IoTデバイスのデータ送信先のテストに

    • 負荷テストや異常系テストが得意 • IoTのテストに興味のある方ブースにどうぞ! • mock(仮想デバイス)をmrubyで実装 • シングルバイナリと言語がちょうどよかったらしい • Web側はRuby on Rails
  10. ビルド時間の解決方法 • Dockerイメージに手を入れる • →事前に、依存ライブラリをDockerfileでビルド • OpenSSL (ご存知) • libuv

    (非同期IOライブラリ) • libonig (正規表現ライブラリ) • ローカル環境と完全にdocker環境を分離 • 混ぜるな危険 • バインドマウントをやめてボリュームに中間ファイルを置く
  11. 依存するmrbgemのメンテナンス • IoTらしくMQTTでデータを送れる機能が • →mruby-mqttというmrbgemが既にあったが… • マルチスレッドプログラミングは難しい • 使うライブラリの仕様をよく読むのは大変… •

    諸々、修正したバージョンを使うことに • バージョン固定がめんどくさい • めんどくさいことはみんなやりません • なんとか、Pull-Request送ったり更新したりする
  12. mrbgemでの原則:最小依存の原則 • 影が薄い… • mrbgem依存解決を入れた同時期にわしが入れた • テストを書くハードルを少し上げている • 本来、ツール(バイナリ)もこの原則に従うべきではある •

    現状、libmruby.aの`mrb_open`でVM作成 • 既に、ツールを最小依存でビルドはできるようにはできた • テストみたいに最小依存のVMを`mrb_open`代わりに • 多数のツールを一緒にビルドする時の競合を回避できる • `__main__`とかVERSION変数とか
  13. 一方そのころCRubyでは • requireに失敗すれば、Gemfile編集なりgem installすればいい • mrubyには固定requireすら無いので何が原因かわかりづらい • 非英語使いの人にはちょっとむつかしいRSpecが流行ってる • mrubyはminitestっぽい(互換ではない)テストツールを利用

    • 個人的にはassertionをいっぱい書く方が結局楽だと思っている • テストでメタプロできる! • なんか、もう文化すら違う • Ruby書ける人&テスト書ける人のハードルすら上がってる
  14. RubyKaigi LTダイジェスト: mrubyの独立 • https://speakerdeck.com/takecheeze/independence-of-mruby • CRubyなしで、mrubyをビルドできることは確認した • CRuby依存で2の足を踏んでいた方! •

    CRuby依存でビルド時間が長くなっている方! • mrubyだけを使いたいという方どうぞ! • Makefileの無駄技巧が身についた • script only toolふっかつ!(まだしてない
  15. 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
  16. Gemfile.lock的な仕組み • バージョン固定化がめんどくさいため更新でたまに事故ったり • 最近だとmruby-iijson https://github.com/iij/mruby-iijson/issues/11 • 現状、みんなgithubから取ってくるので以下を保持するように • リポジトリURL

    • Gitのコミットハッシュ • 追跡対象ブランチ • ビルドごとに別のバージョンのmrbgemも使えるように • ダウンロードできない環境でビルドに必要なファイルを固める仕組み • Goっぽい仕組みを導入するのが現状親しい
  17. 特に必要のない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で書けるようにしたい
  18. mrubyからGCをなくそう! • ※ 半分くらいウソです • 組み込みの人が言う「GCつらい」から • GCはリーク検出用のアルゴリズムの面もあるので • 別に排斥運動ではない(あくまでリリースビルド時の話)

    • Array/StringなどのCoWオブジェクトは既に参照カウント管理 • `mrb_irep`も内部的に参照カウント管理 • http://rubykaigi.org/2017/presentations/tenderlove.html • Mark-Compact GCを実装してOKなら! • 不可能と言われていたことに挑戦するのは楽しいれす(^q^)
  19. 参照カウント式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は、間接的な方(マークするやつ?) • 「ガベージコレクション」本は積んでる…
  20. mrubyでカバレッジツール • mruby VMにはVM命令hookがある • デバッグ情報さえあれば、スクリプトの再ロードなしでできる • カスタムVMを簡単にリビルドできるのがmrubyの強み • hookが呼ばれた箇所

    = 実行された箇所 • デバッグAPIはわしがやったので、行番号とファイルはとれる • #include <mruby/debug.h> • mrb_debug_get_line • mrb_debug_get_filename • あとは、カバレッジ可視化ツールに食わせるデータの蓄積・生成 • 可視化ツールは好きなのを使いたい
  21. キーワード引数 • 次のmrubyリリースあたりの目玉機能になるのでは…? • とりあえず、動く実装はわしが一番乗り: • https://github.com/mruby/mruby/pull/3629 • VMをゴリゴリいじっているので、互換性とかが厳しそう •

    FileUtilsを実装するときにとても楽になる • この前Rubyのパパと話したときは、Hash使うのやめたいとか… • 互換性殺殺するならたしかにやりたい • Hashは、よく使われる割に重たいデータ構造 • 互換性の問題で使っている
  22. キーワード引数 続き • 実装自体は終わってるが、まだ取り込まれてない • 数ヶ月ほってあったので、conflictとかつらい • レビュー時期に合わせて直したい(レビューする人が忙しい) • がんばれば1.3に間に合ってたかもしれないけど、機能が重たすぎた…

    • 最近なんか要望があった気がする • https://twitter.com/tagomoris/status/915497677193748481 • もっと言ってね? (レビューする人とか、色々決める人に) • おじさん、引数が2つ以上だと覚えられないのを正直に言っていいと思う • SwiftかわいいよSwift
  23. 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/
  24. なぜ、VMに大量のメモリエラーが? • →VMスタックのreallocは難しい • VMのスタックを参照するポインタが多数に存在するため • Dangling Pointerこわい (> <)

    • FiberやCoroutineの実装が難しい理由 • →最近のclang付属sanitizer(主にaddress)早くて強い • できるなら、デバッグビルドなどで`-fsanitizer=address`使いましょ? • リンカにもオプションを指定するのを忘れずに • →LuaとmrubyのAPIの違いが露呈した • Luaは、スタックアクセスをインデックス(整数値)でする • mrubyは、スタックアクセスにポインタが露出している部分が
  25. 安全なmruby VMスタックほしい • スタックのreallocを行わないように • →スタックフレームをその都度確保する • 呼び出し情報の操作をcipush/cipopに限定する • メリット

    • Address Sanitizerですぐにアクセス違反がわかる • Shopify事件で見つかったようなバグをかなり減らせる • デメリット • VMスタックのコピーが増加する • 最適化しきれてないためにメモリ利用が増加する • 実装する人がつらい • 実装: https://github.com/take-cheeze/mruby/tree/safer_stack
  26. mruby VMの省メモリ化 • VMの最適化をもっとビルド時に行うように • 参考(IoT向けや省メモリES5エンジン): • 標準クラスやメソッドはリリースビルド時に静的データに埋め込んでいる • https://github.com/cesanta/v7

    • https://github.com/jerryscript-project/jerryscript • 現状、まだVM初期化処理で使う動的メモリが多すぎる • シンボル、クラス・メソッド定義はほぼ静的に決定しているのに • →初期化なので定義部分はかなり静的にできるはず • 定義と依存外部モジュール初期化を分離しないといけない
  27. 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 に同じ仕組みで例外を投げるメジャーなランタイム一覧
  28. 地味なmruby-marshal更新 • IOオブジェクトに対応 • https://github.com/take-cheeze/mruby-marshal/issues/17 • テストではStringIOを利用 • depthに対応 •

    ちょいちょいバグってたので、更新してない方は更新を… • 正常系と異常系は両方テストしよう… • C++で書いてるので、地味にC++例外を有効にする存在 • 正しい例外処理への楔にしたい • `#include <mruby/throw.h>` してるのは今すぐ`mrb_protect`系APIに移行を
  29. 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として追加するだけでいいはず… • かなり危うい作りをしているので、バグがまだありそう
  30. メソッドの静的解決 • 長年のRuby最適化できない問題に挑みたい • メソッドの再定義が自由すぎる • →でも、誰もそこまで無制限なものは望んでない • →でも、どこまで認めるべきかそれぞれのスタンスが •

    Ruby Gemのロードがすべて終わったタイミングとか? • mrubyは、mrb_open後にメソッド定義を禁止できればVMを更に最適 化できるかもしれない • 現状のスクリプト言語のJITは動的なC++テンプレートインスタンス化 のようなことをしている
  31. AST機能の強化 • TruffleRuby: http://rubykaigi.org/2017/presentations/nirvdrum.html • 標準ライブラリのRubyコードを予めパースして埋め込んだり • Rubyパーサーについて: http://rubykaigi.org/2017/presentations/aycabta.html •

    一度中間コードに変換して最適化するよりも、ASTを直接いじった方 がより相応しい最適化をできるのでは? • mrubyのASTはS式っぽい(完全なリスト構造ではない部分も) • →LISPっぽいことが! • 構文解析時よりもAST走査時にエラーをチェックしてほしい • 構文解析時にエラーをチェックするとパースが止まる…
  32. mruby-uvの強化 • libuv 1.14のAPIに対応 • OS系APIで標準ライブラリを実装するのに必要なものがだいぶ揃った • 長年のバグをいくつか直した • addrinfo酷かったけど誰も直さんという…

    • 使っている間に見つけたテストのバグをきれいにした • できないテストはおとなしくskipしといた方がよかった • Auto Fiber化したい • http://rubykaigi.org/2017/presentations/ko1.html • mruby-uvの上で標準ライブラリの実装を行いたい • ※ という妄想です
  33. mrubyにもっと必要なこと • 忙しい偉い人に今更、プロジェクト管理やってとか言いづらい • もっと開発環境を整え続ける人が必要 • ツール • テスト •

    Travis CIでは新しい言語のサポートを追加できる: https://docs.travis- ci.com/user/languages/community-supported-languages/ • サポート • それなりに貢献をしても、それが後進に伝わらずよく断絶してる • VMの(簡単な)バグ潰せるレベルの人がもっと必要 • 現状、多忙な人が1人でやってる…