wtatsuruの技術方面のブログ

はてなスタッフ id:wtatsuru です。日常ブログはこちら https://tatsuru.hatenablog.com/

ISUCON5 本戦出場してきました

ISUCON5 本戦に、同僚の id:motemen id:ichirin2501 と一緒に 2nd Party Cookies として出場してきました。
結果は7位に終わり、トップの fujiwara 組に3倍近い差をつけられてしまう結果となってしまいました。

まず全体

今回の題材は、社長が作ったマイクロサービスアプリの改善。予選の凝ったDB設計とアプリからはがらっと変わって、DBは PostgreSQL に変わったもののそこは焦点からは外れ、外部APIコールの最適化がメインのテーマとなりました。
予選に続いてとてもいい問題で、ベンチマークもスムーズで非常に楽しめました。運営の皆様は大変だったと思いますが、ありがとうございました。
個人的には、初動や全体の方針はそこまで悪くなかったものの、インフラ担当としての開発支援や視野の広さ、実現に持っていく力という部分での差の大きさを痛感しました。

当日の動き

Slack ログを見ながら、自分の動きを中心に振り返ってみます。自分以外のやつはちょっと間違いあるかもしれません。

開始から初動
  • 11:00 開始
  • 全員のログイン設定、デプロイまわりを整える
  • 計測系のパッケージ入れたり最低限のセットアップ
  • nginx のログ調整で alp でのアクセスログ解析準備
  • Perl 実装への切り替え、一発目のベンチマーク。1102
  • 11:30 くらいから最初のログを見つつ、DBの様子・サーバの環境を観察したりして作戦を考える
最初の作戦会議

1時間くらい観察したところで作戦会議。

  • `/data` で外部APIを叩きに行くところが支配的。リソースぜんぜん使い切れてない。ここをキャッシュするとよさそう
  • DB が PostgreSQL で驚いたが、データ量も小さく触らなくてもよさそう。 endpoints テーブルをハードコードする以上のことはあまり必要なさそう?
  • APIも手元から叩けないし PostgreSQL そのままだと通らない(結局プラグインが必要とのことでした)で、手元環境作るのけっこうしんどいと判断。3台あるので実験用には別のものを使う
まずはやれるところを

やり残したこと、まずやるとよさそうなことの準備を進める。

  • 外部APIの接続先とリクエスト時間をログに残して観察。キャッシュで50倍くらいまではいけそう、などと検討
  • nginx で keepalive, static file 配信(実はミスってた)
  • 1台なのでまずは Gazelle + Unix domain socket 化
  • memcached を1台用意してキャッシュする
  • zipcode も郵便番号と住所なんだしAPI叩かなくていい?じゃあDBに入れるか、という大きめの改善を by id:motemen さんがサッと入れてくれた。かっこいい
  • そろそろCPUを使い切ってるので、3台使ってみる。PostgreSQL 外部から接続でちょっと手間取ったが、これ以降は PostgreSQL 全く触っていない。

キャッシュを入れた時点で14519で暫定1位、zipcode で18768、全台使って 33774。このへんで14時過ぎ、勢いづいてくる。
いったん作戦会議を挟む。

ゆきづまり

特別賞が10万点ってことは、そのへんまではいけるはず、もっと飛躍が必要ということでいろいろ考える。

  • static file のアクセスが目立つので gzip 入れて etag / expires などで改善という話を id:ichirin2501 とする。そして効かなかったので戻す。config のちょっとしたミスで効いてなかったことがあとで判明。。
  • JSON parser 負荷高い、という仮説のもと id:ichirin2501 が改善を始める
  • nginx のCPU使用率が目立つ。クライアントの keep-alive はわりときいてる。worker 2, Starlet にして proxy-app 間を keep-alive をきかせてみる。
  • API リクエストの並列化は優先度高いのでは、という相談をしてそちらは id:motemen さんに依頼。
  • サーバ構成で DB と nginx は分けてみた

何だかスコアが伸びないし fail しまくる、nginx 499 出まくっている、という現象に悩まされる。並列リクエストの方は、再現性の微妙なエラーで行き詰まる (endpoints の1つが1コネクションしか受け付けない、ということだったのでそれもあったかもしれない)。さらに zipcode のデータを全部メモリに読み込んでいた結果、swap 発生で全台死にかける、など散々な状況。デバッグしにくいものを実機上でデバッグしていたりして、互いにロックしてしまったのがもったいなかった。

復活

