NetflixのVODは解決済みの問題だ。コンテンツは事前にエンコードされ、エッジサーバーにキャッシュされ、最も近いノードから配信される。再生を押せば、動く。
ライブストリーミングはまったく別の獣だ。すべてのビデオセグメントが数秒以内にエンコード、パッケージング、配信されなければならない。「事前に」はない。「今すぐ」だけだ。
Netflixがタイソン vs ポール戦(6500万同時ストリーム)のようなイベントでライブストリーミングを導入したとき、クラウドエンコーディングパイプラインとCDNであるOpen Connectの間にLive Originというカスタムシステムを構築した。その仕組みを見ていこう。
Live Originは Amazon EC2インスタンス上のマルチテナントマイクロサービスとして動作する。通信モデルはシンプルだ:
エンコーダー → Packager → [HTTP PUT] → Live Origin → [HTTP GET] → Open Connect → 視聴者
2つのアーキテクチャ上の決断がすべてを形作った:
冗長パイプライン。 Netflixは2つの独立したエンコーディングパイプラインを同時に実行する。それぞれ異なるクラウドリージョンで、独自のエンコーダー、Packager、ビデオフィードを持つ。一方が悪いセグメントを生成しても、もう一方は通常良いものを生成する。
予測可能なセグメントテンプレート。 利用可能なセグメントをリストするマニフェストを常に更新する代わりに、各セグメントの長さは2秒固定。Originは各セグメントがいつ公開されるべきか正確に予測できる。
ライブビデオフィードは必然的に欠陥を生む — フレーム欠落のある短いセグメント、完全に欠落したセグメント、タイミングの不連続性。異なるリージョンの2つの独立したパイプラインは、両方が同時に欠陥セグメントを生成する確率を大幅に減らす。
Open Connectがセグメントをリクエストすると、Originは:
パイプラインA (us-east-1): segment_1042 → ✓ 有効
パイプラインB (us-west-2): segment_1042 → ✗ フレーム欠落
Originの判断:パイプラインAのセグメントを配信
まれに両方が欠陥の場合、欠陥メタデータが下流に渡され、クライアントがエラーを適切に処理できる。サイレントな破損はない。
Open ConnectはVOD向けに構築された — 事前配置されたコンテンツに対するnginxのチューニングに何年も費やした。ライブストリーミングはそのモデルに合わなかったため、Netflixはnginxのプロキシキャッシュ機能をいくつかの最適化で拡張した:
無効なリクエストを早期に拒否。 Open ConnectノードはセグメントテンプレートをOpen Connectノードは知っている。正当な範囲外のセグメントへのリクエストはOriginに到達せず即座に拒否される。
404をインテリジェントにキャッシュ。 セグメントがまだ利用できないとき、Originは有効期限ポリシー付きの404を返す。Open Connectはセグメントが期待される直前までこの404をキャッシュし、繰り返しの失敗リクエストを防ぐ。
ライブエッジでリクエストを保持。 次に公開されるセグメントへのリクエストが到着したとき、クライアントまで伝搬する404を返す代わりに、Originはリクエストをオープンに保持する。セグメントが公開されたら、即座に応答する。少し早く到着したリクエストの完全なラウンドトリップを排除する。
ミリ秒精度のキャッシュ。 標準のHTTP Cache-Controlは秒単位の粒度 — セグメントが2秒ごとに生成される場合は粗すぎる。Netflixはnginxにミリ秒精度のキャッシュを追加した。
Netflixはカスタムヘッダーを使ってライブストリーミングイベントを大規模にブロードキャストする。エンコーディングパイプラインがOriginに通知を送り、Originがそれ以降のセグメントにヘッダーとして挿入する。これらのヘッダーは累積的 — 以降のセグメントに持続する。
セグメントがOpen Connectノードに到着すると、通知がレスポンスヘッダーから抽出されメモリに保存される。クライアントに配信する際、視聴者がストリームのどの位置にいても最新の通知データが添付される。
広告ブレイク、コンテンツ警告、ライブイベントの更新を、再生位置に関係なく数百万のデバイスに効率的に伝達する。別の通知チャネルは不要だ。
Netflixは当初、VODインフラと同様にAWS S3を使用していた。低トラフィックのイベントでは機能した。そしてライブストリーミング固有の要件を発見した:
S3は稼働率保証を満たしていたが、厳密なタイミングバジェットではあらゆる遅延が致命的だった。要件は、オブジェクトストレージよりもグローバル、低レイテンシ、高可用性データベースに近かった。
NetflixはApache Cassandraを使用したKey-Valueストレージ抽象化の上に構築した:
結果:中央値レイテンシが113msから25msに低下。P99レイテンシが267msから129msに改善。
しかしOrigin Storm問題が残っていた — 数十のトップティアキャッシュが同時に大きなセグメントをリクエストする。最悪の場合の読み取りスループットは100+ Gbpsに達する可能性がある。同時読み取りが書き込みパフォーマンスを許容できないほど劣化させた。
解決策:EVCacheによるライトスルーキャッシュ(Memcachedベースのnetflixの分散キャッシュレイヤー)。ほぼすべての読み取りが200+ Gbpsのスループットでキャッシュから提供され、書き込みパスに影響しない。キャッシュミスだけがCassandraにヒットする。
書き込みパス: Packager → Origin → KeyValue → Cassandra(+ EVCacheライトスルー)
読み取りパス: Open Connect → Origin → EVCache(ヒット) → レスポンス
→ Cassandra(ミス) → EVCache → レスポンス
すべてのリクエストが同じ重要度ではない:
Netflixは完全なパス分離を実装した:
ストレス時には、優先度ベースのレート制限が作動する。ライブエッジトラフィックがDVRトラフィックより優先される。検出にはメモリにキャッシュされた予測可能なセグメントテンプレートを使用 — データストアへのアクセスは不要で、これはデータストアがストレス下にあるときに特に価値がある。
トラフィックサージには、低優先度トラフィックが影響を受けるとき、Originがmax-age=5を設定してHTTP 503を返し、Open Connectに5秒間拒否をキャッシュするよう指示する。手動介入なしにトラフィックストームを抑制する。
Netflix Live Originが成功するのは、ライブストリーミングの根本的な制約を尊重しているからだ:時間がバジェット。 すべての設計決定 — 冗長パイプライン、予測テンプレート、保持リクエスト、ライトスルーキャッシュ、優先レート制限 — はエンコーディングと配信の間の2秒のウィンドウを守るために存在する。
タイソン vs ポール戦での6500万同時ストリーム時、システムは持ちこたえた。賢かったからではなく、障害モードを中心に意図的に設計されていたからだ。
2秒のバジェットのために設計しろ。残りはすべてそこから導かれる。
— blanho