書いてなかった。

RubyKaigiに初参加して高速Rackサーバの Rhebok について喋ってきました。

内容は

  • Rhebokの概要
  • Hot Deployの仕組み
  • ベンチマーク
  • rackの基本
  • 高速なRackサーバの作り方とRhebokで使っている技術
    • prefork_engine による prefork
    • io timeout の戦略
    • PicoHTTPParser による高速なHTTP処理
    • TCPの最適化

となります。わりと盛りだくさんに喋りました。

Rhebokはもちろん、アーキテクチャや使っている技術にも興味をもって貰えたらうれしいです。YAPC::Asia 2013でMonocerosについて喋ったときの資料を一部使っているのはここだけの秘密

RubyKaigiのスタッフの皆さん、通訳の方、参加者の皆さんありがとうございました。金曜日土曜日と帰るのが遅くなったので家族にも感謝。

メルカリのオフィスにて開催された PHP BLT#1 でLTしてきました。

PHPの勉強会なので、いままでお会いしたことのない方とお話ができてよかったです。

発表内容は大きくなってしまったmaster.phpファイルをどうやって高速に読むかというお話です。PHPではリクエストの終了とともに全てのメモリを捨ててしまうので、変わらないデータもリクエストの度にキャッシュからロードしなくてはいけません。大きなphpファイルがあれば当然毎回の読み込みがオーバーヘッドとなってきます。そんな環境でどうやってアプリケーションのパフォーマンスをあげていったのかを紹介しています。

スライドの中でfile sizeを小さくする必要があると書きましたが、@hnwさんによると、VM命令が多過ぎるのが問題で、構造を簡単にしたことでVM命令が減ったのがよかったのではとのことでした。非常に参考になりました。ありがとうございました

そろそろ傷が癒えてきた。。

ISUCON5の本選にメルカリのインフラ改めSite Reliability Engineerで結成したチーム「GoBold」で参加して、最も速く決められた得点に到達したチームに与えられる特別賞と最終的に3位となりました。チームメンバーは @cubicdaiya、@shmorimo、@kazeburo の3人です

出題のtagomorisさん、kamipoさん、運営の941さん、LINEの皆様、テコラスの皆様ありがとうございました。

ソースコードとやったこと。

こちらにて公開しています

https://github.com/kazeburo/isucon5-final-public

構成は、Nignx + Perl + PostgreSQL + memcachedです。

課題となったサイトは、データベースに格納してある情報からいくつかのAPIに問い合わせて、その結果をまとめてクライアントに返すというもの。流行のマイクロサービスを間違った形で実装したものということでした。題材としてはかなり面白いものでした。

最初にアプリケーションの動作やコードを確認し、3人でどのような実装方法にするか、落書き帳に雑に書きながら相談しました。

Slack for iOS Upload-4.jpg

方針としては

  • 与えられた3台で動作するように変更
  • cache時間の調整は必要になるだろうが、APIの問い合わせを全てmemcachedにcache
  • cacheはDBを変更するタイミングで行う
  • PostgreSQLへの問い合わせを出来る限り減らしていく

という感じです。

やった事と時系列を整理してみる

11:09:59    106211:23:09    1116 < rubyの初期スコア
11:53:25    84  FAIL:
11:58:22    2595 < perlの初期スコア
12:00:45    84  FAIL:
12:08:24    84  FAIL:
12:11:57    2817
12:17:50    2566
12:29:32    2700
12:35:46    10268 < ここで3台で動かす事に成功。usersとsubscriptionがmemcachedに
12:41:46    10274
12:52:32    0   FAIL: < このあたりはapiのcacheとuserのcacheを実装
12:52:46    0   FAIL:
12:58:55    10310
13:04:14    0   FAIL: < end_pointのcacheの実装
13:05:30    0   FAIL:
13:10:16    0   FAIL:
13:13:39    10291
13:16:32    65  FAIL: < ログイン周りもmemcachedに
13:37:11    10292
13:39:28    10732
13:42:13    0   FAIL:
13:46:49    90068   < apiのcacheが完成
13:49:06    0   FAIL:
13:55:56    40  FAIL:
14:01:01    98672
14:11:22    0   FAIL:
14:14:00    70209
14:16:22    100058 < nginxのログを切って特別賞get
14:19:52    87753
14:22:37    93487
14:31:43    102180
14:38:34    0   FAIL:
14:39:55    0   FAIL:
14:42:07    84  FAIL:
14:47:22    84  FAIL:
14:49:51    75  FAIL:
14:54:16    0   FAIL:
14:55:51    84  FAIL: 
15:01:34    109671
15:10:21    97975   FAIL:
15:13:12    108082 < ここら辺でtenkiのcacheを1秒に
15:16:17    99331
15:17:29    80  FAIL:
15:23:16    100742
15:28:36    99526
15:32:58    103129
15:40:24    89299   FAIL:
15:45:53    0   FAIL:
15:49:46    38564   FAIL: 1秒に。keepaliveもやめる
17:41:45    86611 
17:47:03    65252 < tenkiのcacheを3秒に
17:49:59    74  FAIL:
17:51:41    84084
17:54:44    80924
17:58:35    65422

