“SRE”がテーマで2016/03/12に開催されたRetty Tech Cafe #5 で「メルカリにおける、継続的なアプリケーション改善を支える技術」という発表をしてきました。
私の発表資料はspeckerdeckにて公開しました。
デプロイや監視まわりでメルカリのSREがやっていることをまとめてみました。参考になれば幸いです。
元GoogleのSREであるRettyのCTO、樽石さんの発表では、SREが目標とする指標について簡潔にまとめられていて、とても参考になりました。自動化やクラウドが提供するサービスが中心となりやすいインフラとはちょっと違う切り口で、アプリケーションのパフォーマンスやサービスの信頼性を確保する方法、その考え方について短い時間でしたが情報交換できました。ありがとうございましたー。
SREの活動についてもっと喋れる場があるといいなー。
書いてなかった。
RubyKaigiに初参加して高速RackサーバのRhebok について喋ってきました。
内容は
となります。わりと盛りだくさんに喋りました。
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人でどのような実装方法にするか、落書き帳に雑に書きながら相談しました。
方針としては
という感じです。
やった事と時系列を整理してみる
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 281712:17:50 256612:29:32 270012:35:46 10268< ここで3台で動かす事に成功。usersとsubscriptionがmemcachedに12:41:46 1027412:52:32 0 FAIL:< このあたりはapiのcacheとuserのcacheを実装12:52:46 0 FAIL:12:58:55 1031013:04:14 0 FAIL:< end_pointのcacheの実装13:05:30 0 FAIL:13:10:16 0 FAIL:13:13:39 1029113:16:32 65 FAIL:< ログイン周りもmemcachedに13:37:11 1029213:39:28 1073213:42:13 0 FAIL:13:46:49 90068< apiのcacheが完成13:49:06 0 FAIL:13:55:56 40 FAIL:14:01:01 9867214:11:22 0 FAIL:14:14:00 7020914:16:22 100058< nginxのログを切って特別賞get14:19:52 8775314:22:37 9348714:31:43 10218014: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 10967115:10:21 97975 FAIL:15:13:12 108082< ここら辺でtenkiのcacheを1秒に15:16:17 9933115:17:29 80 FAIL:15:23:16 10074215:28:36 9952615:32:58 10312915: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 8408417:54:44 8092417:58:35 65422
特別賞以降は何をしてもダメでした。自分たちの作ったbugだったり、ベンチマーカの挙動が変わったり、再現しないエラーが発生したり、初期チェックの段階でのAPIの問い合わせエラーが発生したりとかなり右往左往してしまいました。正直1回通ったベンチマーカの挙動は変えないで欲しいなと。。
ベンチマーカの競技中での動作変更や自分たちが感じたAPIサーバの不安定さは、事前解答など、出題側でのQuality確保の時間ががあれば、ある程度回避できたのではないでしょうか。ISUCONの準備には多くの時間がかかり、通常業務への影響も少なからずありますが、それでもデスマーチが前提ではISUCON自体を長く続けることはできません。過去の出題者として、問題作成のスケジュールの持ち方や出題チームでの役割分担、事前解答といった面で協力できるのではないかと思っています。
ISUCON5 の予選にメルカリのインフラ系エンジニアで結成したチーム「GoBold」で参加して、無事2位で通過しました。チームのメンバーは、@cubicdaiya、@shmorimo、@kazeburoの3人で、普段から横にならんで座って、メルカリのパフォーマンス改善やサーバ環境の整備に携わっています。
今回の予選問題、ずいぶん盛ってきたなーというのが最初の印象。モリスさんも予選問題を本選のようにしたいと言っていたし、その通りになっていました。マシンの性能に対しての扱うデータ量もメモリに乗り切らないであろうスレスレのラインでkamipoさんの調整も神だなと思いました。おかげでやることが満載だし、素晴らしい問題でした。運営の941さんも含め本当にお疲れさまでした。本選もよろしくお願いします。
ISUCON5予選 2位で通過しました - 考える人、コードを書く人
最終的なソースコードといくつかの設定はgithubで公開しました
https://github.com/kazeburo/isucon5-elimination-public
構成は、Nignx + Perl + MySQL + memcachedです。PHPは使ってないです。はい
課題となったサイトは、ISUxiというSNSサイト、日記、日記のコメント、足跡、そしてhome.plのようなトップ画面。元m社のたんぽぽチームとアプリ運用チームの2人いるチームとしては負けてはならない課題でした。結果はfujiwara組に負けましたが。。あーくやし
やったこととスコアを時系列に整理してみると、こんな感じ。全部で72回ベンチマークを実行。
time score11: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 <ようやくsuccess13: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 196714:07:46 1926 14:10:31 1947 14:22:49 414:26:09 2144 <userをin memory化14:29:41 2137 14:38:35 2014:41:18 2160 14:48:39 1088 <enqueueが2つされる14:48:39 1117 14:51:26 2182 15:06:01 1715: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 015: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 1682316:51:06 017:06:42 17623 17:10:21 19950 17:13:38 20102 17:28:07 18107 17:37:25 1717:44:09 16217 17:49:53 14297 17:55:28 1571 <友達のいないユーザからのアクセスでエラー多発 18:00:17 018: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ベースにしてコードの確認をやっていた方が手戻りが少なかったかなと思う。
今回のデータは3GB近くあり、それに対してサーバのメモリが3.5GB、HDDはSSDではなく超遅いというのが特徴。そこで再起動試験で安定したスコアを出そうしたら、warmupが重要になる(ならなかったけど)ということで、いくつか工夫をしました。
my.cnfは以下
innodb_buffer_pool_size = 800Minnodb_flush_log_at_trx_commit = 0innodb_flush_method=nosyncinnodb_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
ぜひ、ご応募ください。私はメルカリのインフラチームのエンジニアと共に出場します。よろしくお願いします。
@uzulla氏がPHPの話を所望されていたので、PHPのエラーログ周りを整理しつつNorikraの話をしました
1日目にLightning Talkができると、自分のセッションの宣伝ができるので良い
最高のYAPC::Asiaでした。牧さん、スタッフの皆様ありがとうございました!おつかれさまでした。 再来年(?)にまた会える事を楽しみにしています。
奥さんと息子、娘が応援に来てくれたので、息子とホールの壇上でパシャリ。いつもありがとう。
久しぶりに喋ってきました。Mackerel meetup #4とShibuya.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
入社なうpic.twitter.com/OSi3NaCAnV
— masahiro nagano (@kazeburo)February 2, 2015
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は最新版で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
簡単ですね
ベンチマークは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.ruclass HelloApp def call(env) [ 200, {}, ["hello world\n"] ] endendrun HelloApp.new# run Rack::Chunked.new(HelloApp.new)
unicornの設定はこちら
$ cat unicorn.rb worker_processes 8preload_app true#listen "/dev/shm/app.sock", :backlog=>16384listen 8080, :backlog=>36384
$ 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 readRequests/sec: 325426.21Transfer/sec: 43.76MB
$ 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 56Requests/sec: 67392.13Transfer/sec: 9.06MB
$ 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 readRequests/sec: 184506.07Transfer/sec: 18.12MB
$ 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 49Requests/sec: 62455.78Transfer/sec: 6.13MB
$ 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 readRequests/sec: 113481.40Transfer/sec: 15.26MB
$ 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 29Requests/sec: 44659.91Transfer/sec: 6.01MB
HTTP/1.1に対応したRhebok、Gazelleと共にお試しくださいませ。