Perl Hackers Hub

第29回 Perlプログラマのためのstrace入門ーWebアプリケーションをシステムコールレベルでデバッグしよう(3)

この記事を読むのに必要な時間:およそ 4 分

(1)こちら⁠2)こちらから。

strace実践編─⁠─Webアプリケーションは思ったとおりに動かない

最後はstraceの実践編です。

Perlで書いたWebアプリケーションの動作の妥当性は,どうやって担保すればよいでしょうか。もちろんユニットテストである程度の担保はできますし,Selenium WebDriverなどを使って実際にブラウザを立ち上げてサービス自体の動作を自動チェックしている人も多いと思います。また,自動テストをしたうえで,やはり最後は実機で動作を見るという慎重な人もいると思います。

これらはいずれも有効なのですが,どちらかと言うとプログラムの動作に対する外側からのチェックであるため,⁠memcachedにデータを効率良くキャッシュしているつもりが,実は頻繁にデータベースにアクセスが飛んでいた」⁠Redisに都度接続でTCPコネクションを張っていたつもりが,張りっぱなしになっていた」などの下回りの障害につながりかねない問題に気づくことができません。

こういった問題は得てして,気づいたときにはすでに本番で障害が発生していることが多いです。できる限り開発環境でのシステム監視などで事前に発見したいところですが,開発環境と本番環境のサーバスペックや,Webアプリケーションで立ち上げているプロセスの総数(ホストをまたいだ合計値)などが異なっているような場合には,なかなか難しいものです。

開発環境などであらかじめさらっとstraceの結果を見ておくと,上記のような問題を事前に発見できる可能性があります。

Perlで作られたWebアプリケーションで陥りがちなパターン

昨今の現場はさまざまなミドルウェアが使われているので一概には言えませんが,個人的な感覚としては,Cache::Memcached::Fastなどのクライアント側でコンシステントハッシュ法を使ってスケーリングさせるライブラリを用いてmemcachedにアクセスしている場合に,straceで観測できる次のような下回りの問題が起こりやすいようです。

  • 接続先のノード数をあまり気にせず多数のkeyに対してget_multiやset_multiを実行するコードがデプロイされた結果,リクエスト中で全ノードへの接続・再接続が発生するようになり応答が劣化する
  • アプリケーション側で生成しているkeyの値が間違っている,あるいはアプリケーション間でずれており,キャッシュを活用しているつもりが実は常にデータベースにフォールバックしている
  • 思ったよりキャッシュヒット率が低く,実はデータベースにフォールバックしている

「俺ならそんなことはやらかさない」と思う人もいるかと思いますが,実際にstraceでmemcachedへのアクセスからのMySQLへのアクセスなどの流れを見ると,思ったとおりのアクセスパターンになっていなかったということはよくあります。特に後者2つは,アプリケーションの見かけ上の動作としては問題なく,かつリクエストが少ない間は顕在化しないため発見が難しいです。しかし本番環境などへの展開前にちょっと時間をとってstraceの結果を眺めてみることで気づける確率が高いです。

これはあくまで憶測ですが,上記のような問題が起こりやすいのは,次のような理由からと考えています。

  • ライブラリ側でしれっとコンシステントハッシュ法による分割が行われるため,利用者側であまり接続先のノード数(Cache::Memcached::Fastの場合はnewで渡すserversの数)を意識しないことが多い
  • 同様の理由で,それぞれのノードに対してどういうタイミングでTCPコネクションが張られるかを意識しないことが多い

以降では,実際の現場でstraceを使うことにより問題発見・問題解決に至った実例を2つ紹介します。

実例1:epollを使っているつもりが,select(2)を使っていた

AnyEventはPerlでイベント駆動なアプリケーションを書く場合によく使われるモジュールです。通常,Linux上でAnyEventを利用したコードを書く場合は,I/O多重化のバックエンドとしてepoll_*系のAPIが利用されることを期待します。実際にはAnyEventをuseしたうえでそのバックエンドの一つであるEVがインストールされている(require可能な状態になっている)場合のみLinux上でepollが使われるのですが,本番環境で投入されているコンポーネントにEVがインストールされていなかったため,フォールバックした結果としてselect(2)が使われている,という問題がありました。

これはいささか極端な例ですが,外部から見るとどちらのAPIが使われているかはパッとはわからないところでも,straceでattachすれば一発でわかります。結論としてはselectが使われていることで問題が起こっていたわけではなかったのですが,そのままにしておくのもあとあとよくないのでEVをインストール,デプロイして修正を行いました。

著者プロフィール

横江直輔(よこえなおすけ)

意識の低いエンジニア。仕事ではPerlとかJavaScriptとか書いたり。個人AndroidアプリをScalaでちょこちょこ作っていたが,最近Google Playのアカウントを剥奪された。

App Storeにも一個アプリを出したが放置状態。Objective-C忘れました。

Blog:http://zentoo.hatenablog.com/

コメント

コメントの記入