特別賞以降は何をしてもダメでした。自分たちの作ったbugだったり、ベンチマーカの挙動が変わったり、再現しないエラーが発生したり、初期チェックの段階でのAPIの問い合わせエラーが発生したりとかなり右往左往してしまいました。正直1回通ったベンチマーカの挙動は変えないで欲しいなと。。

ベンチマーカの競技中での動作変更や自分たちが感じたAPIサーバの不安定さは、事前解答など、出題側でのQuality確保の時間ががあれば、ある程度回避できたのではないでしょうか。ISUCONの準備には多くの時間がかかり、通常業務への影響も少なからずありますが、それでもデスマーチが前提ではISUCON自体を長く続けることはできません。過去の出題者として、問題作成のスケジュールの持ち方や出題チームでの役割分担、事前解答といった面で協力できるのではないかと思っています。

ISUCON5 の予選にメルカリのインフラ系エンジニアで結成したチーム「GoBold」で参加して、無事2位で通過しました。チームのメンバーは、@cubicdaiya、@shmorimo、@kazeburoの3人で、普段から横にならんで座って、メルカリのパフォーマンス改善やサーバ環境の整備に携わっています。

今回の予選問題、ずいぶん盛ってきたなーというのが最初の印象。モリスさんも予選問題を本選のようにしたいと言っていたし、その通りになっていました。マシンの性能に対しての扱うデータ量もメモリに乗り切らないであろうスレスレのラインでkamipoさんの調整も神だなと思いました。おかげでやることが満載だし、素晴らしい問題でした。運営の941さんも含め本当にお疲れさまでした。本選もよろしくお願いします。

チームのメンバーのblog

ISUCON5予選 2位で通過しました - 考える人、コードを書く人

やったこと

最終的なソースコードといくつかの設定はgithubで公開しました

https://github.com/kazeburo/isucon5-elimination-public

構成は、Nignx + Perl + MySQL + memcachedです。PHPは使ってないです。はい

課題となったサイトは、ISUxiというSNSサイト、日記、日記のコメント、足跡、そしてhome.plのようなトップ画面。元m社のたんぽぽチームとアプリ運用チームの2人いるチームとしては負けてはならない課題でした。結果はfujiwara組に負けましたが。。あーくやし

やったこととスコアを時系列に整理してみると、こんな感じ。全部で72回ベンチマークを実行。

