2012年12月アーカイブ

この一年まとめも3回目になった。去年はここ

今年は1/1の会社統合によって所属がNHN Japanに変わってオフィスが新宿=>大崎=>渋谷と流浪しましたが、仕事は変わらずやってます。その成果の一つがこれ

1年でPVが2倍に伸びました。ライブドアブログの主なニュースまとめ2012年版

もちろん良いブログ、良い記事に恵まれ、ソーシャル系サービスの波にうまく乗ったがことが大きな要因だとは思いますが、サーバ側でも逐次アプリケーションやSQLの改善をしたり、インフラの増強等をしてこれたのも良かったのではないかと自負しています。

GrowthForecast

GrowthForecastを公開したのは去年の年末でした。弊社でも様々な指標のグラフ化に利用していますし、他社でも多くの方が使って頂いているようです。

GrowthForecastというグラフ表示ツールで捗る話

GrowthForecast を初めて紹介したのは上の去年の記事でした。その後、機能追加などをしつつ、ドキュメントもまとめました

GrowthForecastに1分更新グラフ作成とサマリーなどのJSONフォーマットでの出力機能追加

GrowthForecast のデータストアに MySQL が使えるようになりました

oranieさんがsqliteのパフォーマンス問題をいろいろ踏み抜いてくれたので、SQLiteの設定を見直しつつ、MySQLにも対応しました。

やったー GrowthForecast のページできたよー\(^o^)/

そして出来たのがこのドキュメントページです

GrowthForecast - Lightning fast Graphing / Visualization

ぜひ参考にしてくださいませ。

最近モリスさんによって追加されたJSON-APIについてはmikedaさんが素敵な記事をアップしてくれました。きっとモリスさんもなんか書いてくれるはず!

GrowthForecastでAPI使って複合グラフ作ったり、グラフの色を変えたりしてみた

CPANモジュール

ブログに書いてないモジュールもあったりするけど、いくつか作った。

Twiggy::Prefork をリリースしました

某RSS集約サービスの裏側で動いていたりする。半年以上動作しているがとくに問題は起きていない

Plack::Middleware::ServerStatus::Lite にアクセス数表示機能をつけました

Proclet という supervisor モジュール書いてリリースした

今年のイチオシモジュール。機能追加もしつつ、GrowthForecastやこのあと作った様々なツールでProcletを使っている。Web+Workerな管理ツールを作る時のマストアイテムになりました。

ProcletでSTDOUT/STDERRにサービス名を追加して表示できるようになったよー

Proclet::Declare リリースしました Re: Proclet::Declare について考えた

やったー PerlでProcfileベースのプロセス管理ツール foreman の互換コマンド “proclet” できたよ

foreman互換のコマンドを作った事で複数のプロセスを使う開発も捗るようになった

SQLite のパフォーマンスチューニング、または DBIx::Sunny 0.16 の話

GrowthforecastでSQLiteのパフォーマンス問題に当たったので、その調査。DBD::SQLiteのデフォルトが変わって行くもあるので要チェック

ロードアベレージを監視して任意のコマンドを実行するコマンド

そういえば使ってない

File::RotateLogs っていうログをファイルに記録していくモジュールを書いた

社内の新サービスではAxsLog+File::RotateLogsという組み合わせが使われる事が増えてきた。

DBIx::DSN::Resolver ってのを書いた

DBIx::DSN::Resolver::Cached をリリースしました

この二つのモジュールは、今社内で導入を進めているMHA for MySQLを使う際に、DNSへの負荷を減らすために使っている。ちなみにDNSにはMySQLをバックエンドにしたPowerDNSを用いている

Perl Advent Calendar Japan 2012 Hacker Track に Plack::Middleware::AxsLog の記事を書いたよ

Yet AnotherなAccessLogモジュール。advent calendarで紹介しました。奥様は20周年の某二人組のコンサートに行ってました。

Plack上でPHP(php-cgi)を動かす

やったー、ApacheナシでNagiosうごいたよー!。今はPHPが必要だと言う理由だけで古いバージョンのNagiosを使っているので年明けにこれを使ってアップデートするつもり。

