AWSフル活用!クッキングLiveアプリ「cookpadLive」を支える技術

メディアプロダクト開発部の長田(@osadake212)です。
私の主な仕事は、CookpadTV 株式会社のサービス開発をすることです。CookpadTV ではたくさんのサービスを同時に開発しており、今回の記事ではそのたくさんのサービスの中の一つである cookpadLive とそれを支える技術について、利用している AWS サービスを中心に紹介します。

cookpadLive チームメンバーによる、過去の発表や記事と被る箇所もあるのですが、この記事では全体を眺めることができるように紹介していきます。

cookpadLive とは

cookpadLive

cookpadLive とは、料理上手な有名人と Live 配信で一緒に料理が楽しめるクッキング Live アプリです。視聴者は Live 配信中にコメント機能を使って、料理のわかりづらいポイントを質問したり、作って欲しい料理のリクエストをすることができます。
普段あまり料理をしないユーザーも楽しめる Live 配信になっていて、 cookpadLive をきっかけに料理を作るユーザーもいます。

Live 配信中にはコメント以外にも多くの機能があります。

  • コメント・ハート・スタンプ(スタンプは有料会員であるゴールド会員限定)
  • Live 配信後半にゴールド会員だけが視聴できる スペシャルTIME
  • 広角カメラによる配信映像で Live を楽しめる スタジオ観覧モード
  • Live 配信中に料理やレシピカードを購入できる スペシャルSHOP
  • Live 配信中に出演者と生電話ができる スペシャルTALK

これらの機能は Live 配信によって取り外しが可能になっていて、企画に合わせて最適な機能を組み合わせることができるようになっています。

また Live 配信以外にも、アーカイブやスペシャル VIDEO のような機能もあります。

cookpadLive の歴史

cookpadLive は 2018年にリリースしました。リリース当時は Live 配信を視聴する機能しか備わっておらず、アプリの UI、バックエンドの構成も現在とは大きく異なっています。

特徴的な変遷を紹介すると

  • コメント配信システムを Firebase から AWS へ乗り換え
  • ユーザーが増えたことによるスパイクに耐えるための仕組みの整備
  • ゴールド会員という月額有料プランを用意し、スペシャル TIME, スタジオ観覧モードのようなゴールド会員向けの機能を実装
  • 都度課金にもチャレンジしてマイクロサービスを作ったが、うまく利用できなかったのでマイクロサービスの撤退
  • Live 配信リソースを効率よく利用するために Live 配信システムを刷新した
  • スペシャル TALK、スペシャル SHOP の機能を実装

こんな感じで、小さくリリースしてから、積極的に新しいことにチャレンジし、失敗しながらも前に進むために継続的に開発をしています。

※こちらの記事にスパイクに耐えるための仕組みの詳細について記載しているので、合わせて読むと分かりやすいです。
cookpadTV ライブ配信サービスの”突貫” Auto Scaling 環境構築

cookpadLive のシステム概要

クックパッドでは AWS を積極的に利用しています。

