事前に検出できない問題でサービスダウンすることも

時田理氏(以下、時田):という感じでいろいろやっているんですが、ちょっとこの間のセールで、実はサービスがちょっとダウンしてしまいまして。

サービスダウンは当然避けたいものではありますが、やはりけっこう突然やってきたりするものです。

セール時は最大で10倍ぐらいのリクエストが来ると説明をしましたが、そうするとふだんは特に問題ないようなところに突然負荷が増えて。それでサービスがダウンしてしまうようなことはあるので、事前に検知できればいいんですが、発生してからわかることがいくつかあるので。そういったサービスダウンとの戦いの歴史が今までにいくつかあるので、そういった対策を今日はちょっと紹介しようかなと思います。

N+1問題と対策

まずは1つ目ですが「N+1問題」です。これはいろいろなWebサービスでよく問題になるものだと思うんですけど、クエリの発行数が増えてしまうやつです。

パフォーマンスを下げる要因のたぶん第1位なんじゃないかと思っています。実は検出自体はわりとしやすくて、RailならBulletというN+1問題を検出してくれるgemがあるので、こういったものを使うと検出自体はわりと簡単にできます。

ただ、コードレビューなどの見落としで、N+1を含んだActive Recordのコードが紛れ込んでしまうことはあるので、これは減らしたい。もちろん検出できたら直しますが、セールとかになって突然エンドポイントがすごく重くなり「あ、これN+1じゃん」となったりすることはあるので、こういったのは減らしたいと。

再発防止として、N+1の検出をCI上でやるようにしています。Bulletでは検出したらログを出し、そのままCIを失敗させることができるんですが、CIでN+1が検出されたら落とすようにしました。これでたぶんけっこう紛れ込む頻度は減ったんじゃないかな、と思っています。

スロークエリの見つけ方と対処法

次はスロークエリです。これはN+1よりも検出がしづらいものになっていて、コードレビューで見つかるというよりかは、わりとDatadogなどのAPMツールを眺めていて、SQLの発行回数が多かったり、「なんか実行時間長いね」と眺めて見つけることが多いです。

これは見つけるところは、機能も実装して、リリースをして、APMツールを眺めて、場所を特定して、そうしたらActive Recordと格闘することになります。1行で書いていますがけっこう大変で。Active Record特有のSQLの組み方次第で、重くなっちゃったりすることもあるので。ここはけっこう調査が必要で、RailsのActive Record自体のコードを読んだりすることもわりとあります。

Herokuの管理方法

次はHerokuです。HerokuにはDynoというものがあり、これはすごく簡単に言うと、アプリケーションのスケール単位です。ほかのサービスだと、わりとインスタンスなどと言うことが多いと思いますが、HerokuではDynoという単位で管理しています。

SUZURIはHerokuのPrivate Spaceを使っていますが、これはDynoの数の上限が、デフォルトで100になっています。いろいろ調べた感じ、上限を上げてもらうことは可能みたいなんですがSUZURIではDynoサイズそのものを上げることで対応しています。

Dynoサイズは1台あたりのパフォーマンスのことです。これを1台あたりのパフォーマンスを上げることで、なるべく上限に近づかないように運用をしています。1台あたりのパフォーマンスが上がるので、一応何もしなくてもマシーン自体のパフォーマンスが上がるので、速くはなります。ただ、上げただけだとあまり効率がよくないので、パフォーマンスに応じた設定を行っています。

例えば、Pumaの使用スレッド数をCPUのコア数に合わせるとか。いろいろな項目があるんですが、そういったものをやっています。

データベースの問題と対策

次はデータベースです。SUZURIはHerokuのPostgres add-onを使用していますが、これがなかなか曲者です。Postgresのadd-onは、プランによって金額は変わりますが、高いプランでもConnection Limitは500で固定されていて、かつ、HerokuのDyno単位でコネクションプールをRailsは確保しようとするので、何も対策しないと、Dynoの数×割り当てられるコネクションプールが、わりとすぐ500に到達してしまうっていうのがあるんです。

たぶん2年ぐらい前だと思いますが、このコネクションプールが500に到達してしまって、それが起因でサービスが止まってしまったことがあって。それはその時に対策をしました。

その時にやったことがいくつかあるんですけど、そのうちのいくつかは、例えばリードレプリカ用のDBを用意するとかです。書き込みをしないリクエストは、リードレプリカのほうを向けることによって、コネクションを分散できると。

