本セッションの登壇者
セッション動画
草野 翔と申します。インターネット上ではrosylillyという名前で活動しています(Twitter・GitHub)。2022年4月からpulse Inc.に参画し、2022年12月からLink! Like! ラブライブ! の技術統括をしています。また、ISUCON本(達人が教えるWebパフォーマンスチューニング)の著者の1人で、「ベンチマーカーの実装」の章を執筆しました。ISUCONについては予選落ちやISUCON9での優勝の後、ISUCON10からは運営側として関わっていますが、今年は選手として参加したいと思っています。
「スクールアイドルと共に暮らす」体験をアプリで
Link! Like! ラブライブ!はスクールアイドルの活動を応援するアプリで、スクールアイドルステージで日々楽しんだり、活動記録ではフルボイス&フル3Dで彼女たちの「今」を覗いたり、With×MEETSやFes×LIVEでリアルタイムな応援を楽しんだりと、「スクールアイドルと共に暮らす」体験ができます。
「リアルタイム」にはいろいろな種類があります。今回は、スクールアイドルのライブをリアルタイムに届ける・配信するという部分ではなく、ライブ配信に投稿されたコメントなどをスクールアイドルのみなさんやファンの皆さんに届ける部分のお話をしたいと思います。
リアルタイムコメントを支える魔法のような技術
リアルタイムコメントは、おおよそ下記のような技術で構成されています。
- バックエンドサーバー: Goで記述されたサーバーデーモン
- GoシングルバイナリDockerイメージとしてビルドしているので、きれいにデプロイできる
- Google Cloud PlatformのCloudRunで稼働
- CloudRunのAutoScalingを利用しているので、何もしなくてもリクエスト数に比例してうまくスケールする
- PubSub: コメントの書き込みをさばくための遅延キュー
- Google Cloud PubSubを使って遅延キューを実現し、コメントの書き込みを順次処理する
- コメントを逐一データベースに書き込むようにすると、コメントが一気に書き込まれたときにロックがかかってしまいパフォーマンスがでなくなる・レスポンスが返ってこなくなるため、これを防ぐために遅延キューを使っている
- DataStorage
- 直近10秒間のコメントタイムラインなどのごく至近的なデータはKVS(Redis)に書き込む
- 永続的な記録データはGoogle Cloud Spannerに書き込む
- プロダクト全体でRDBとしてGoogle Cloud Spannerを使用
CloudRunは正直特筆することがないくらいよくできた製品で、あまり何も考えずに立ち上げてもAuto Scalingがうまく働き、リクエストが増えると処理できるだけのコンテナ数になるように自動的に調整されます。コンテナから利用するデータストレージがGoogle Cloud Spannerであれば、Auto Scalingされた分だけ性能を引き出すことができます。
ただし、突発的な負荷にはやや弱く、リクエストを処理するためにコンテナ数を一気に増やし、不要だとわかると一気に減らすというやや派手な挙動が見られます。事前にリクエストが急増することがわかっている場合、minimum instanceを増やしてウォームアップしておくとコンテナ数も比較的安定します。
また、動作させるコンテナはシングルバイナリ+rootless+distrolessで構築し、高速に起動するようにしています。
プロダクト全体でRDBはGoogle Cloud Spannerを使用しています。Cloud SpannerはMySQLなどの他のRDB製品とは全く毛色の違うデータベースで、その違いは感動的でさえあります。
たとえば、プライマリキーにはstringのUUIDを使う必要がある、適切にshardingされるようにプライマリキーの構造を工夫する必要がある、updateを一度に実行できる行数に制限があるなどのクセがあります。料金も高いので、気軽に他のRDBから移行することは難しく、新しいプロダクトで最初から採用するのに向いています。
ただ、うまく使うと「魔法がかかったように」性能を引き出すことができます。Cloud Spannerでは、Processing Unit(PU、ノード数のようなもの)をリアルタイムに、無停止で、サーバーからの再接続なしで変更できます。そして、負荷が高まったときにPUを2倍にすると、オンラインでデータベースの性能が2倍になり、負荷がほんとうに半分になります。
たとえばMySQLでscalingして突発的な負荷が発生したときにread replicaを足すようなオペレーションをしたとすると、オペレーションそのものが危険なため、ライブ配信中にやるべきではありません。事前に負荷を見積もり、準備しておく必要があります。
一方でCloud spannerの場合は、ライブ中であってもPUをリアルタイム・無停止で変更できる点は、魔法といってもいいくらい、使ってよかった製品でした。みなさんも機会があれば使ってみてください。
最強のノブを作り込む
このような技術に支えられたGCP環境でもやはり現実は厳しく、突発負荷には弱いです。
突発負荷が発生する1つの例が、ファンの方がほぼライブの開始時間通りに集合することです。私のこれまでの別アプリでの経験では、ライブ配信ではユーザーが開始10分前ごろから接続してくることが多かったのですが、Link! Like! ラブライブ! のファンの方は開始1分前などほぼ時間通りに「うまく」集合します。たった1分間の間に想定外の人数が接続してくることは予知できるものではなく、Auto scalingでは対応できません。
また、生放送では何が起こるかわかりません。機材トラブルなどで配信が少し遅れることもあれば、スクールアイドルの発言でコメントが一気に盛り上がることもありえます。このような予想できない・突発的な負荷に対応したクラウドはありません。
そこで、「最強の調整ノブ(調整弁)」を作り込んで突発的な負荷に耐える方法をご紹介します。
ここでいう「ノブ」とは、指でまわして/動かして値を変えるつまみやフェーダーのようなものと考えてください。クライアントアプリに対して、コメントやライブ情報のポーリング間隔、1回あたりコメント取得数といったパラメータをサーバーから即時変更できるようになっています。
たとえば、1秒間隔でコメントをポーリングしていたものを2秒間隔に切り替えると、単位時間あたりの合計リクエスト数は半分になるはずなので、負荷をコントロールできます。
リアルタイム配信では一般にWebSocketやWebRTCのStream、gRPCのstreamingなどリアルタイム通信用プロトコルを使用しますが、Link! Like! ラブライブ!ではあえて使っていません。Cloud RunのAuto scalingでバックエンドサーバーの台数がどんどん増えるので、クライアントから新しいサーバーへコネクションを張り直してもらい、ポーリングを単純な個別のリクエストとして処理したほうが都合がいいからです。
また、コネクションの張り直しをリアルタイム通信用のプロトコル上で実現すると、サーバー起因でクライアントにコネクション切断と再接続をさせる必要があり、複雑な制御が必要です。
ライブ中はオペレーションチームを組んで対応しています。役割を分けて、作り込んだノブやCloud SpannerのPUをライブ中にリアルタイムで調整します。PUの変更は即時反映ではなく2~3分程度かかりますので、それまではポーリング間隔を広げて負荷を下げ、PUの変更が反映されたらポーリング間隔をもとに戻す、というようにPUの変更と同時にポーリング間隔のノブも変更します。
このように、Link! Like! ラブライブ!ではライブが落ちるということだけは絶対に許さず、見た目が「少し重いかも」程度の体感で済むようにしています。
無敗の背景にエンジニア目線の作り込み
このような技術とオペレーションの結果、リリース以来ライブ中のサーバー障害はほぼゼロです。1秒のダウンタイムもなく、一部ファンの方に30秒だけHTTPステータス500が見えてしまったことだけが過去一番の大障害です。
1回のフルオペレーションでのライブ(30分程度)中の平均オペレート回数はおよそ5〜8回で、これには一時的な変更(特定の曲の間だけ、このセリフのタイミングだけ、など)も含みます。
ノブを操作することを前提にオペレーションすることで、平時のCloud SpannerのPUをかなり小さくできるので、ダイレクトなコスト削減効果が得られています。ダウンタイムなしのサービスの安定運用とあわせてコスト削減の効果もあり、この設計にしてよかったと感じています。
もともと開発計画時には、ノブは仕様にありませんでした。われわれエンジニア側からの発信でノブを作り込んでおきたいと運営チームに伝え、Acceptされたので実装されています。運営チームにはノブの必要性と何ができるのかをエンジニアからきちんと説明して、理解を得ました。
実際にこのノブがなければライブを乗り越えられてきていないと思います。ライブの入場者は常に見積もりどおりにはなりませんし、ライブを視聴するファンの方の体験を損なわないためにはサーバーを落とさないという点は死守しなければなりません。Auto scalingですべてうまく自動調整できればいいのですが、結局最後は人間の暗算でノブを操作して持ちこたえるという選択肢を今は取っています。ただ、ログが残っていくので、どういうときにどのノブをどのように操作すればよいかというノウハウが溜まったら、仕組みにできると思います。
私自身、個人的によかったと感じたこともあります。ノブを作る時点ではそれが本当に役に立つかわからず、役に立たなかった可能性もあったかもしれませんが、エンジニア人生で「こんなこともあろうかと作っておきました」と言えた日が来たときには、このノブを作り込んでおいて本当によかったと思いました。
まとめ: 最強のノブを作っていきましょう
みなさんもぜひ、自分のサービスで「このような調整ができるノブを用意しておくとひょっとしたら役に立つかもしれない」と思ったら、チャレンジしてみてください。最強のノブを作っていきましょう。
パルス株式会社では、絶賛採用強化中です。最強のノブを作りたい・私が考えたノブの話を聞いてほしいなど、ご興味がありましたら採用サイトからお気軽にご連絡ください。
ご清聴ありがとうございました。