Rails や go などのアプリケーションサーバーは、社内で Amazon ECS を使って動かす環境が整っているので、 cookpadLive のアプリケーションサーバーもその仕組みを使って動かしています。( Dockerfile と jsonnet を書くと本番環境で動き始める素晴らしい仕組みです。*1
基本的な Web アプリケーションであれば上記の仕組みだけで十分なのですが、 cookpadLive のように映像を扱ったり、リアルタイムでなにかをしたりする場合には工夫が必要です。AWS にはさまざまなサービスがあり、 cookpadLive の新機能を実装するときには、まず AWS のサービスをうまく使うことで実現できないか、という観点で技術調査・設計・実装を進めています。

細かいところは省略しつつも、 cookpadLive システムの構成はだいたい以下のようになっています。

クックパッドには強力な開発基盤があり、さまざまな社内アプリケーションから利用できるように設計されています。
cookpadLive もその開発基盤の上で開発しており、特に認証基盤・決済基盤・DWH はその中でも重要な役割を担ってくれています。

次のセクションでは、AWS を使った特徴的な部分について紹介します。

cookpadLive のシステム的な特徴

以下の3つについて紹介します。

  1. Live 配信
  2. Live 配信中のメッセージ
  3. スペシャル TALK

1. Live 配信

Live 配信・アーカイブ生成などの映像を扱うために AWS メディアサービスを利用しており、その中の AWS Elemental MediaLive*2, AWS Elemental MediaStore*3, AWS Elemental MediaTailor*4 AWS Elemental MediaConvert*5 を利用しています。

ワークフロー

Live 配信は MediaLive -> MediaStore -> MediaTailor -> CloudFront のように構成しています。
MediaLive で配信スタジオからの RTMP Push を受け付けて、 MediaStore を destination に HLS 形式にエンコードしたものを出力させます。
また、CloudFront の手前に MediaTailor を挟んでおくことで、ゴールド会員限定のスペシャル TIME を実現できるようにしています。

アーカイブは MediaLive の Output にアーカイブ用のものを追加しており、 RTMP Push を受け付けた時に S3 を destination に MPEG2-TS 形式で出力し、Live 配信終了時にアーカイブ生成処理を実行します。
出力された ts ファイルは一度1つの ts ファイルに結合された後、 MediaConvert を使って HLS 形式にエンコードし、 CloudFront 経由で配信しています。

MediaTailor の部分が若干特殊になっているものの、一般的なワークフローで Live/アーカイブ配信を実現しています。

配信リソース管理のためのマイクロサービス

cookpadLive では配信リソースを管理するマイクロサービスを用意したのにはいくつか理由があり、その1つを紹介します。
※こちらの発表資料に詳しく記載しているので、合わせて読むと分かりやすいです。
cookpadLiveのライブ配信基盤 Cookpad Tech Kitchen #23

cookpadLive では、1配信1系統毎に MediaLive のインスタンスを立ち上げています。なので通常配信に加え、別カメラの映像を流すスタジオ観覧モードを追加するなど、1つの配信でも別系統の映像を配信したい場合は追加でインスタンスを立ち上げています。つまり、同じ時間帯に3つの配信あり、さらにそれぞれ通常・スタジオ観覧モードの2系統があった場合、6つの MediaLive のインスタンスが必要になります。また、それぞれの配信でリハーサル配信をする場合もあったりして、1日にいくつものインスタンスを必要とする場合があります。

MediaLive のインスタンスは作成してから数分〜数十分経過しないと利用可能にならないので、あらかじめ作成しておく必要があるのですが、作成タイミングによって MediaLive のインスタンス作成上限に引っかかったり、 Live 配信の開始時刻までに利用可能にならないリスクがあります。

リソース確保に伴うこれらの課題をマイクロサービスを用意して抽象化することで、 Live 配信に関するビジネスロジックの実装に集中できる環境を作りました。

2. Live 配信中のメッセージ

cookpadLive の開発では、コメント・ハート・スタンプなどのリアルタイムの情報をまとめてメッセージと呼んでいます。
Live 配信中にリアルタイムにサーバーからクライアントにメッセージを送信するために AWS AppSync を利用しています。

AppSync は GraphQL のインターフェースで AWS DynamoDB などのデータソースにアクセスすることができるサービスです。
GraphQL には3つのオペレーションがあり、クエリ・ミューテーション・サブスクリプションがあります。 cookpadLive のメッセージ配信は、 AppSync のサブスクリプションのオペレーションを使って実現しています。

クライアントが直接 AppSync を利用していないのにはいくつか理由があります。

まず1つ目は、送られたメッセージをそのままファンアウトさせるのではなく、アプリケーションサーバーである程度処理を加えたかったからです。
具体例を挙げると、なりすましが難しい形でユーザー属性をメッセージに付け加えたかったり、不適切なコメントをフィルタリングしたり、といった処理をしたかったからです。
AppSync の Resolver や datasource や Cognito などを工夫することで実現することも可能だとは思うのですが、システム全体の整合性を考えると現実的ではないと判断したので cookpadLive ではアプリケーションサーバーを経由して AppSync にミューテーションする方式を採用しました。

余談ですが、 AppSync はサーバーアプリケーションからオペレーションすることをあまり考慮しておらず、言語によっては SDK が用意されていません。なので cookpadLive では AppSync との通信部分を独自に実装しました。

2つ目の理由は AppSync への書き込み流量をサーバー側で制御したかったからです。
Live 配信は決まった時間にユーザーがいっきに集まりますし、配信の盛り上がりや企画次第ではハート・コメント・スタンプが連打され、リクエストのスパイクが起きやすいという性質があります。

AppSync に対するオペレーションの Rate limit も存在するのですが、例えばデータソースに DynamoDB を指定している場合はテーブルのキャパシティプランニングも合わせて必要になります。
サービスの成長や出演者の人気度の変化に柔軟に対応するためにも、アプリケーションサーバーを経由して AppSync に書き込みする構成にしています。

※こちらの発表資料に詳しく記載しているので、合わせて読むと分かりやすいです。
クックパッドの動画事業での AWS AppSync 活用事例

コメントの永続化

図をみると message サーバーから AppSync へ書き込むフローと、 S3 に書き込むフローにわかれている部分があるのですが、前者は Live 配信中に他ユーザーにファンアウトするためのフローで、後者はコメントデータの永続化のためのフローになっています。
シンプルに考えると、 AppSync のデータソースを DynamoDB にすればいいのではないか、と思われるかもしれないのですが、 負荷試験の結果 cookpadLive のコメントの流量で DynamoDB に書き込みをしようとすると期待するパフォーマンスがでないことがわかりました。
コメントデータはアーカイブの生成が完了したタイミングで準備ができていればよいので、パフォーマンスチューニングをするのではなく、多少遅延してもよいのでパフォーマンスを気にせず永続化できる仕組みを構築しました。(AppSync のデータソースは type: NONE を利用しています。)

※こちらの発表資料に詳しく記載しているので、合わせて読むと分かりやすいです。
アーカイブ配信でもライブ感を味わいたい / cookpad_tech_kitchen#23

余談ですが、この課題は AppSync のデータソースに Amazon Kinesis を指定できるようになると解決するので、是非 Kinesis をサポートしてほしいです。

3. Live 配信中の通話機能(スペシャル TALK)

スペシャル TALK は、Live 配信中にキャストと1対1で生電話ができる機能で、配信スタジオからは映像と音声の両方、ユーザーからは音声だけの通話ができます。
この機能を実現するために Amazon Chime と AWS AppSync を利用しています。

まずユーザーはスペシャル TALK への応募を行います。応募が揃ったら通話するそのユーザーに対して AppSync のサブスクリプションを使って、サーバーからアプリに対してイベントを送信します。アプリはこのイベントを受け取ると API を叩き Amazon Chime の接続情報を取得します。取得した接続情報をもとに Amazon Chime に接続し、接続が完了すると通話が開始される、というフローになります。

配信スタジオとの統合

システム構成ですがこの形に辿り着くまでに多くの議論を重ねました。
今まで開発したことないタイプの機能でしたし、既存の配信スタジオのマイク・スピーカー・カメラ・スイッチャーとどのように統合するのかであったり、 Live 配信の企画として成立させるためには通話以外にどういう機能が必要なのかを検討したりと、エンジニアチームだけではなく、撮影技術チーム・Live 配信ディレクターチームも巻き込んで今の形に仕上げました。

また、開発やデバッグにはとても苦労しました。 通話機能なので相手が居ないと正しく動いているかどうかが判断しづらく、離れたところで音楽を鳴らして擬似通話したり、ある程度完成したら複数人でデバッグしてみたりと、試行錯誤しながら開発をしました。

また、Amazon Chime は他の AWS リソースと違ってコンソールでリソースの操作ができず API でしか操作できなかったので、今どうなっているか、などの状態をパッと把握するのが難しいです。
この課題については現在も残っていて、チームメンバーと議論しながら解決に向けて進めている最中です。

余談ですが、結合テストについては、コロナ禍で全員リモートだったこともあり、実際の利用環境に近い形でテストすることができ、ユーザーの気持ちや問題に早く気づくことができました。

今後の予定

今回の記事では主にサーバーサイドの技術について触れました。cookpadLive では iOS・Android・FireTV のプラットフォームでアプリを展開しており、これらのアプリでも技術的な挑戦をしています。
アプリのチャレンジについては、次の機会で執筆しようと思いますので、乞うご期待ください。

cookpadLive では引き続き技術的なチャレンジをしていきます。
直近では API のパフォーマンス改善と、 JSON API を GraphQL に置き換えることを検討しています。そのうち情報発信していきますので、こちらも乞うご期待ください。

記事では書ききれなかったことも多く「もっと cookpadLive について知りたいよ」という方がいらっしゃいましたら、是非 @osadake212 までご連絡ください。

info.cookpad.com

参考記事

*1:ECS インフラの変遷 https://techlife.cookpad.com/entry/2021/08/05/114810

*2:ライブ動画処理サービスです。公式サイト: https://aws.amazon.com/jp/medialive/

*3:メディア向けに最適化された AWS ストレージサービスです。公式サイト: https://aws.amazon.com/jp/mediastore/

*4:動画ストリームにターゲット広告を個別に挿入できます。公式サイト https://aws.amazon.com/jp/mediatailor/

*5:ファイルベースの動画変換サービスです。 公式サイト: https://aws.amazon.com/jp/mediaconvert/

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/