time score
11:22:12    52  <rubyの初期スコア
11:43:41    557 <perlの初期スコア
12:30:23    818 
12:55:18    1121    <userのrequest単位のcache、パスワードのhashの計算をperlで行うように
13:05:18    0   
13:08:44    0       
13:18:33    0       
13:22:56    19      
13:25:04    0       
13:26:37    794 <ようやくsuccess
13:30:04    882     
13:34:47    1191        
13:37:15    1191        
13:41:08    1223        
13:43:06    1198        
13:46:40    0   
13:48:00    0   
13:52:11    916 
13:54:03    19  
13:55:23    19  
13:59:30    1883    <is_friendsがmemcachedとなる, commentsテーブルにentry_user_idカラムを追加
14:02:33    0   
14:04:29    1967
14:07:46    1926        
14:10:31    1947        
14:22:49    4
14:26:09    2144    <userをin memory化
14:29:41    2137        
14:38:35    20
14:41:18    2160        
14:48:39    1088    <enqueueが2つされる
14:48:39    1117         
14:51:26    2182        
15:06:01    17
15:13:24    2026    
15:18:41    4277    <comments_of_friendsのクエリ改善と足跡の集計をperlで行うように
15:23:25    4305        
15:25:50    4242        
15:29:41    4372        
15:32:21    4376        
15:36:00    7134    <relationsのクエリからOR anotherを削除
15:40:29    0
15:44:02    8166    <comments_of_friendsのクエリ改善。limit 1000がなくなる
15:49:47    18439   <entries_of_friends,comments_of_friendsのクエリ改善、relationsをつかう
15:53:24    18873           
15:56:05    18561           
16:00:03    18167           
16:07:13    19525           
16:11:47    22126   <nginxのoptimize、unix domain socketを使ったり
16:18:35    21333           
16:22:58    21378           
16:28:49    18497   <再起動。このあたりから再起動とwarmupの試験
16:45:34    16823
16:51:06    0
17:06:42    17623           
17:10:21    19950           
17:13:38    20102           
17:28:07    18107           
17:37:25    17
17:44:09    16217           
17:49:53    14297           
17:55:28    1571    <友達のいないユーザからのアクセスでエラー多発   
18:00:17    0
18:17:52    0               
18:20:51    21243   < warmupをcatで行うようになって再起動後もスコア安定
18:27:54    21622           
18:30:27    22719           
18:34:52    22845           
18:39:46    23553   <comments_of_friends limitの調整
18:46:30    24118           
18:53:47    26300   < comment数のN+1を改善
19:00:00    25861   < 最後の再起動試験

MySQLのクエリ数が多いので、そこを減らすことから取りかかり、relationsのmemcached化とuserのin memory化を先に進め、その後に比較的重いクエリをひとつひとつ解決していきました。fujiwara組と逆かも。

アプリケーションのコード変更は3人で話し合い、次に解決する課題を整理し @shmorimo と自分とで分担して作業を行っていきました。直接masterにpushしていたけど、p-rベースにしてコードの確認をやっていた方が手戻りが少なかったかなと思う。

MySQLの設定とwarmup

今回のデータは3GB近くあり、それに対してサーバのメモリが3.5GB、HDDはSSDではなく超遅いというのが特徴。そこで再起動試験で安定したスコアを出そうしたら、warmupが重要になる(ならなかったけど)ということで、いくつか工夫をしました。

my.cnfは以下

innodb_buffer_pool_size = 800M
innodb_flush_log_at_trx_commit = 0
innodb_flush_method=nosync
innodb_doublewrite = 0

bufferpoolを減らして、ODIRECTは使っていません。innodbのbuffer_poolだけに頼るのではなくdisk cacheで行う戦略をとりました。まるでMyISAMのようですね。

そしてアプリケーションの起動スクリプトには