いったん元に戻し、あらためて一つ一つ見ていく。

  • static file の配信まわりでミスっていたのを id:ichirin2501 の指摘により修正。マジ感謝
  • proxy-app 間の keepalive をやめることで 499 大幅減少。間のいろいろな改善が効いて 52097。3位くらいだったと思う
  • nginx ミスがわかったので再びクライアント最適化系を入れる。sys 減らそうとネットワーク系も少しいじる。

記録残すの忘れてるが、16:30過ぎくらいに 76000 くらいまでいってた気がする。トップまであと5万よっしゃいくぞ、と盛り上がった。この時点でもまだサーバリソースは使い切れていなかった。

失速、敗北
  • 70000こえたあたりで、fail が目立ち始めた。API の内容をキャッシュしすぎている、というようなエラー。
  • よく出る Tenki API のキャッシュを切ってみるものの、スコアが1桁落ちる。
  • 一度再起動試験を入れて無事に成功。しかしスコアは全く伸びず。。
  • id:motemen id:ichirin2501 2人がかりのキャッシュ対策を見守りながら、最後の1時間でマージするちょっとした改善の準備をさっと行う
  • API の傾向とレスポンスを観察。tenki の Last-Modified が3秒おきなのを把握。If-Modified-Since つけてもダメ、と思ってしまったが、何かミスがあったような気がしてならない。。
  • 特定のAPIのみのキャッシュ期間の調整がうまくいかない。バグがあったのかもしれない
  • しょうがないので static file 関連の最適化を外し、わざと遅くすることで fail を防ぐ。5-6万あたりで安定して通るようになった

最終スコアは 57,492。fail してもおかしくない状態だったし、悔いの残るスコアになってしまった。

反省点

具体的な反省点。

  • 外部APIの特性をギリギリまで見ていなかった。そこが焦点になった時点でAPIの特性を観察しておくべきだった。
  • 開発効率へもう少し気を配りたかった。開発環境がない、デプロイが微妙に遅いなどの点。トップの fujiwara 組は、サーバ上に Squid を立てるなどして手元で開発できるようにした、とのこと。そのあたりにコストをかけておきたかった。
  • もう少し全体を見て、方針のリードをしたかった。とくに本戦では配置と最適化のポイントの絞りこみが重要なことはわかっていたので、引いた目線へのリソース配分はすべきだった。

これから

2年前のISUCON3 よりは手ごたえがありましたが、もうひと伸びの必要性を感じました。上位は全て出題経験者だったし、ひとまず社内ISUCONなんかで出題経験を積みたいなーとなんとなく思っています。
あとは普段の仕事を真面目にがんばるだけですね。。要素技術についてはそんなに負けてないと思うので、幅を広げる・考察を深めるというあたりに注意してがんばっていきたいと思います。

ISUCON5 予選通過しました

ISUCON5 予選に参加し、無事に決勝に進むことができました。今年は、社内で id:motemen id:ichirin2501 と、 2nd Party Cookies というチームで出場しました。id:motemen がアプリ、id:ichirin2501 はアプリ / DBまわり、自分はインフラ全般を担当しました。結果は総合10位で決勝進出ですが、同じく社内若手エンジニアのチーム「はむちゃん」に負けてしまって悔しさいっぱいです。

やったこと

  • 事前準備として、分析等の環境整備用セットアップスクリプト、nginx / slowlog の解析準備、ミドルウェアよくある設定復習、デプロイ雛形あたりまではやって臨む。
  • 最初1時間とってた準備時間でサーバ構成を把握して、サーバ環境整備+最初のログ取得・解析。2台立てて1台はsandbox化。
    • 普段あまり使っていない systemd でちょっと手間取る。
    • ログの分析結果とサーバリソース・使用状況を見つつ作戦会議。
  • リクエストに加えてベンチマーカーの特性も観察する。
    • ヘッダにルーティング情報入れて、alp で URL ではなくそちらで分析できるようにしたのではかどる。
  • あとは MySQL, nginx, Plack あたりのチューニングをちまちま。/etc/my.cnf がダミーとは..。
  • 昼過ぎくらいで一通り終わったので、やり残しを消化しながらクエリを見たり再起動対策したり。このへん、細かめの施策で時間を潰してしまいがちだったかも。
  • 最後に再起動試験と変更コストの大きな変更トライ、そしてベンチマークガチャ。

振り返る

予選は堅実にやろうと考えて、ある程度の事前準備はやる、使ったことないものはなるべく使わない・副作用のありそうなことは必要に応じて最低限、という原則で臨みました。最低限の役割は果たせたと思いますが、攻めが足りなかったことと、もっとやりたいけど知見が足りなくて手を出せていない部分が少し残ってしまった感じがあったので反省です。とくに、普段使わないけどISUCONでは必要になるノウハウあたりはもう少し手を動かしておこうかなと思っています。

