カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

Javaウェブオペレーションエンジニアがトラブル切りわけ時に見ていること3つ

忘年会シーズンで肝臓への負担を極力避けている@masudaKです。今回はJavaアプリケーションの運用のポイントに関して、書いてみたいと思います。


このエントリはJava Advent Calendar 2012の22日目のエントリです。

Javaアプリケーションの運用ポイントとは

昨今ではLLのほうが敷居が低く、開発スピードも早いということからか、PHPやRubyなどのLLによるWebアプリケーションが多くリリースされているかと思います。


しかしながら、TwitterがJVMベースの開発にシフトしたように、より深いレベルで実装を行おうとした際にLL以外の実装も一つの選択肢として残っているのは間違いないでしょう。


そのようななかで自分が最もよく触れているJavaでのアプリケーションの運用ポイントについて述べてみたいと思います。

ここでいう「運用」とは、サービスをリリースしたのち、サービスへのリクエストが重くなったり、過負荷になって応答がなくなるようなことがないよう、安定したサービスを提供する業務と置き換えて頂いて構いません。

まずはフロント

まずは入り口を押さえておくのが何よりもよいでしょう。

もちろん、サービスを提供しているサーバへのpingが通らないなど、N/Wレベルでの問題は特に起こっていなく、特定の処理によりロードアベレージがあがってるとか、swapしてるとか、もう少し上のレイヤーで問題が起こっていることを想定しています。

ということで、フロントから押さえておくのがよいわけですが、JavaでWebアプリケーション構築する場合に最も多いのがTomcatによるアプリ構築かと思います。そして、フロントはApacheなどのウェブサーバでリダイレクトするかAJPで繋いで、Tomcatに渡すという環境の場合、まず「ウェブサーバの状況を見る」というのがまっさきに挙げられます。

なので、Apacheにせよ、nginxにせよ、server-statusなどにより、仕事してるプロセス(またはスレッド)と、そうではないプロセス(またはスレッド)を特定して、まずこの段階で詰まっているかを確認します。

まだリリースしてないサービスであれば、muninなどのモニタリングツールで、busyとidleの値を取ってグラフにしたおいたほうがよいでしょう。リリースして、このような値をとってない場合は今すぐ取りましょう。

それで、ps aux(スレッドを見たい場合は、-Lをつける)、topなどのコマンドやserver-statusを使って、まずウェブサーバが仕事してるかしてないかを特定します。また、PreforkやMPMの設定を見て、明らかにMaxが多すぎない/少な過ぎないことを把握し、busyプロセスがいなければ、もうバックエンドにリクエストを投げているので、ここを見るのは放置していいでしょう。

何よりもスレッドの把握

Webサーバでさばきすぎているということも全くないわけではないですが、そこで詰まるのはそこまで多くないように思います。

そうすると、なぜ応答が遅くなって、ロードアベレージやレスポンスタイムなどがあがっているかが途端に分からなくなってきます。

んで、なんかこんなリリースがされたなとかそういうのを把握してもいいですが、サーバを見てるので、そこからデータを取るのが早いでしょう。

んで、Javaアプリの場合はスレッドダンプという便利なものがあり、kill -3 PIDでcatalina.outに吐くか、jstack PIDで出力するのがよいでしょう。

んで、あとはそのまま見るか、にダンプを読み込ませて、どこで詰まっているかを見ます。

よくあるのは、APIとかDB、memcacheなどをクライアントで叩いて、そこで詰まってるケースです。その場合は待ちが多かったり、runningのプロセスがながーくlockしてたりするので、クライントの設定を疑いましょう。

httpクライアントにしても、JDBCクライアントにしても、選ぶときは色々考えて選ぶものの、どうも細かい設定まで見なく、同時実行スレッド数が2になっていたりということもあったりします。ソースを読まなければ挙動も分かりませんし、なおざりになりやすいのですが、すごーく重要なポイントなので、ここは気をつけましょう。

接続先の把握

と同時につなぎに言ってる先のデータを見ておくようにしましょう。DBであればshow processlistやqps、スロークエリを調べて、重い、または急激なクエリ増が発生してないことを把握しておきましょう。

memcachedも同様で、stats含めて平常時と多く差がないことを把握しておきます。ここで問題ない場合は、バックエンドにまでリクエストがいってないので、クライアントの設定がより疑わしくなるでしょう。

モニタリングをしてると、なんかメモリがどんどん食われていってswapしてたり、GCがうまく機能してないなということもあったりするかもしれません。そういう場合はヒープダンプを取ります。そういう場合は、「-XX:+HeapDumpOnOutOfMemoryError」や「-XX:+PrintClassHistogram」を起動オプションに設定しておき、ヒープダンプを吐かせるようにします。jmapで取ってもいいでしょう。

ただいずれにしても、良かれと思って、ヒープを多めに当ててると取得に時間がかかるので、LBから取得のタイミングで切り離す含めて、うまく実装しといたほうがいいように思います。

あとは、解析です。そのときに便利なのが「Memory Analyzer(MAT)」です。使い方などは、メモリリークトラブルシューティング記 - その5: Memory Analyzer でヒープダンプを解析(最終回) - #侍ズム
が詳しいでしょう。

んで、開放されていないオブジェクトを特定して、直す。だけといえばだけでしょう。開放大事。

終わりに

簡単ではありますが、自分が主に見ているポイントについて述べてみました。

@kazeburoさんが、ウェブオペレーションエンジニアはリリース前のソースコードのココを見ているッ! - blog.nomadscafe.jpに綺麗にまとめてくださってますが、縁あって、リリース後にJavaアプリを見ることになった場合に、可能な限りすぐ切り分けをするとなると、自分だったらこう見るなというところをまとめたつもりです。

まー、これだけ見て済めば話は早いのですが、大体根深く、JavaだけでもOSやカーネルの挙動含めて色々な知識が必要だったりするので、その辺は勉強足りない足りないと思いながら、見てるのが現状だったりします。

ということで、22日のエントリはこれにて終わりにしたいと思います(もう23日だと…)。

Adventカレンダー、昨日は@Fantom_JACさんの
今どきのウェッブサービスの作り方 - Java Enthusiast
でした。明日は@disktnkさんになります。


よろしくおねがいします。