Apacheモヒモヒ

素のApacheを使うのは小学生まで

Apache 2.2.15から入った mod_reqtimeout を Reverse Proxyで使う場合の注意点

最新のログファイルにリンクが作れるようになった Apache 2.4.1 の rotatelogs を試す

2.4系のサービス投入はまだ。大きなサービスでサーバ側でSSLを使うような話があればその前に検討したいと考えてる

Apache httpd.conf の Allow from .. にコメントを書いてしまうとDNSの逆引きが行われてレスポンスが悪化するので注意の件 + コメントが書けるようになるパッチ

設定を書く時にはパッチした方が便利だとは思う

modcopyheader ってのを書いた話 Re: Apache上のPerl FastCGIはCustomLogにデータを書くことができるか?

前職時代に作ったモジュールを思い出しながら復元。nginxだともっと簡単にできるようですね

Apacheのログで取得できる%{FooBar}nの値をCGIで得るには

mod_copy_headerの逆のことをやろうとした場合。使う場所があればmod_copy_headerに機能追加してもいいかな

Apache HTTP ServerをRFC6585の”429 Too Many Requests”に(とりあえず)対応させるパッチ

試してみた

nginx-1.1.x で httpなupstreamにもkeepaliveができるようになったので検証してみた

この機能は1.2系に追加されましたが、記事で紹介しているHTTP/1.0への対応はされてない。負荷が大きめの某所でHTTP/1.0に対応させるパッチを1.2系に適用して使っている

Replication Booster for MySQL を試す

実際に常に動かしています。たまにCPU 100%で張り付いていることがあるので監視して再起動とかしてたり。

Intel SSD 910 800GB のベンチマーク

CentOS 6.2 で RPS/RFS を使ってネットワークの割り込み処理を複数コアに分散してみた

姑系記事

アプリケーションのソースや設定をみて思った事などつらつらと

Plack::Middleware::ReverseProxy でリモートホストを確認する理由

Plack::Middleware::ReverseProxy はIPアドレスを利用する他のミドルウェアより先に指定しましょう!

Plack::Middleware::AccessLog でありがちな罠

cron等をつかって外部のAPIに問い合わせる場合は、毎時0分を避けるのが大人のマナー

僕らがmemcachedのキャッシュキーにsha1やmd5のhash値を使うを避ける理由

ウェブオペレーションエンジニアはリリース前のソースコードのココを見ているッ!

話してきた話/イベント

hb qp bp study 新年LT&ビアバッシュ2012に参加してビール飲んでピザ食べてきた

MySQL Beginners Talk で LT してきました

Monitoring Casual Talk #1 に参加してきた! #monitoringcasual

YAPC::Asia 2012 で発表してきました!日本よ、これが #yapcasia だッ!!

この発表の紹介したmy.cnfの公開がこちら

MySQLの設定ファイル my.cnf をgithubにて公開しました & チューニングポイントの紹介

isucon2 を開催しました。あるいは、優勝組の5倍のスコアを出す方法

まさか2回目があるとは思ってなかったisucon。吐いて寝てたアレは後日急性胃腸炎だと診断されました

2013年は

2012年はオペレーション・エンジニアという言葉がだいぶ認知されたように思えます。おぺかじもその一つでしょうか。

ただ残念に思っているのが、DevOps界隈などでオペレーションの自動化やそのツール・派手な可視化サービスが中心に紹介されて、サービスの可用性や性能、スケーラビリティを高めるソフトウェアの工夫にはなかなか目が向かないことです。大規模ならやっていて当然なんて言ってしまう人も現れる始末です。

なので、来年はオペレーション・エンジニアの業務の目的として「Site Reliability」という言葉を使って行きたいと思っている次第でございます。

なお、4月末には二人目の子供が生まれる予定でございます。お兄ちゃんとなる長男と生まれてくる子供の成長を今から楽しみにしています

ネタではありません。メリーお正月

Plack上でみんな大好きPHPをphp-cgiを使って動かすモジュールをリリースしました