/bin/cat /var/lib/mysql/isucon5q/*.ibd > /dev/null 

exec /home/isucon/.local/perl/bin/carton exec -- start_server --path /tmp/app.sock  --backlog 16384 -- plackup -s Gazelle --workers=10 --max-reqs-per-child 500000 --min-reqs-per-child 400000 -E production -a app.psgi

のように書いて、メモリに乗っけてからアプリケーションを起動するようにしました。おかげでアプリケーションサーバさえ起動していれば満足のいくスコアがでるようになっています。

反省

22000がでたところで、安心してしまいGo Boldに行かなかったところが反省。アプリケーションを変更するプロセスもよくしていって、本選は(fujiwara組に)勝ちたい。

kamipoさんOracle ACEおめでとうございます。

MyNA(MySQLユーザ会)会 2015年8月 でメルカリのデータベース戦略とPHPについて喋って来たので、資料を公開します。

内容はWEB+DB PRESS Vol.88の記事に書いたこと+新ネタと、PHP(PDO)の話です。MySQL 5.7のところにみなさん驚かれていたようです。

他の方の発表では、dimSTATが面白かったですね。あのグラフをどうやって作っているのか全くしらなかったので、勉強になりました。あれはベンチマークしたくなります。また、MySQLで困っている人をみつけて助けてあげようとするkamipoさんの情熱も、どこから沸いてくるのか不思議ですが、さすがでした。

開場のyoku0825さんありがとうございました。みなさまお疲れさまでした。

実は、この会で喋る事をすっかり忘れていて、YAPC::Asiaの懇親会の時に、yokuさんに「木曜日のタイトルお願いします」僕「え??ああ、あああああ!」という会話をして思い出しました。ほんとすみませんでした。いやぁ、YAPC::Asiaで1時間のトークをしたあと中4日で40分のトークの準備は大変でした..

YAPC::Asia Tokyo 2015で「ISUCONの勝ち方」という発表をしてきました。

技術的な内容も後半にありますが、ISUCONがどうして始まったのか、ISUCONで良い成績をだすためにはどんな準備をして、チューニングのためにどんなことを考え、調査するのが良いのかについて喋ってきました。

発表が2日目の朝イチでしたが、多くの方にきて頂き、ありがとうございます。この発表でISUCONになるべく多くの方に興味を持って頂き、参加する方が増えればいいなと思ってます。

今年のISUCONの参加者募集はすでに始まってます。

ISUCON5 オンライン予選の参加登録を開始&参加チームとメンバーリスト : ISUCON公式Blog

ぜひ、ご応募ください。私はメルカリのインフラチームのエンジニアと共に出場します。よろしくお願いします。

Lightning Talk

@uzulla氏がPHPの話を所望されていたので、PHPのエラーログ周りを整理しつつNorikraの話をしました

1日目にLightning Talkができると、自分のセッションの宣伝ができるので良い

さいごのYAPC::Asia Tokyo

最高のYAPC::Asiaでした。牧さん、スタッフの皆様ありがとうございました!おつかれさまでした。 再来年(?)にまた会える事を楽しみにしています。

IMG_3757.jpg

奥さんと息子、娘が応援に来てくれたので、息子とホールの壇上でパシャリ。いつもありがとう。

久しぶりに喋ってきました。Mackerel meetup #4Shibuya.pm Tech Talk #17ではLTを、Norikra meetup #2では少し長めの話をさせて頂きました。

資料3つ貼っておきます。

メルカリでもサーバ・運用周りの仕事をしています。メルカリではzから始まるモニタリングツールをメインに使用していて、サーバ周りのさまざまなデータを突っ込んで監視に役立てていますが、カジュアルにグラフをつくって、アラートを仕掛けるという用途には向いていないなぁと思ってます。

そこで、Norikra と Mackerelを組み合わせて柔軟にログの可視化+閾値の設定を行うってのを思いついて設定したところ、結構うまく行って、それについてtwitterでつぶやいていたところ、今回のような機会を頂いたというわけです。

harukasanのログ解析ツールのまとめは非常にわかりやすく、fujiwaraさんの使い方はさすがでしたし、Gunosyの2段Norikraは目から鱗でした。みなさまありがとうございました。参考にさせて頂きます。

久々に開催されたShibuya.pmではyokohama.pmぐらいでしか紹介していなかったGazelleについて喋ってきました。前日にc4.8xlargeでベンチマークを行ったらすごい数字がでたのでびっくりした。livedoorBlogでまだ動いているようだし、安定していると思うので、ぜひ使って頂けたらと思います。

同僚のbokko氏のNginxのチューニングもよくまとまっていたし、kazuhoさんのHTTP/2の優先度制御も具体例があって参考になりました。弊社だとスマホアプリのバックエンドのAPIが中心になる訳だけど、API毎の優先度の制御とかできるのかなぁと考えてみたりしました。LTではひさびさにPerlの話がいっぱい聞けて、楽しかったです。みなさまありがとうございました。

最後に、YAPC::Asia Tokyo 2015に、「ISUCONの勝ち方」というトークを応募しています。今年も開催されることが発表され、多くの注目が集まっています。ご興味をもった方はぜひブックマークやtweet、いいねなどをして頂けるとうれしいです。

http://yapcasia.org/2015/talk/show/86ebd212-fab3-11e4-8f5a-8ab37d574c3a

1000万ダウンロードと会社の2周年という記念の日に入社しました。

iOS、Androidアプリのダウンロード数はもちろん、商品の出品数や流通額も大幅に伸びています。また、アメリカでの展開等も進んでいるので、サーバ・フロントのエンジニアを募集しています。ご興味のある方はぜひご連絡くださいませ。

3月には六本木ヒルズへのオフィス引越も予定されています。mixi入社前にlivedoorのセミナーでヒルズへ行ってから9年。ここに通うことになるとは思いもしませんでした。

今後の仕事

仕事は変わらずサーバ周りの運用・パフォーマンス改善、スケーラビリティの向上です。モニタリングツールの整備を足がかりにMySQL周りの最適化や可用性向上を行い、PHPのアプリケーションのチューニングなどに携わって行きたいなと考えております。

何よりも自分たちの選択した技術で、ユーザに信頼性の高いサービスを提供できるよう努力して参ります。引き続き、多くの皆様のお世話になると思いますので、よろしくお願いします。

ウィッシュリスト置いておきますね

去年から今年にかけて高速なPSGIサーバとRackサーバを作り、そしてPHPの会社に入ったということで、YAPC::Asia Tokyo、Ruby Kaigi、PHP カンファレンスの3つで喋ることが今年の目標です。

wrkに無理矢理なpatchをあてて、unix domain socket経由でHTTPサーバをベンチマークできるようにしてみました。

pullreqはしてない。

GazelleやRhebokといったアプリケーションサーバを作っていますが、TCP経由のベンチマークではEphemeral Portの枯渇やTIME WAITの上限にあたってしまい、ベンチマークがしづらいという問題があります。

そこでnginxをreverse proxyとして設置し、nginxとアプリケーションサーバ間をunix domain socketで繋いでベンチマークをとっていましたが、nginxがボトルネックになりやすく、直接アクセスしたいなと考えていたので、やってみました。

これを使ってRhebokとUnicornの “Hello World” ベンチマークを行ったところ、unix domain socketのベンチマーク結果はTCPに比べて、Rhebokで5倍、Unicornで3倍高速という数字がでました。

rhebok_tcp_unix.png

Rhebokは最新版でHTTP/1.1のサーバとなり、デフォルトでTransfer-Encoding: chunkedを使います。Unicornはchunked transferを行わないので、機能的にあわせるためrack middlewareを導入しているのが上記のグラフの「Unicorn + Chunked」です。

このベンチマーク結果をみると、やはりTCPの新規コネクションのコストは大きく、TCPの都度接続は避け、できるだけコネクションを使い回すか、unix domain socketの使用したいと思うでしょう。

ただ、Reverse Proxyとアプリケーションサーバ、もしくはクライアントとアプリケーションサーバ間のTCP接続をkeepaliveする場合、保持すべき接続数のチューニングが難しくなりがちなので、自分としてはお勧めしません。クライアントとのTCP接続の維持をReverse Proxyで行い、アプリケーションサーバ間との接続はunix domain socketを使うのがよいのでしょう。もちろん、両者が同じホスト上で動作していないと使えませんが。。

使い方

まずブランチ指定してcloneしてきて、make

$ git clone -b unixdomain https://github.com/kazeburo/wrk.git
$ cd wrk
$ make

-P オプションでパスを指定します。

$ ./wrk -t 1 -c 30 -d 30 -P /tmp/app.sock http://localhost/benchmark

簡単ですね

RhebokとUnicornのベンチマーク

ベンチマークはEC2のc3.4xlargeを使いました。コア数は16です。OSはAmazon Linuxです。

以下のカーネルチューニングをしました

$ sudo sysctl -w net.core.netdev_max_backlog=8192
$ sudo sysctl -w net.core.somaxconn=32768
$ sudo sysctl -w net.ipv4.tcp_tw_recycle=1

一番下はTCPのポート枯渇対策です。ただTCPのベンチマークはどれもtimeoutのエラーが出てしまっています。

Rubyはxbuildを使って2.1.0をインストール

$git clone https://github.com/tagomoris/xbuild.git
$ ./xbuild/ruby-install 2.1.5 ~/local/ruby-2.1
$ export PATH=/home/ec2-user/local/ruby-2.1/bin:$PATH
$ gem install rhebok unicorn

アプリケーションサーバのワーカー数は8とし、wrkはスレッド6個、同時接続数600で実行しました。

アプリケーションはconfig.ruに直接書いている

$ cat config.ru
class HelloApp
  def call(env)
    [
      200,
      {},
      ["hello world\n"]
    ]
  end
end
run HelloApp.new
# run Rack::Chunked.new(HelloApp.new)

unicornの設定はこちら

$ cat unicorn.rb 
worker_processes 8
preload_app true
#listen "/dev/shm/app.sock", :backlog=>16384
listen 8080, :backlog=>36384

Rhebok + unix domain socket

$ rackup -s Rhebok -O Path=/dev/shm/app.sock -O BackLog=16384 -E production -O MaxRequestPerChild=0 -O MaxWorkers=8 config.ru
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 -P /dev/shm/app.sock http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency  1.73ms  244.96us   6.62ms   81.28%
    Req/Sec 57.64k   4.04k   66.56k 69.06%
  9762337 requests in 30.00s, 1.28GB read
Requests/sec: 325426.21
Transfer/sec:    43.76MB

Rhebok + TCP

$ rackup -s Rhebok -O Port=8080 -O BackLog=16384 -E production -O MaxRequestPerChild=0 -O MaxWorkers=8 config.ru
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency 36.02ms  194.43ms   1.83s   97.66%
    Req/Sec 11.87k   2.20k   21.89k 89.91%
  2021671 requests in 30.00s, 271.85MB read
  Socket errors: connect 0, read 0, write 0, timeout 56
Requests/sec:  67392.13
Transfer/sec:     9.06MB

Unicorn + unix domain socket

$ unicorn -c unicorn.rb -E production config.ru TCP
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 -P /dev/shm/app.sock http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency  3.21ms  170.17us  11.49ms   88.06%
    Req/Sec 32.71k   2.04k   43.67k 65.95%
  5534941 requests in 30.00s, 543.69MB read
Requests/sec: 184506.07
Transfer/sec:    18.12MB

Unicorn + TCP

$ unicorn -c unicorn.rb -E production config.ru TCP
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency 33.58ms  179.94ms   1.75s   97.80%
    Req/Sec 11.01k   2.04k   22.00k 89.73%
  1873600 requests in 30.00s, 184.04MB read
  Socket errors: connect 0, read 0, write 0, timeout 49
Requests/sec:  62455.78
Transfer/sec:     6.13MB

Unicorn + chunked + unix domain socket

$ unicorn -c unicorn.rb -E production chunked.ru unix
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 -P /dev/shm/app.sock http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency  5.24ms  265.60us  13.67ms   77.33%
    Req/Sec 19.76k   1.79k   26.00k 68.71%
  3404283 requests in 30.00s, 457.77MB read
Requests/sec: 113481.40
Transfer/sec:    15.26MB

Unicorn + chunked + TCP

$ unicorn -c unicorn.rb -E production chunked.ru tcp
$ ./wrk -t 6 -c 600 -d 30 --timeout 60 http://localhost:8080/
Running 30s test @ http://localhost:8080/
  6 threads and 600 connections
  Thread Stats   Avg      Stdev  Max   +/- Stdev
    Latency   555.60ms  1.10s   3.43s   81.56%
    Req/Sec  7.85k   5.05k   26.89k 65.28%
  1339932 requests in 30.00s, 180.18MB read
  Socket errors: connect 0, read 0, write 0, timeout 29
Requests/sec:  44659.91
Transfer/sec:     6.01MB

HTTP/1.1に対応したRhebok、Gazelleと共にお試しくださいませ。

h2o はserver_starter経由のgraceful restartをサポートしているので試してみた。ついでにdockerで動かしてみた。

実際にwrkでベンチマークしながらコンテナにHUPシグナルを送り、graceful restartを行ってみたのが次の動画

左上がh2oをdocker経由で起動しているウィンドウ、HUPを受けてh2oのプロセスを入れ替えている様子がわかります。。左下はwrkの実行、右側はコンテナにHUPを送ってます。

HUPシグナルはdocker killを使って送ります。

$ docker kill --signal="HUP" $(docker ps |grep kazeburo/h2o|awk '{print $1}')

wrkの結果はこんな感じ。エラーは出ていません

vagrant@vagrant-ubuntu-trusty-64:~/wrk$ ./wrk -t 1 -d 10 https://localhost/
Running 10s test @ https://localhost/
  1 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     0.94ms    2.00ms  26.16ms   96.74%
    Req/Sec    12.71k     3.38k   24.78k    70.77%
  118981 requests in 10.00s, 45.39MB read
Requests/sec:  11896.85
Transfer/sec:      4.54MB

リクエストの取りこぼしなく、graceful restartが出来ることが確認できました。

h2o、X-Forwarded-Forやreverse proxy時にupstreamのサーバにHostヘッダを送る機能も付いたので、いよいよ実戦投入ができるようになって来たんじゃないかなと思うこのごろです。

特に、Dockerでアプリケーションサーバを動かし、デプロイの際に同じサーバ上のnginxやhaproxyの設定を書き換え、再起動しているところは、h2oにすることでパフォーマンスの向上が見込めるのではないでしょうか。

Dockerfileなど

Dockerファイルはこちらのrepositoryにあります。

Dockerfileはこのようになっています。start_serverはgolang版を使っています。インストール楽。

FROM ubuntu:trusty
MAINTAINER Masahiro Nagano <kazeburo@gmail.com>

ENV DEBIAN_FRONTEND noninteractive
RUN locale-gen en_US.UTF-8 && dpkg-reconfigure locales
RUN apt-get update \
  && apt-get -y install git cmake libssl-dev \
    libyaml-dev libuv-dev build-essential \
    ca-certificates curl \
  && rm -rf /var/lib/apt/lists/*

# go-start-server
ENV GO_START_SERVER_VERSION 0.0.2
RUN curl -L https://github.com/lestrrat/go-server-starter/releases/download/$GO_START_SERVER_VERSION/start_server_linux_amd64.tar.gz | tar zxv -C /usr/local/bin --strip=1  --wildcards '*/start_server' --no-same-owner --no-same-permissions

