INIAD meets webを支えたシステム 〜インフラ編〜

Kubernetes

この記事は、↓こちら↓の続きになります。

もしまだ見てない方はぜひそちらからご覧ください!

インフラの全体構成

INIAD meets webの本番環境はGoogle Cloud Platform上に構築しています。
最近パリのDCで火災が発生して、結構大騒ぎになっていましたね。

GCPは、マネージドKubernetesクラスタが1クラスタ目まで無料(AWSでは1クラスタ目から管理手数料が発生)であることや、管理すべきインフラのシンプルさ、INIADで現用されているということで、全体的に親和性が高いための採用です。

というわけで、基本的なアプリケーションも全てGoogle Kubernetes Engine上で構築しているほか、INIAD meets webに関するドメインはCloud DNS上で管理されています。

なお、GCPは料金が高額になりがちなため、アーカイブ配信用のサイトは無料枠が大きいOracle Cloud Infrastructure上で構築中です。
アーカイブ配信はもうちょっと待っててね。

アプリケーションのデプロイ環境

前述の通り、基本的なアプリケーション類は全てGKE上に構築しています。

emapiやwsapiだけでなく、フロントエンド(参加団体向け、放送管理・クイズやアンケート管理、一般視聴者向け)のコンテナや、Pub/Subに使うRedisも含めてデプロイされています。

開発の流れ

また、production環境だけでなく、staging環境も用意されており、開発者はmeets webのstagingブランチにpushするだけで、各種システムの動作確認を行うことができます。

staging環境での動作確認を経て、mainブランチにmergeされたものがproduction環境にデプロイされて、一般視聴者や参加団体に使われるようになります。

Kubernetesですので、デプロイはローリングアップデートで行われます。
つまり、新しいバージョンのコンテナが起動したのを確認してから、古いバージョンのコンテナを落とすことで、サービスを止めずにアップデートを行うことができます。

アンケートの結果表示で、最初の1回だけ小数点の切り捨てをすっかり忘れてしまっており、頭を抱えたのですが、ローリングアップデートのおかげでサービスダウンをせずに次の結果表示では無事修正バージョンが表示されました。

アプリケーションの相互通信

GKE内にデプロイされたコンテナはこのような構成になっています。

強いて特筆すべきは、gRPC通信でしょうか
wsapiからemapiへのデータ送信は、ClusterIP Serviceを介して行われます。

gRPCは認証なし(厳密には、WebSocket Sessionに含まれたユーザー情報を一緒に渡している)に呼び出されるため、クラスタ外部へ公開しない形で通信を行なっています。

また、API編でも触れた通り、emapiとwsapiはそれぞれ別のPodとして動いており、それぞれがスケールすることで、より多くのユーザーからの接続を受けるwsapiのみをスケールアウトしたりといった柔軟なスケール・負荷分散が行えるようになっています。

こちらもAPI編で触れていますが、Pub/SubのためにRedisを用いており、こちらもClusterIP Serviceを介してemapiやwsapiから接続できるようになっています。

なお、Podがダウンすると情報が揮発してしまうため、本来であればRedisもPersistent Volumeを用いて永続化したいところですが、手間と揮発による問題の2つを考慮した上で永続化していませんでした。

映像配信のインフラ

映像配信にはHLS(HTTP Live Streaming)を使用しています。
これは、1つの映像を2〜10秒程度の「セグメント」と呼ばれる映像ファイルに分割し、そのURLをプレイリストファイルに書き込むことで、映像を視聴できるようにするものです。

これにより、回線の状態が悪化した際に、それ以降は「画質が悪く、ファイルサイズが軽いセグメントファイル」を再生するなど、柔軟な画質変更が行えるようになるメリットがあります。

映像はOBSやATEMなどからRTMPで配信するため、RTMPで入力した映像をHLS形式に変換する必要があるのですが、ここをセルフホストすると、スペックの高いVMを用意するなどコストが嵩むほか、ディスクの読み書き速度や、(OBSやATEMで行う)RTMPの送出の設定によって、配信の品質が著しく変動してしまうことが懸念点となっていました。

例えば、2020年のINIAD-FESでは、VM上で変換処理を行っていたところ、ATEMから配信した映像がよく止まってしまうなどの不具合がありました。

この経験を踏まえて、昨年登場した「Google Live Stream API」を使用することにしました。
これは、RTMPで送出した映像をHLSに変換した上で、Google Cloud Storageに保存してくれる機能です。
宛先がCloud Storageなので、そのまま公開で配信することもできますし、Cloud CDNとの相性も抜群です。

何よりも、変換処理をGoogleが全て面倒見てくれて、画質も指定したものに自動で変換をしてくれることから、運用コストが段違いで下がることになります。
一方で、WebUIが存在しないのがネックで、今回はcURLで叩きましたが、放送管理APIから管理できるようにしたい野望もあります。

映像配信の機密性

映像配信はGoogle Cloud Storageから行われます。
しかし、Google Cloud Storageの認証・認可はIAMベースであり、それ以外の権限は基本的にインターネット上に全公開 or 非公開となります。