https://metacpan.org/release/Plack-App-PHPCGI
https://github.com/kazeburo/Plack-App-PHPCGI

PlackにはPlack::App::WrapCGIというモジュールもあって、これを使うと任意の言語で作られたCGIをPlack上で動かすことができます。

ただ、PHPの場合にはshebangがなかったり、実行bitも付いていないことが多いので、WrapCGIでは対応することができません。そこで、今回のモジュールを作りました。中身はWrapCGIのコピペと環境変数の追加だけでできました

どうしてこれが作りたかったかというと、管理ツールなどでPHPを動かす為だけにApacheを起動したくないからです。

これを使って試しにnagiosを動かしたpsgiファイルが以下

gist: https://gist.github.com/4386440

{index|main|side}.phpはちゃんと動いた模様。その他のcgiもWrapCGIで動かせるのでApache要らずでnagiosが動かせました!やったね!

あぱっちもひかんあんてなが反応したので

それApacheにパッチあてればできるよ!

--- httpd-2.2.22.orig/modules/metadata/mod_headers.c    2010-08-25 23:12:46.000000000 +0900
+++ httpd-2.2.22/modules/metadata/mod_headers.c 2012-12-11 16:51:07.000000000 +0900
@@ -200,6 +200,17 @@
         return "(null)";
 }