# h2o
ENV H2O_VERSION 20150122
RUN git clone https://github.com/h2o/h2o \
  && cd h2o \
  && git submodule update --init --recursive \
  && cmake . \
  && make h2o
COPY h2o.conf /h2o/h2o.conf
COPY start.sh /h2o/start.sh
RUN chmod +x /h2o/start.sh
WORKDIR /h2o
ENV KILL_OLD_DELAY 1

ENTRYPOINT ["/h2o/start.sh"]
CMD ["/h2o/h2o.conf"]

最後の start.shはstart_serverのwrapper script。LISTENするポートを環境変数H2O_PORT経由で変更できるようにしてあります。

例えばポート、8080 でlistenした場合、h2o.confをこのように書き、

listen:
  port: 8080
  host: 0.0.0.0

hosts:
  "127.0.0.1.xip.io":
    paths:
      /:
       proxy.reverse.url: http://localhost:8081/
       proxy.preserve-host: ON

docker runする際に、環境変数にてポートを指定します。

$ docker run --net=host -e "H2O_PORT=80" -v $(pwd)/example_h2o.conf:/h2o/h2o.conf kazeburo/h2o

H2O_PORTはスペースで区切ることで複数のポートを書く事ができます。デフォルトは80と443をLISTENします。

ちなみに、現在のstart_serverはIPv4のみをサポートしているので、設定ファイルのhost: 0.0.0.0を書かないと、h2oがIPv6もbindしようとするので、エラーとなります。

ここも参考にしてくださいませ

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

アイテム

  • Slack for iOS Upload-4.jpg
  • IMG_3757.jpg
  • rhebok_tcp_unix.png
  • rhebok.png
  • gazelle_bench.png
  • IMG_2745.jpg
  • IMG_2748.jpg
  • benchui.png
  • gf_hook_gh.png
  • gf_gh_trigger.png

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja