「ウェブオペレーションエンジニアはリリース前のソースコードのココを見る!」みたいな記事があればいいね
— masahiro nagano (@kazeburo) November 20, 2012
ちょいと前にツイートしたこの件のまとめ。新規サービスのリリースや既存サービスに新しい機能が追加される際に、しばしばそのソースコードを確認しているのですが、僕がどんなところを見ているのかまとめてみました。
そのサービスへの導線とランディングページの確認
まず、そのサービスへの導線やランディングページを確認します。そしてその一番アクセスがあろうページ、一つか二つに確認対象を絞ります
— masahiro nagano (@kazeburo) November 20, 2012
どんな素敵なサービスも、機能も適切な誘導がなければ使われる事はありません。また誘導次第では大量のアクセスが一度にサーバに対してやってきます。まず確認するのは新しいサービスへの導線やランディングページで、どのページに最もアクセスがくるかということです。
リリース前という限られた時間で、全てのソースコードに目を通すことは難しいので、この最もアクセスであろうページ、1つ2つに確認対象を絞ってしまいます
コントローラのコードの調査と外部との通信の洗い出し
その次にそのページのControllerのコードを探し、外部との通信を行う箇所を洗い出します。MySQL、memcached、外部APIなどです。そしてその接続方法や通信内容について精査します
— masahiro nagano (@kazeburo) November 20, 2012
アクセスの多いページを確認したら、ソースコードの中でコントローラのコードを探します。最近の弊社ではWebアプリケーションフレームワークとしてAmon2の採用が増えているので見当が付けやすくて助かります。
コントローラのコードを見つけたら、そこから処理をさかのぼりながら、外部と通信を行う箇所を洗い出します。MySQL、memcached、その他データベース、外部APIなどがその対象です。多くのWebアプリケーションの場合、外部との通信を行っている箇所が性能のボトルネックとなるためです
データベースとの接続・切断のタイミング
MySQLやmemcachedへの接続でたまにあるのが、通信する度に接続と切断を繰り返すタイプのコード。適切に接続を維持ようにします。memcachedはプロセス単位で、MySQLはリクエスト単位で接続を管理するポリシーをよく採用します
— masahiro nagano (@kazeburo) November 20, 2012
データベースというとすぐに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の設定とかインデックスとか当たり前過ぎですが、アクセスがかなり集中する場合はMySQLに接続せず、memcachedやローカルにキャッシュする、もしくは静的にHTML等に書き出してしまうのも考慮にいれて下さい
— masahiro nagano (@kazeburo) November 20, 2012
MySQLのチューニング、適切なスキーマ設計、 SQLのチューニングももちろん重要なのですが、アクセスが集中する場所ではMySQLを使わず、memcachedやサーバのDiskにキャッシュを作って利用するのが常套手段です。
その際に気をつけるのがキャッシュが切れた時の動作、キャッシュが切れた瞬間にアプリケーションサーバからデータベースに一斉にアクセスが飛び、過負荷に陥ってしまうことです。リリース前の時間のない時でもキャッシュが切れる前にcronでアップデートするなのどの手段はとれると思われます。
またISUCON2で優勝組の5倍の性能を出す方法でも用いた、バッチで静的なHTMLに書き出し、アプリケーションサーバで処理しないという手もあります。ついついアプリケーションで処理することだけを考えてしまいますが、一歩下がった視野を持てるようになりたいものです。
アプリケーションサーバのプロセス数/プロセス生存期限
これはソースコードではないですが、本番サーバにデプロイされたあとのアプリケーションサーバのプロセス数やApacheでいうところのMaxRequestPerChildの設定値も確認します。
Perlでよく使われるStarmanやStarletという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アプリケーションの高速化の記事も参考となるでしょう