あとは、先ほどもちょっと説明しましたが、Dynoサイズを上げることで、そもそも必要なDyno数が減ることによって、コネクションプールの削減ができたりしました。このような感じで対策をしています。

いろいろチューニングはしていますが、機能開発とパフォーマンスの改善はわりとトレードオフなことが多いので、戦いはまだまだ続く感じです。

これからやりたいこと1:エラーバジェットの導入

次はこれからやりたいことについて話したいと思います。まず1個やりたいことがあって、エラーバジェットの導入をしたいなと考えています。

エラーバジェットというのは、サービスでエラーレートのSLOをまず決めるんですけども。例えば、エラーレートは99.9パーセント以上を確保しようと。エラーバジェットは、そのSLOと現状の差分のことを表します。

すごく簡単に言うと、その99.9パーセントに落ちるまで、あと何回500エラーを起こしていいか、というバジェットになります。

エラーバジェットはどういうふうに使うかというと、そのエラーバジェットを使い果たすまでは新機能の追加に注力していいと。それを使い果たしたら、SLOを下回ってしまうので、新機能開発をいったん止めて、パフォーマンスとかエラーレートの改善に活動をシフトさせていくようなものになります。

エラーバジェット自体の可視化はけっこう簡単です。Datadogに、確かSLOモニターかな、という機能が最近できて、これを使うとそのエラーレート、(スライドの)下のグラフを見てほしいんですが、一番左上にSTATUSと書いてあって、これは現状のエラーレート。TARGETが目的とするSLO。エラーバジェットは、このパーセントで確か管理されるのかな? あと何パーセントの余地があります、みたいな感じで表示されます。

エラーバジェット自体は30日単位で見ることがわりと多くて、例えば月の真ん中ぐらいにエラーバジェットを使い果たしたら、月の残りはもう全部、パフォーマンス改善やエラーレートの改善に取り組む感じです。

可視化自体はできていますが、もちろんビジネスとの兼ね合いがあるので、いきなりエラーバジェットの仕組みを導入するのではなく、まずは誰でも見られるような状態にして、少しずつエラーバジェットの文化をチームに導入していこうかな、と考えています。

これからやりたいこと2:プライベートクラウドの活用

あともう1個は、プライベートクラウドの活用です。先ほども少し説明しましたが、ペパボには全社で利用できるNKEというKubernetesのクラスタがあり、これをちょっと活用したいなと考えています。

SUZURIのRuby on Railsアプリケーションは12 factor appという概念に基づいて作っています。Heroku以外へのプラットフォームへの移植性は高いので、Herokuを今は使い倒しつつ、いつでもこういったプライベートクラウドのKubernetesクラスタに移行できる状態を保とうかな、と思っています。

ただいきなりHerokuをやめるとかそういう予定はまだなくて。Herokuはすごいシンプルで使いやすいサービスなので、このあたりはデプロイの手軽さと、あとは設定の柔軟性。Herokuは簡単ではあるけれども、どうしても制約はいくつかあるので、そういった部分。トレードオフを考えながら、今後そのプライベートクラウドを活用すべきかどうかを考えていこうかと思います。

もう1個具体的にやりたいことはあって、Railsアプリケーションの機能検証で、今SUZURIはプロダクション用の環境と、あとステージング用の環境が1個ずつあって。ステージング用の環境で検証を行っていますが、どうしても複数の開発者が複数の機能を別々の機能を作っていて、機能検証したい時に検証環境が空くのを待つ必要がある課題があるので、その課題を解消するために、気軽にプライベートクラウド上で検証環境を作成できるようにしようとしています。

これはインフラの専門知識がそこまでなくても簡単に使えるような仕組みを目指していて、具体的に言うとGitHub Actions、CIをトリガーにして検証環境を作って、いらなくなったら簡単に環境ごと捨てるのを実現しようとしています。

現状はSUZURIのRailsアプリケーションが、Kubernetesのクラスタ上でDockerイメージから起動できるようになるところはできたので、これからCI上に乗せて、簡単に作れるフローを考えていく予定です。

終わりになりますが、今SUZURIではSREの活動をいろいろやっているので。SUZURIのSREに興味ある方、求人募集中なので、興味持った方は応募していただければと思います。

以上です。ありがとうございました。