冒頭にも書いた通り若手に負けて悔しいというのは本当に大きいのですが、ISUCON3以来の出場でチーム数見てびびってた身としては、決勝進出できて若干ほっとしている部分があります。少し自信戻ったし、優秀なチームメイト2人もいるので、ぜひ優勝狙っていきたいと思っています。

運営の皆様、この規模は本当に大変だと思います。ありがとうございました。手ごたえのある問題、快適なベンチマーカーもよかったです。決勝も期待しています。

こちらもどうぞ

アプリとクエリ改善の戦略などは motemen さんのエントリにまとまっています。
ISUCON5 予選通過したが若手に負けました #isucon - 詩と創作・思索のひろば

はてなにおけるサーバリソース可視化とMackerel

こんにちは。id:wtatsuru です。
この記事は はてなエンジニアアドベントカレンダー2014 の19日目です。昨日は、 id:hakobe932 による golangで書かれたSlack bot でエンジニアに話題提供しよう - はこべブログ ♨ でした。
今日は、Webサービスのシステム運用において不可欠なサーバリソース可視化の、はてなにおける運用の簡単な紹介をします。

はてなとサーバ管理ツールと可視化

サーバを運用する上で、日頃のサーバのリソース等の可視化と監視は必要不可欠なものです。その対象はCPU使用率やメモリ使用量などのOSレイヤから、ミドルウェアプロセス状況、レスポンスタイムまで多岐にわたります。このデータを蓄積しておくことで、システムの状態を正確に把握し、問題に対処することが可能となります。*1

f:id:wtatsuru:20141219200526p:plain

はてなでは以前から、内製のサーバ管理ツールでサーバの管理を行っていました。サーバが作られると中央の管理ツールに登録され、その役割から必要な情報を自動で取得・可視化する仕組みを作っていました。
はてな社内のサーバ管理ツールに関しては、id:y_uuki による以下のスライドが詳しいです。
YAPC::Asia 2013ではてなのサーバ管理ツールの話のはなしをしました - ゆううきブログ

システムが大きくなるほど、可視化の仕組みが大きくなるほど、気軽に新しいグラフを組み込むのが難しい、という課題が出てきていました。GrowthForecast 等の簡単に投稿・可視化できるツールを導入していたものの、やはりマスタ情報と一元管理された場所で見える、というメリットは捨てがたいものです。

Mackerelを使った可視化

f:id:wtatsuru:20141219200556p:plain

今年リリースされた Mackerel には、そんな可視化のノウハウも詰め込まれています。
Mackerel は標準のエージェントを入れるだけで基本的な情報は可視化されますが、さらに独自のカスタムメトリックを登録することが可能になっています。カスタムメトリック取得は、下記のようなSensu形式の出力をするコマンドを登録する形をとっています。

{metric name}\t{metric value}\t{epoch seconds}

これにより、スクリプトをさっと書いてとりあえず入れておき、必要であれば全体に展開する、などの柔軟な運用が可能となっています。

実際に使用している、「さっと書いた」プラグインはこのような感じになります。

f:id:wtatsuru:20141219200628p:plain


カスタムメトリックについて、詳しくはヘルプを参照してください。
ホストのカスタムメトリックを投稿する - Mackerel ヘルプ

公式プラグイン

ミドルウェアのメトリック可視化に公式プラグイン集を使う - Mackerel ヘルプ
公式プラグイン集には、はてなで実際に試用されているミドルウェアプラグイン等が取り込まれています。ここでは、「グラフの出力と定義を同時に配布できる」というもうひとつの特徴が試用されています。
はてなでも、基本的に全サーバにMackerelを投入しさまざまなメトリックを収集しております。
MySQL のグラフなど、ユースケースによってもっとこういうのが欲しい!というものがありそうなので Issue, Pull Request お待ちしております。

f:id:wtatsuru:20141219200653p:plain

プラグインの作り方に関しては、以下の記事がとてもわかりやすいです。
Mackerel プラグインを書いてみよう - インフラエンジニアway - Powered by HEARTBEATS

まとめ

以上、はてなでのサーバリソース可視化の運用の簡単な紹介でした。社内の運用にも Mackerel を使用しつつサービス運用・改善に取り組んでおります。
明日は id:sakahara さんです。よろしくお願いします!

