Copyright © DeNA Co.,Ltd. All Rights Reserved. H2O x mruby で ⼈はどれだけ 幸せになれるのか March 4, 2017 @i110 Technology Development, System Management Unit DeNA Co., Ltd. YAPC::Kansai 御中
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oxmrubyで⼈はどれだけ幸せになれるのか
March4,2017
@i110TechnologyDevelopment,SystemManagementUnitDeNACo.,Ltd.
YAPC::Kansai御中
Copyright©DeNACo.,Ltd.AllRightsReserved.
⾃⼰紹介
! @i110
! 株式会社ディー・エヌ・エー! システム本部技術開発室所属
⁃ ⾊んな⼈が⾊んなことをやってる部署⁃ 平均年齢⾼め
! 昨年夏くらいにあれやこれやあってH2Oの開発にjoin
! 現在、業務時間の全てをH2Oの開発に注いでいる状態⁃ 最近はmrubyまわりの機能追加&改善が多め
2
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oについてさらっと
3
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oとは
! https://h2o.examp1e.net/! Google翻訳いわく
! おおむねこの通りです(⾚字部分を除いて)
! みんな⼤好き某kazuhosanが開発! 現在の最新verはv2.2.0-beta1
4
H2Oは、古い世代のWebサーバーと⽐較して、CPU使⽤率が低いユーザーに迅速な応答を提供する、
新しい世代のHTTPサーバーです。基盤から設計されたサーバーは、優先コンテンツ配信とサーバープッシュを含むHTTP/2機能をフルに活⽤し、
Webサイトの訪問者に有望な経験を提供します。
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oはなぜ⾼速なのか
! 主に3つの理由による1. 優先度制御が優秀2. 先進仕様への対応の速さ(Cache-digests、TLS1.3、etc..)3. neatかつ無駄のない実装
! 詳しくはWebで!
5
Copyright©DeNACo.,Ltd.AllRightsReserved.
しかし…
! Apacheとnginxつよい
! 理由(私⾒)1. 豊富なモジュール群2. 情報の多さ3. 枯れてる度
! 私は当然H2Oが最⾼のWebサーバーだと信じているので世界の⼈々をより幸せにするためにH2Oが普及してほしい
! 速いことはわかった。それ以外に何が必要か
6
hGps://w3techs.com/technologies/overview/web_server/all
Copyright©DeNACo.,Ltd.AllRightsReserved.
便利で快適なWebサーバーへ
! Apacheやnginxに出来て、H2Oに出来ないことを無くしたい! 拡張モジュールの整備! ユーザー⾃⾝による拡張モジュールの書きやすさ
! Apacheやnginxよりも、同じことが簡単にできるようにしたい! 設定ファイルの柔軟さ&テスタビリティ
! あとドキュメントももっと沢⼭書きますすいません
7
Copyright©DeNACo.,Ltd.AllRightsReserved.
組み込み⾔語としてのmrubyの採⽤
! 2015年7⽉にリリースしたv1.4で、RyosukeMatsumoto(@matsumotory)さんが初期実装
! リクエストハンドラとしてmrubyのコードを実⾏できる
8
Copyright©DeNACo.,Ltd.AllRightsReserved.
組み込み⾔語としてのmrubyの採⽤
! ApacheやNginxよりも強いサポート! コアに付属、デフォルトでON! ⾮同期I/Oを可能にする各種ライブラリの提供! RackSpecに準拠した⾃然なインターフェース! 今後さらに⼿厚くしていく予定
! 想定⽤途! アクセス制御、URIリライト、レスポンスの書き換え、etc..! プログラマブルで柔軟な設定が可能に! サーバー機能の⼿軽な拡張も可能に
9
Copyright©DeNACo.,Ltd.AllRightsReserved.
mrubyの何がどう嬉しいのか
1. サーバ機能の拡張性2. 設定の柔軟性&テスタビリティ
10
Copyright©DeNACo.,Ltd.AllRightsReserved.
1.サーバ機能の拡張性
! ユーザ⾃⾝が、欲しい機能をさくっと書いて追加できる状態が理想! しかしcでモジュール作るのは⼤変
! Cloudbleed(2017)! https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-
parser-bug/
! Cloudflare(CDN)で発⽣したユーザ秘匿情報の漏えい問題! 原因は、内製のnginxモジュール(HTMLパーサ)のバグ
! ポインタ管理のミスによるバッファオーバーラン! mrubyで書けば原理的に発⽣しえない問題
! コードにバグがあっても、バッファオーバーランが発⽣することはない
11
Copyright©DeNACo.,Ltd.AllRightsReserved.
2.設定の柔軟性&テスタビリティ
! 個⼈的な話にはなりますが、Apacheやnginxのディレクティブを⼀切覚えられない(覚えるつもりもない)ので毎回サーバー⽴てるたびに2時間くらいぐぐっている
! 凶悪なmod_rewrite、ミスりやすいアクセス制御、何故か効かないsection…
! 理想! シンプルなことはディレクティブで簡単に! 複雑なことはプログラマブルにがっつり
12
Copyright©DeNACo.,Ltd.AllRightsReserved.
なんとなく印象と⼀致する参考値
! ⽐較! Excelnotworking:1,170万件! PowerPointnotworking:1,040万件! nginxconfignotworking:73万件
! nginxはユーザーの⾃⼰解決能⼒が⾼いというのもありそう
13
Copyright©DeNACo.,Ltd.AllRightsReserved.
2.設定の柔軟性&テスタビリティ
! ディレクティブによる設定だと、設定がちゃんと効いているか実際にリクエストを送って試してみるしかない! 設定追加したらConflictして別の箇所が動かなくなった!とか…
! 複雑であるからこそテストを書きたいのに…
! mrubyならテストを書ける! 粒度の細かい単体テスト! スタブを使ったテスト! etc..
! 近⽇中に何かしらのHowToを公開します(たぶん)
14
Copyright©DeNACo.,Ltd.AllRightsReserved.
というわけで、本⽇は
! H2Oxmrubyの仕組みの簡単な解説! 知られざる機能の紹介
! ステルスで追加した機能! 近々マージ予定の機能
15
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oxmruby概説
16
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oのアーキテクチャ
! マルチスレッドxイベントループ! デフォルトでコア数と同じ数のスレッドが起動! 各スレッドがイベント駆動でリクエストを捌く
! 外部I/Oによるブロックはスループットに直結するので、全てのI/O絡みの処理をノンブロッキングで⾏うことが肝要
17
Copyright©DeNACo.,Ltd.AllRightsReserved.
mruby中の処理もノンブロッキングでやりたい
! 何も考えずに既存のmrbgemsを使うと簡単にブロックする
! イベントループ対応された既存mrbgemsはほとんど無い! 特にh2oでは独⾃の(⾼速な!)イベントループを使っており、例えばlibuv対応のmrbgemsがあっても普通には使えない
! CにもAnyEventみたいなレイヤがあればいいんですけどね
! H2Oxmrubyではイベント駆動の恩恵を受けられないのか?
18
Copyright©DeNACo.,Ltd.AllRightsReserved.
それFiberでできるよ
! Fiber:Rubyにおけるコルーチン/継続/協調的マルチタスク
! Perlでいうと…なんでしょうね?Coro?(使ったことない)
19
Copyright©DeNACo.,Ltd.AllRightsReserved.
FiberによるノンブロッキングI/O
20
Copyright©DeNACo.,Ltd.AllRightsReserved.
FiberによるノンブロッキングI/O
! 実際のH2Oのコード! https://github.com/h2o/h2o/blob/master/lib/
handler/mruby/embedded/core.rb#L51
! 設定ファイルから得たmrubyハンドラのコードを、FiberRunnerに変換
! 最適化のため、⾒た⽬がそこそこ怖い! 作成したFiberの再利⽤! begin-rescueのオーバーヘッド回避
! ちなみにv2.3からはもっと怖い感じになります
21
Copyright©DeNACo.,Ltd.AllRightsReserved.
RackApplicaIonとしてのmrubyハンドラ
! Rack仕様に則ったRackアプリケーションとして(ほぼ)記述可能! v2.2以前はレスポンスの書き換えが不可だが、v2.3以降で出来るようになる予定
22
Copyright©DeNACo.,Ltd.AllRightsReserved.
RackApplicaIonとしてのmrubyハンドラ
! mrubyコードを外部ファイルに置くことも勿論可能
! RackMiddleware的なことも勿論可能
! そのうちRack::Builder互換のDSLも作りたい
23
Copyright©DeNACo.,Ltd.AllRightsReserved.
H2Oxmrubyであれやこれやできるよという話
24
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl
25
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl
! v2.1から、aclという組み込みメソッドを⽤意! アクセス制御が簡単に書けるように
! サンプルケース! 127.0.0.1からのアクセスは常に許可! 192.168.*以外からのcurlは403! 特定のipからのリクエストには503! “moved”が含まれるパスへのリクエストは特定URLにリダイレクト! /admin/以下へのリクエストにはBasic認証を要求
26
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl-nginxの場合
! 多分こんな感じ
! つれぇ…! Ifの条件中で、andやorが使えないしネストもできない
! 変数使ってうまいことやりくり…
! そもそも公式がifisevilと⾔っている模様…何だと…! https://www.nginx.com/
resources/wiki/start/topics/depth/ifisevil/
27警告:そんなにちゃんと動作確認してないので参考にしないで下さい
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl-Apacheの場合
! 僕の低いApache⼒では不可能でした! 正直にいうと調べる気も起きませんでした
28
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl-H2O2.0以前の場合
! うっこわい! まぁやりたいことが素直にできはするし、読めもする! しかしもうちょっとなんとかならないものか
29
Copyright©DeNACo.,Ltd.AllRightsReserved.
AccessControl-H2O2.1以降
! なんとなくエレガントに書けるようになった!! aclブロックの中では各種DSLメソッドが使える
! allow:即座に後続のハンドラにpassthrough! deny:即座に403! etc..
! 各メソッドのブロック内では、適⽤条件を好きに書ける! ここでもaddrやpathなど、envにアクセスするためのDSLメソッドが使える
30
Copyright©DeNACo.,Ltd.AllRightsReserved.
DoSDetecIon
31
Copyright©DeNACo.,Ltd.AllRightsReserved.
DoSDetecIon
! 標準でDoSDetectorというライブラリを提供
! IPアドレスベースで単位時間内のリクエスト数をカウントし、超えたら⼀定期間BANする(403Forbidden)
! Apacheのmod_dosdetectorに相当! https://github.com/stanaka/mod_dosdetector! ロジックも参考にさせて頂きました
32
Copyright©DeNACo.,Ltd.AllRightsReserved.
DoSDetecIon-Advanced
! DoS判定時の処理はcallbackでカスタマイズ可能! ログ吐いたりアラートメール⾶ばしたり
! strategyを⾃作すれば、DoS判定ロジックもカスタマイズ可能
33
Copyright©DeNACo.,Ltd.AllRightsReserved.
DoSDetecIon–もろもろ
! 注意点! 現在の実装は、スレッド単位のローカル変数でカウントしているので、閾値をホスト数×スレッド数で割り算する必要がある
! v2.3あたりから、mrubyからRedisが使えるようになる予定(後述)なので、そういったstrategyを⽤意する予定がなくはない
! Puremrubyで書かれているため、モジュール⾃作したいユーザーさんのサンプルとして良いのでは! なおPuremrubyコードなら単にrequireするだけでよく、H2Oの再ビルドは不要
! c等を使ってmrbgemsを書きたい場合は、mrubyとh2oをビルドしなおす必要あり
! 個⼈的にはjoin初⽇に書いたやつなので思い⼊れがある(⾃分で使ってないけど)
34
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest
35
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest
! mrubyハンドラからノンブロッキングなHTTPRequestが⾶ばせる! proxy的なことが容易にできる! 2段階の同期ポイント
! req.joinしてステータスとヘッダのみを取得! resp[2].joinしてボディを取得
36
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例1
! 起動時にetcdから設定値を取得するサンプル(v2.3〜)
! 資料からは詳細省いたけど、このハンドラの外側で⾮同期処理させるやつ⼤変だったんですよ…詳しくはWebで…! 逆にいうと今はハンドラの外ではhttp_requestとか使えないのでお気をつけ下さい
37
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例2
! 複数リクエストを同時に投げて、結合して返すサンプル
38
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例2-もっと効率よく
! こんな⾵にすると、より早くレスポンスのパイプラインに載せられる! メモリフットプリントも⼩さくなる(多分)
39
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例3
! ESI(EdgeSideIncludes)のサンプル! ちなみにESIとは
! エッジサーバ(e.g.CDN)側で、動的にコンテンツを組み⽴てるためのマークアップ仕様(超古い)
! ページを構成する部品ごとにキャッシュできる
40
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例3
! 適当にこんなの作って…
41
<esi:include>タグのsrc属性を抜き出して
HTTPリクエストを送信
bodyを受信(というか同期)
Copyright©DeNACo.,Ltd.AllRightsReserved.
HTTPRequest-例3
! こう
! シンプルに⾒えるが、この機能をcで実装することをご想像下さい…
42
Copyright©DeNACo.,Ltd.AllRightsReserved.
Redis
43
Copyright©DeNACo.,Ltd.AllRightsReserved.
Redis
! v2.2から、TLSSessionResumptionのバックエンドとして使うため、ネイティブでredisサポートしてます! IDbasedの場合:キャッシュの保管場所として! ticketbasedの場合:チケットの保管場所として
! ちなみにautorotate機能付き! 2.1以前はmemcachedだけだった
! HiRedisをラップしてh2oコアに持ってる
! v2.3以降! そのredisライブラリをmrubyからも扱えるようにしたH2O::Redisがリリース予定
! キャッシュをredisから取ってきて返却する、みたいな処理をmrubyで⾏うことが可能に
44
Copyright©DeNACo.,Ltd.AllRightsReserved.
Redis:例
! 静的なページのHTTPレスポンスをまるっとredisにキャッシュする例
45
Copyright©DeNACo.,Ltd.AllRightsReserved.
OtherTCPBindings
46
Copyright©DeNACo.,Ltd.AllRightsReserved.
OtherTCPBindings
! HTTPやRedisやコア側で実装がすでにあったからそれを使えばよかった
! でも今後、他のプロトコルを使いたい場合等はどうする?! MySQL,memcached,etc..
! 個別にcでプロトコルバインディングを実装してくのはしんどい
47
Copyright©DeNACo.,Ltd.AllRightsReserved.
OtherTCPBindings
! ノンブロッキングなH2O::TCPSocketを作った
! 個別のバインディングは、mrubyでこれを使いつつ書けばよい! ので、⾮常に書きやすくなったはず! mrubyのパフォーマンスが問題になることはほとんど無いだろう想定
! とりあえずMySQLでも書いてみる予定(予定は未定)48
Copyright©DeNACo.,Ltd.AllRightsReserved.
ImageFilter
49
Copyright©DeNACo.,Ltd.AllRightsReserved.
ImageFilter
! 発表駆動開発で、今⽇までになんとかそれっぽいもの作ろうと思ってたんですけど、間に合いませんでした…
! いちおう今後こんなことも考えてますよ、的なノリで紹介させて下さい
! 主旨:! mod_small_light的な、画像をオンザフライで縮⼩やクリップしてくれるやつが欲しい
! 流⽯にmrubyで画像処理をするのは遅いだろう! 外部コマンド実⾏できたらよいのでは! それ以外にも出来ることの幅が広がるし
50
Copyright©DeNACo.,Ltd.AllRightsReserved.
ベンチマーク
51
Copyright©DeNACo.,Ltd.AllRightsReserved.
ベンチマーク
! ⽰したいこと! mrubyであれこれやっても、システムのボトルネックはどうせI/Oもしくはアプリケーションなので、問題にならないはず
! 同じ処理をするnginxと⽐較して誤差レベルの劣化しかないはず
! サンプルアプリ:! 単純なTODOリストみたいなやつのJSONAPI! https://github.com/i110/tinytodo
! 設定ファイルやwrkscriptも⼊ってます
52
Copyright©DeNACo.,Ltd.AllRightsReserved.
ベンチマーク構成
53
! 検証環境! EC2東京リージョンc4-8xlarge3台(論理コア数36)
! リバースプロキシ(H2Oornginx)! アプリケーションサーバ(Plackapp)! 負荷かけサーバ(wrkwithluascripting)
! 同⼀プレイスメントグループ、拡張ネットワーキング有効
負荷かけサーバ
revproxy(H2Oornginx)
クソアプリ負荷 proxy
sqlite
Copyright©DeNACo.,Ltd.AllRightsReserved.
事前ベンチマーク
54
! ベンチ取る前にまず、H2Oxmrubyとnginxの純粋な性能差を調べよう! アプリ抜きで、それぞれが直接200返してみる! mrubyぶんのoverheadのせいでnginxのほうが速い想定
負荷かけサーバ
revproxy(H2Oornginx)
クソアプリ負荷
sqlite問答無⽤で200
Copyright©DeNACo.,Ltd.AllRightsReserved.
事前ベンチマーク–結果
55
! この時点でH2Oのほうが速いんですけど…まじで…! クソアプリいらなかったわ…
Requ
estspersecon
d
Copyright©DeNACo.,Ltd.AllRightsReserved.
ベンチマーク:考察
! よくわからない! まぁnginxもごにょごにょ書きすぎると遅くなるということですかね! とにかく⼤勝利でよかった
! 少なくともほとんどのユースケースでは、mrubyであれこれやっても問題ないと⾔えるのではないか
56
Copyright©DeNACo.,Ltd.AllRightsReserved.
結論
57
Copyright©DeNACo.,Ltd.AllRightsReserved.
結論
! H2Oxmrubyでみんなで幸せになろう! つらみのある設定ファイルとはさよならしよう! ⾃由にサーバを拡張していこう
! 簡単に拡張機能を書けるので是⾮書いてみてください! ブログに書いたりgithubで公開してくれる等すると⼤変嬉しいです! Featurerequestしてくれるだけでも⼤変嬉しいです
! ⾊々な機能&改善が⼊る予定のv2.3にご期待下さい! むしろコアまわりのPRもお待ちしております
58