一方で、INIAD meets webは学内限定コンテンツです。
サイト自体はログインを必須化していますが、何らかの理由で配信URLが流出してしまうと、映像が第三者に流出してしまう可能性があり、権利関係の問題を孕んでいます。
また、セグメント化されて軽量化しているとはいえ、映像ファイルが第三者にダウンロードされると、想定以上の転送料金(いわゆる「データ通信料」)がかかることになります。

そのため、Google Cloud Storageには「署名付きURL」と「署名付きCookie」の2つのアクセス制限方法が用意されています。

署名付きURL

Cloud Storageの特定のファイルのURLに対して、予め発行した鍵を使った署名を付けることで、一時的に非公開のファイルをダウンロードできる機能です。

具体的には、このような形式になります。

https://storage.googleapis.com/example-bucket/cat.jpeg?X-Goog-Algorithm=
GOOG4-RSA-SHA256&X-Goog-Credential=example%40example-project.iam.gserviceaccount
.com%2F20181026%2Fus-central1%2Fstorage%2Fgoog4_request&X-Goog-Date=20181026T18
1309Z&X-Goog-Expires=900&X-Goog-SignedHeaders=host&X-Goog-Signature=247a2aa45f16
9edf4d187d54e7cc46e4731b1e6273242c4f4c39a1d2507a0e58706e25e3a85a7dbb891d62afa849
6def8e260c1db863d9ace85ff0a184b894b117fe46d1225c82f2aa19efd52cf21d3e2022b3b868dc
c1aca2741951ed5bf3bb25a34f5e9316a2841e8ff4c530b22ceaa1c5ce09c7cbb5732631510c2058
0e61723f5594de3aea497f195456a2ff2bdd0d13bad47289d8611b6f9cfeef0c46c91a455b94e90a
66924f722292d21e24d31dcfb38ce0c0f353ffa5a9756fc2a9f2b40bc2113206a81e324fc4fd6823
a29163fa845c8ae7eca1fcf6e5bb48b3200983c56c5ca81fffb151cca7402beddfc4a76b13344703
2ea7abedc098d2eb14a7

一方で、HLSはプレイリストファイルに書いてある、大量のセグメントファイルのURLを参照する形になっています。

そのため、プレイリストファイルに対する署名は付けられますが、セグメントファイル1つ1つに署名を行うのは現実的ではありません。

そこで、登場するのが署名付きCookieです。

署名付きCookie

特定のディレクトリのパスについて署名し、Cookieとして付与しておくと、そのディレクトリの子孫に対してアクセスが許可されます。

例えば以下のディレクトリ構成であったとします。

/videos
├ /foobar
│   ├ manifest.m3u8
│   ├ video001.ts
│   ├ video002.ts
│   ├ video003.ts
│   ├ video004.ts
│   └ video005.ts
├ /foobar2
│   ├ manifest.m3u8
│   ├ video001.ts
│   ├ video002.ts
│   ├ video003.ts
│   ├ video004.ts
│   └ video005.ts
・
・
・

/videos/foobarに対する署名を発行すると、manifest.m3u8だけでなくvideo001.tsやvideo002.tsにもアクセスが可能になります。
一方で、/videos/foobar2以下にはアクセスできません。

また、Cookieで署名を送るため、URLは変更する必要がありません。

署名付きCookieを使用するためには、CloudCDNとGoogle Cloud Load Balancerの利用が必須となります。(署名付きURLはCloud Storageの機能ですが、署名付きCookieはCloud CDNの機能です)

meets webでは、サイトへのログイン時に、ログインの有効期限と同じ有効期限の署名を付与しています。

これにより、サイトにログインしていないユーザーは映像を観ることができず、サイトにログインすると自動で映像も観ることができるようになります。

まとめ

Googleのインフラを最大限活用した上で、スケーラブルなシステムを構築することができ、今回のINIAD meets webで最も重視していた「安定」に挑戦することができました。

でも、ここまでガチった割に視聴者少なかったかもしれません、悔しい定期

余談 〜配信の遅延〜

アンケートの結果発表のとき、40秒の「集計タイム」なるものが存在していたのですが覚えているでしょうか。

実は、集計自体はリアルタイムで行われていて、すぐに結果表示画面を出すことはできました。

HLSは、その特性上遅延があり、収録会場のホールで司会者が「回答を締め切ります!」と言ってから視聴者の画面に伝わるまでに概ね30〜40秒の遅延が発生します。

しかし、WebSocketの状態変更はすぐに配信されるため、「回答を締め切ります!」と言ってから30秒程度待った上で回答を締め切る必要があり、そのために集計タイムという時間稼ぎをしていたのでした。

また、今紹介している団体の情報の切り替えも手動で行われているのですが、こちらもHLSの遅延を考慮して、ホール内でそのコンテンツが始まってから30秒程度待機した上で状態変更をしています。

最近では、遅延が小さく抑えられるLL-HLS(Low Latency HLS)の開発・導入が進んでいるのですが、Google Live Stream APIでは非対応のようで、来年の時には対応していると嬉しいですね。

タイトルとURLをコピーしました