*1:まあ単純にグラフが並んでいるとわくわくしますよね。

crontab の割り算と大きな数字

crontab にこんなのが書いてありました

*/120 * * * * $COMMANDS

書いた人は2時間おきに実行したかったんだと思いますが、まあうそっぽいですよね。
ちなみに crontab(5) には "steps" と書いてあります。

       Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".

ISC cron 3.0 の entry.c を観察。
まず * は最小から最大まで、と同じ意味。なるほど。

	if (ch == '*') {
		/* '*' means "first-last" but can still be modified by /step
		 */
		num1 = low;
		num2 = high;

で、num1-num2/num3 の形式で記述されてると num3 を step として単純に num1 から num2 まで増やしてる。大きすぎる数字を入れると一発目しか実行されない。

	/* range. set all elements from num1 to num2, stepping
	 * by num3.  (the step is a downward-compatible extension
	 * proposed conceptually by bob@acornrc, syntactically
	 * designed then implmented by paul vixie).
	 */
	for (i = num1;  i <= num2;  i += num3)
		if (EOF == set_element(bits, low, high, i))
			return EOF;

結論

大きな数字を書いても意味がない

Twitterでのやりとり

@songmu さんの言う通りの結論でした。

新しいReserved Instance

AWSの新Reserved Instanceの話です。

Amazon Web Services ブログ: 【AWS発表】EC2のリザーブドインスタンスモデルがシンプルに
購入オプションを見ると、Heavy Usage がなくなって焦ったのでがんばって追いつきました。こちらの記事がたいへん参考になりました。

Amazon EC2のリザーブドインスタンスで重度の購入オプション拡充&軽度/中度の廃止 | cloudpack技術情報サイト

どうなったのか

三行で

  • 従来の "Heavy Utilization" のみに集約された
  • 初期費用の払い方で、最初に一括・半分払って残りは月々・初期費用無し月額のみ、の3つのオプションあり。真ん中が従来のと同じ
  • インスタンス起動の有無に関わらず料金は同じ

感想

基本Heavyで買って、AutoScale な部分をどうするか、〜〜〜、これは人間が考えるものではない!!!という議論をしていましたが、過去のものとなりました。使う分だけ買いましょう。
リソース確保のためだけに light っていうやり方ができなくなりました(最近はそんなに困らなくなったんでしょうか)。料金面では、頑張らなくて良くなった分、頑張っていたよりは高くなることもあるかもしれませんね。

おまけ

こういうので表にしました https://gist.github.com/tatsuru/fd048d331135b00b30b0

multilog が作るディレクトリのパーミッション

ここで固定されてる。
https://github.com/daemontools/daemontools/blob/master/src/multilog.c#L312
damontools-toaster で堅いこと言うなよ、というパッチを当てたりする。よい子は真似してはいけない。

diff -Naur ./src/multilog.c ../daemontools-0.76/src/multilog.c
--- ./src/multilog.c    2014-09-16 15:52:28.654130526 +0000
+++ ../daemontools-0.76/src/multilog.c  2014-09-16 15:51:25.779141973 +0000
@@ -309,7 +309,7 @@
   if (fchdir(fdstartdir) == -1)
     strerr_die2sys(111,FATAL,"unable to switch to starting directory: ");
 
-  mkdir(d->dir,0700);
+  mkdir(d->dir,0755);
   d->fddir = open_read(d->dir);
   if ((d->fddir == -1) || (fchdir(d->fddir) == -1))
     strerr_die4sys(111,FATAL,"unable to open directory ",d->dir,": ");

Docker Index Trusted Build してみた

Docker Index の Trusted Build ってやつをやってみた。
tatsuru/ikachan Repository | Docker Index
やり方はこの辺に書いてある通り Docker Index Help Documentation and Support | Docker Index

  • まずは適当にリポジトリを作る tatsuru/docker-images · GitHub
    • Dockerfile 1枚のリポジトリが並ぶのは嫌だったので、ikachan/ 以下に Dockerfile と README を置く。
  • Trusted Build のページから適当に Build 追加。Github の hook が作られる。
  • 自動ビルドされるので、 build statusのページ を眺めるだけ
    • Build Queue ってのがあって、自分の場合は20分くらい時間がかかった。CI的に使うには向いてなさそう。
  • ビルドされたら Transfer Queue に入って転送される。これはすぐに終わった。

repository information には README.md と Dockerfile がうまいこと結合されたやつが入ってた。
公開用のビルドは全部これでやると、たしかに安心できる。もうちょっと速くなればなぁ。

はてなで一緒に働きませんか?