+static const char *header_request_notes_var(request_rec *r, char *a)
+{
+    const char *s = apr_table_get(r->notes,a);
+
+    if (s)
+        return unwrap_header(r->pool, s);
+    else
+        return "(null)";
+}
+
+
 static const char *header_request_ssl_var(request_rec *r, char *name)
 {
     if (header_ssl_lookup) {
@@ -853,6 +864,7 @@
     register_format_tag_handler("D", (const void *)header_request_duration);
     register_format_tag_handler("t", (const void *)header_request_time);
     register_format_tag_handler("e", (const void *)header_request_env_var);
+    register_format_tag_handler("n", (const void *)header_request_notes_var);
     register_format_tag_handler("s", (const void *)header_request_ssl_var);

     return OK;

このパッチをあてて、Apacheをビルドし直して、

CookieTracking on
RequestHeader set X-User-Track-Cookie %{cookie}n

のように設定すると、CGI側で、

HTTP_X_USER_TRACK_COOKIE => 127.0.0.1.1355212696374357

が取れました!

ちなみに CookieTracking は mod_usertrackの設定で、cookieというnotesにtracking用の値を入れてくれます。

逆にCGIからの出力をnotesに入れたい場合は、「modcopyheader ってのを書いた話 Re: Apache上のPerl FastCGIはCustomLogにデータを書くことができるか?」のエントリが参考になるよ!

mod_dosdetectorなどを使ってbotの大量アクセスを検知して、エラーをかえしたい場合に

DoSDetection on
DoSPeriod 10
DoSThreshold 30
DoSBanPeriod 60
DoSTableSize 200
DoSIgnoreContentType image|javascript|css
RewriteEngine On
RewriteCond %{ENV:SuspectDoS} =1
RewriteRule .*  - [R=503,L]

と設定したりしますが、深遠なる理由でウェルノウンな503や402を使いたくない環境で、RFC6585で追加された”429 Too Many Requests“の存在に気付き、

RewriteRule .*  - [R=429,L]

の様に設定したみたのですが、動きませんでした。

Syntax error on line xxx of /path/to/httpd/conf/httpd.conf:
RewriteRule: invalid HTTP response code '429' for flag 'R'

とエラーがでます。ApacheはまだRFC6585をサポートしていないようです

そこで無理矢理対応させたのが、

このgist。このパッチをあててApacheをrebuildすると 429が返せました。

$ curl -v http://localhost:8080/
< HTTP/1.1 429 Too Many Requests
< Date: Mon, 10 Dec 2012 16:18:56 GMT
< Server: Apache
< Content-Length: 192
< Connection: close
< Content-Type: text/html; charset=iso-8859-1
< 
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>429 Too Many Requests</title>
</head><body>
<h1>Too Many Requests</h1>
<p>You sent too many requests.</p>
</body></html>
* Closing connection #0

ただ、429をかえす場合、RFC6585では

The response representations SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait before making a new request.

とアクセスが成功しなかった理由について説明をいれることが求められているのでこのパッチのように固定の文字列で返してしまうのはイマイチなのかなと思いました。

なお、某所ではこのパッチは使用せずに、429を返すサーバを書いてproxyして対応しました。nginxの対応考えるとproxyのほうが楽

ちょいと前にツイートしたこの件のまとめ。新規サービスのリリースや既存サービスに新しい機能が追加される際に、しばしばそのソースコードを確認しているのですが、僕がどんなところを見ているのかまとめてみました。

そのサービスへの導線とランディングページの確認

どんな素敵なサービスも、機能も適切な誘導がなければ使われる事はありません。また誘導次第では大量のアクセスが一度にサーバに対してやってきます。まず確認するのは新しいサービスへの導線やランディングページで、どのページに最もアクセスがくるかということです。

リリース前という限られた時間で、全てのソースコードに目を通すことは難しいので、この最もアクセスであろうページ、1つ2つに確認対象を絞ってしまいます

コントローラのコードの調査と外部との通信の洗い出し

アクセスの多いページを確認したら、ソースコードの中でコントローラのコードを探します。最近の弊社ではWebアプリケーションフレームワークとしてAmon2の採用が増えているので見当が付けやすくて助かります。

コントローラのコードを見つけたら、そこから処理をさかのぼりながら、外部と通信を行う箇所を洗い出します。MySQL、memcached、その他データベース、外部APIなどがその対象です。多くのWebアプリケーションの場合、外部との通信を行っている箇所が性能のボトルネックとなるためです

データベースとの接続・切断のタイミング

データベースというとすぐにSQLの話になってしまいますが、その前にアプリケーションからどのようにデータベースへ接続しているのかを確認します。TCPのハンドシェイクのコストはそれなり高いので、適切な範囲で接続を維持するようにして効率性をあげます。イベントベースで動くmemcachedはコネクション維持のコストが小さいのでできるだけ長いプロセス単位での接続、MySQLは1コネクション1スレッドと少しコストがあがるのでリクエスト単位で接続を落とすようにしています

データベースの構成と参照先

ほとんどのサービスの初期のMySQLの構成は、Master:Slave=1:1になっています。この構成でSlaveを参照してしまうと、可用性が下がるなど運用に大きな問題がでます

MySQLをmaster:slave=1:1構成にして参照をslaveに向けるのがなぜ良くないか
http://d.hatena.ne.jp/sfujiwara/20110620/1308531677

slaveという名前のサーバがあると、つい参照してしまいがちですが、そこは我慢して使わないようにしましょう

キャッシュの使い方

MySQLのチューニング、適切なスキーマ設計、 SQLのチューニングももちろん重要なのですが、アクセスが集中する場所ではMySQLを使わず、memcachedやサーバのDiskにキャッシュを作って利用するのが常套手段です。

その際に気をつけるのがキャッシュが切れた時の動作、キャッシュが切れた瞬間にアプリケーションサーバからデータベースに一斉にアクセスが飛び、過負荷に陥ってしまうことです。リリース前の時間のない時でもキャッシュが切れる前にcronでアップデートするなのどの手段はとれると思われます。

またISUCON2で優勝組の5倍の性能を出す方法でも用いた、バッチで静的なHTMLに書き出し、アプリケーションサーバで処理しないという手もあります。ついついアプリケーションで処理することだけを考えてしまいますが、一歩下がった視野を持てるようになりたいものです。

アプリケーションサーバのプロセス数/プロセス生存期限

これはソースコードではないですが、本番サーバにデプロイされたあとのアプリケーションサーバのプロセス数やApacheでいうところのMaxRequestPerChildの設定値も確認します。

Perlでよく使われるStarmanStarletというpreforkなアプリケーションサーバのデフォルト値は以下のようになっています

サーバ名 MacClient(プロセス数) MaxRequestPerChild(プロセス生存期限)
Starman 5 1000
Starlet 10 100

プロセス数が適切か、生存期限が短すぎないかあたりを確認します。またStarletでは—max-reqs-per-childと—min-reqs-per-childオプションが用意され、プロセスの生成と終了のタイミングにランダム性を取り入れる事ができます。

その他/モニタリング準備

Perlのアプリケーション限定な話になってしまいますが、前のエントリーで紹介したPlack::Middleware::ReverseProxyのロード順も確認しています。

またリリース後はアプリケーションサーバのプロセスのモニタリングを行うためPlack::Middleware::ServerStatus::Liteは必ず導入をお願いしています。他の言語だとなにがあるんだろ。

まとめ

以上、こんなところを僕は見るようにしていますが、確認してきちんと見たつもりでもリリースしてふたを開けてみたら、予想以上のアクセスがあったり見逃していたバグがあったりと問題はでます。そういう時はテンション上げて問題解決に取り組みます。ヒャッハー!

参考文献

松信さんのデータベース本

Mobageのサーバサイドアプリケーションチューニングは参考になるところが多い

Perlの連載がfujiwaraさんの記事Webアプリケーションのパフォーマンス改善に関する記事です

もうひとつWeb+DB PRESSから、Vol.70のRailsアプリケーションの高速化の記事も参考となるでしょう

割と良く見る間違いです

builder {
    enable "ServerStatus::Lite",
        path => '/server-status',
        allow => [ '127.0.0.1', '192.168.9.0/24'],
       scoreboard => ..;
   enable 'ReverseProxy';
   $app;
};

これは間違いです。リバースプロキシ配下にてアプリケーションサーバを起動すると、/server-statusに対して全世界からアクセス可能になります

reverseproxy2.png

Plack::Middleware::ReverseProxyがX-Forwarded-Forヘッダをみて、REMOTE_ADDRを書き換える前、上の図の状態でPlack::Middleware::ServerStatus::Liteが実行されてしまうので、/server-statusへのアクセスが許可されてしまいます。

どうすれば良いかというと

use Plack::Builder::Conditional;

builder {
    enable match_if addr(['192.168.0.0/24','127.0.0.1']),
        'ReverseProxy';
    enable "ServerStatus::Lite",
       path => '/server-status',
       allow => [ '127.0.0.1', '192.168.9.0/24'],
       scoreboard => ..;
   $app;
};

このように、ReverseProxyはServer::Statusよりも先において、REMOTE_ADDRを書き換えてから、アクセス制限に用いてください。あと、必ず書き換える前にリモートホストを確認するようにしましょう

同様に AccessLog ミドルウェアも

builder {
    enable "AccessLog";
    enable match_if addr(['192.168.0.0/24','127.0.0.1']),
       'ReverseProxy';
    $app;
};

これでは、記録されるIPアドレスが全てアプリケーションサーバにアクセスするリバースプロキシのIPアドレスになります。クライアントのIPアドレスを記録するのであれば、

builder {
   enable match_if addr(['192.168.0.0/24','127.0.0.1']),
       'ReverseProxy';
   enable "AccessLog";
   $app;
};

としましょう。

このようにPlack::Middleware::ReverseProxyは、IPアドレスを使う他のミドルウェアよりも先に指定するのが基本です。

以前も紹介していますが、Plackのmiddlewareを指定する場合は以下の図をイメージすると良いでしょう。

pylons_as_onion.png

Plack::Builder で

builder {
    enable 'A';
    enable 'B';
    enable 'C';
    $app
};

と指定した場合、Aが外側、Cが内側になります

実はこのblogでまだ紹介していないモジュールのひとつ、「Plack::Middleware::AxsLog」について、Perl Advent Calendar Japan 2012 Hacker Trackで記事を書きました。

AxsLog - Yet Another AccessLog middleware - Perl Advent Calendar Japan 2012 Hacker Track
http://perl-users.jp/articles/advent-calendar/2012/hacker/5

まだまだ枠が空いているので、ぜひ、書いて、寿司喰いましょう。

Perl Advent Calendar Japan 2012 - Hacker Track : ATND
http://atnd.org/events/34610