皆さんこんにちは、エンジニアの西尾です。
2019年9月24日(火)、フジテレビ系列のテレビ番組 「セブンルール」 にて弊社代表およびサービスを取り上げていただきました。
テレビ放送開始直後、サイトにはいままでにないアクセスが押し寄せましたが、 システムを落とすことなく、かつパフォーマンスも落とすことなく無事に稼働させることができました。
今回はテレビ放送前に、食べチョク開発チームが行ったパフォーマンス対策を紹介いたします。
開発チームのミッション
放送日が決まった時、代表が一言、Slackにつぶやきました。
放送中にサーバーは絶対落とさない。 この日から少しずつパフォーマンス対策を始めることにしました。
食べチョクのシステム構成
食べチョクはAWS上で運用しており、構成はとてもオーソドックスなものとなっています。
EC2,ELB,RDS,ElasticCache(Redis),S3,CloudFrontを主に利用しています。
フレームワークはRuby on Rails + React、DBはMySQLです。 データベースのレプリケーションは行っていません。
ボトルネックがどこになりそうか
遅そうだなあと、なんとなく思っている箇所はいくつもありましたが、 闇雲にパフォーマンス・チューニングをはじめても本当に効果があるのかはわからないものです。
ステージング環境にApache Benchをかけて、どこがボトルネックになりそうかを探ります。
アプリサーバーがとにかく重くて、性能がでない。
放送日前までに行ったパフォーマンス対策
めちゃくちゃ高性能なインスタンスとDBを立てれば、高負荷にも耐えられる可能性があります。 しかしお金が無限にあるわけでもなく、アクセス数の見積もりが間違っていたらサーバーが落ちてしまう可能性があるため、 アプリのパフォーマンス改善も行いました。
NewrelicとPrometheus(Grafana)を見ながら、1つずつ重い箇所を改善していくことにしました。
改善1. N+1 クエリ と N+1 partial rendering の撲滅
N+1クエリ、気をつけているつもりですが、ちょいちょいいました。 また一覧系のViewに N+1 partial renderingもいて、パフォーマンスを悪化させていました。
まずは簡単なところから改善。
改善2. 重い処理を分割・改善する
たとえば、以下は改善前の商品情報ページのコードですが、もうなんというか見ただけで重そうで、実際重かったです。
重い処理があるとサーバーが詰まってしまうので、一部を別処理・別APIに切り出したりしました。 Fat Controllerも改善できて、よかった。
改善3. ムダにデータを読み込んでいるところを無くす
不要なデータを読み込んで、かつレンダリングまでしている箇所がいくつもありました。 たとえば生産者一覧画面。HTMLをよくよく見てみると...
データは全部読んで、CSSで隠してました。ひえぇ。
改善4. Fragment Cacheを利用する
Railsのビューレンダリングは遅いし、重いです。
Fragment Cache を入れられるところは入れていき、 処理速度を改善していきました。
改善5. 共通ヘッダーを軽くする
ヘッダーは基本全画面に表示されますが、微妙に処理が遅かったです。 ここを少し早くすれば全ページの読み込み速度が改善されるので、キャッシュを部分的に入れるなどして速度改善をしました。
改善6. 静的ファイルをCDNで配信する
画像ファイルはCDNを経由して配信していたのですが、 jsファイルや一部バナー画像などはWebサーバーから直接配信していました。
Webサーバーの負荷は少しでも軽くしたいので、これらをCDN経由で配信するように改修しました。
改善7. RailsでDBレコード取得部分のキャッシュを入れる
アプリサーバーはスケールアウトできるのですが、 データベースは現状レプリケーションを行ってないため、スケールアウトができない状態です。
データベース(RDS)のスケールアップはすぐにはできず、 テレビ放送中にデータベースがパンクしたらシステムダウンを発生させてしまう可能性がありました。
マルチDB対応は時間的にもすぐにはできないと判断し、 今回はDBのレコードを部分的にRailsでキャッシュさせ、 データベースアクセスをできるだけ少なくするような改修をしました。
このキャッシュ機能は基本的にはオフにしているのと、重要な処理では使わないようにしています。 例えば商品詳細で商品レコードをキャッシュして、 実際には商品が売り切れているのに売出し中と表示されてしまう、みたいな問題が起きないように注意しました。 またキャッシュクリアの仕組みも作りました。
テレビ放送中は、キャッシュをTTL=15分でONにすることでデータベースの負荷を下げることにしました。
改善8. アプリサーバーのパフォーマンス・チューニング
普段はアプリサーバーにはEC2のmediumインスタンスを使っていますが、 テレビ放送当日はc5.2xlargeを使いました。
最初はxlargeくらいにしようと思っていましたが、負荷試験を再度したところ、 思いの外CPUがいっぱいいっぱいでレスポンスが悪くなりがちだったので、2xlargeを使うことにしました。
2xlargeのインスタンスに合わせて、puma(アプリサーバー)のworker数とthread数を調整しました。 また、DBのconnection poolの値もworker/thread数に合わせて増加させました。
改善9. ELBの暖気申請を出す
これはパフォーマンス・チューニングではないのですが、 ELB(ロードバランサー)は突然の高負荷でアクセス不可になってしまう可能性があります。
DevelopersIOの記事 を参考に、AWSにあらかじめELB暖気申請を出しておきました。
サーバーが落ちた時のために
放送日前日までパフォーマンス対策を行っていましたが、 システムが落ちない保証はありません。
最悪の事態に備えて、Sorryページ(エラーページ)も作ることにしました。 使う事態にならなくて助かりましたが、一応以下ページを用意していました。
そして迎えたテレビ放送
9月24日のセブンルール(テレビ放送)は、夜の23:20から始まりました。 本番開始直後の開発チームのSlackを張っておきます。焦っていて日本語がおかしい。
システムが落ちないか心配で、放送時間中にはちゃんとテレビは見られませんでした。 Grafana(モニタリングツール)とGoogle Analyticsを見ながらサーバー落ちるなと祈っていましたが、 放送開始から終了までの30分間、アクセスを無事さばき切ることができました。
パフォーマンスはどうだったか
番組放送中の食べチョクトップページと商品詳細ページのパフォーマンスは以下のとおりです。
放送中でめちゃくちゃアクセスが来ているにもかかわらず、問題ないパフォーマンスで アクセスをさばけました。
トップページ (User::TopController#show)
のパフォーマンスが0時30分過ぎから落ちているのは、
改善7でいれたDBのレコードキャッシュ機能をオフにしたためです。
商品詳細も高速にレスポンスを返しています。
おわりに
食べチョクが地上波に30分間取り上げられるのは初めてで、 サイトにはかつて無いアクセスが押し寄せましたが、無事乗り切ることができました。
パフォーマンス対策にあたり、ISUCON 参加の経験が役に立ったなと感じました。 「推測するな、計測せよ」が重要で、Newrelic+Grafana(モニタリングツール) と実装を見比べながら、地道に改善を行いました。 ISUCON、今年は忙しくて参加できなかったのですが、来年は開発チームのメンバーと出場できたらと目論んでいます。
これからも食べチョクには、どんどん新しい機能を盛り込んでいく予定ですが、 パフォーマンス対策も忘れずに実装いけたらと思います。
放送を見逃した方へ
以下サイトで10/1(火)22:30まで見逃し配信をしています。
関連リンク
よろしければこちらも御覧ください。