記事のサマリー(TL;DR)
- OpenAIは週9億人超のアクティブユーザー向けに、WebRTCスタックを「リレー+トランシーバー」構成へ刷新
- 1セッション1ポートモデルをやめ、ICE ufragにルーティングメタデータを埋め込む手法で固定UDPポートを実現
- Cloudflare のジオステアリングと Global Relay 拠点の組み合わせで、接続初段の遅延を世界規模で低減
リアルタイム音声APIを利用する事業者が押さえるべきアーキテクチャの変化
OpenAIのRealtime APIを使って音声エージェントや対話システムを構築している国内開発者にとって、このインフラ刷新は接続品質と安定性に直接影響します。Global Relay の地理分散展開により、日本リージョン近傍のリレー拠点を経由した接続が可能になれば、国内ユーザー向けアプリケーションの往復遅延(RTT)とジッターが改善されます。また、WebRTCのクライアント実装は標準プロトコルのまま変更不要であるため、既存のRealtime API統合コードを改修せずに恩恵を受けられる点も重要です。Realtime APIを使ったコールセンター自動化・音声エージェント・多言語対応チャットボットを検討している場合、プロトコル互換性が保たれているため移行コストをかけずに性能向上が見込めます。
詳細
なぜ音声AIにはWebRTCが必要か
音声AIが自然に感じられるのは、会話が「発話の速度」で進む場合だけです。ネットワークが邪魔をすると、不自然な間、途切れ、割り込みの遅延として即座に知覚されます。これはChatGPTの音声機能、Realtime APIを使う開発者、インタラクティブなワークフローで動くエージェント、そしてユーザーが話している最中に音声処理が必要なモデルすべてに共通する課題です。
OpenAIのスケールでは、この課題は3つの具体的な要件に変換されます。
- グローバルなリーチ:週9億人超のアクティブユーザーへの対応
- 高速な接続確立:セッション開始と同時に発話できる状態の実現
- 低遅延・安定したメディアRTT:低ジッター・低パケットロスによるターンテイキングの自然さ
WebRTCはブラウザ・モバイルアプリ・サーバー間で低遅延の音声・映像・データを送受信するオープン標準です。P2P通話と結びつけられることが多いですが、クライアント—サーバー型のリアルタイムシステムにも実用的な基盤となります。具体的には以下を標準化しています。
- ICE(Interactive Connectivity Establishment): 接続確立とNATトラバーサル
- DTLS(Datagram Transport Layer Security)と SRTP(Secure Real-time Transport Protocol): 暗号化トランスポート
- コーデックネゴシエーション: 音声の圧縮・復号方式の合意
- RTCP(Real-time Transport Control Protocol): 品質制御
- エコーキャンセルやジッターバッファリングなどのクライアント側機能
音声AIにとって最も重要な特性は、音声が連続ストリームとして到着することです。これにより、ユーザーがまだ話している最中から文字起こし・推論・ツール呼び出し・音声生成を開始でき、「全発話を受け取ってから処理」するプッシュトゥトーク型との差を生み出します。
なお、WebRTCの基礎的な実装として、WebRTCの原設計者の一人であるJustin UbertiとPionの作成・メンテナーであるSean DuBoisの貢献が重要な役割を果たしており、両者は現在OpenAIの同僚として在籍しています。
SFUではなくトランシーバーモデルを選んだ理由
WebRTCのターミネーション方式として一般的な SFU(Selective Forwarding Unit) は、各参加者からWebRTCストリームを受け取り、他の参加者へ選択転送するメディアサーバーです。グループ通話・教室・共同会議など、もともとマルチパーティなプロダクトには適しています。
OpenAIのワークロードは異なります。セッションの大半は 1対1——ユーザー1人とモデル1つ、またはアプリケーション1つとリアルタイムエージェント1つ——で、すべてのターンで遅延感度が高い構成です。そこでOpenAIは トランシーバーモデル を採用しました。WebRTCエッジサービスがクライアント接続をターミネートし、メディアとイベントをモデル推論・文字起こし・音声生成・ツール使用・オーケストレーション向けのシンプルな内部プロトコルへ変換します。
このモデルでは、ICE接続チェック・DTLSハンドシェイク・SRTP暗号化キー・セッションライフサイクルといった すべてのWebRTCセッション状態はトランシーバーだけが保持 します。バックエンドサービスはWebRTCピアとして振る舞わず、通常のサービスとして独立してスケールできます。
Kubernetes上でのWebRTCが抱えていた問題
最初の実装は、Pionを基盤とした単一のGoサービスで、シグナリングとメディアターミネーションの両方を処理していました。ChatGPTの音声機能、Realtime APIのWebRTCエンドポイント、複数の研究プロジェクトを支えていました。
しかしKubernetesとの相性に問題がありました。従来の 1セッション1ポートモデル は以下の理由でKubernetes環境に不向きです。
- 高い同時接続数では、非常に広いUDPポート範囲の公開と管理が必要
- クラウドロードバランサーやKubernetesサービスは数万の公開UDPポートを前提に設計されていない
- ポート範囲が広いほど外部から到達可能な攻撃面が広がり、ネットワークポリシーの監査が困難になる
- Kubernetesではポッドが常に追加・削除・再スケジュールされるため、大きなポート範囲を予約・広告し続けるオートスケールが脆弱になる
1サーバー1ポート 設計はポート数の問題を解決しますが、フリート全体でセッションの所有権を維持するという第2の問題が生じます。ICEとDTLSはステートフルなプロトコルであり、セッションを作成したプロセスがそのセッションのパケットを受け続けなければ、DTLSハンドシェイクの完了・SRTP復号・ICEリスタートなどの処理が壊れます。
リレー+トランシーバーアーキテクチャの詳細
OpenAIが採用した解決策は、パケットルーティングとプロトコルターミネーションを分離することです。
| アプローチ | メリット | デメリット |
|---|---|---|
| セッション単位のIP:port(ネイティブUDP) | 直接のクライアント—サーバーパス | 1セッション1ポート、Kubernetesに不向き |
| サーバー単位のIP:port | 公開UDP面積が小さい | フリート全体のデマルチプレックスが困難 |
| TURNリレー | クライアントはリレーアドレスのみ到達すればよい | アロケーション設定のオーバーヘッド、移行困難 |
| ステートレスフォワーダー+ステートフルターミネーター(OpenAIの方式) | 小さな公開UDP面積、トランシーバーがWebRTCセッション全体を所有 | リレーとトランシーバー間のカスタム連携が必要 |
リレーは軽量なUDP転送レイヤーで、メディアの復号・ICEステートマシンの実行・コーデックネゴシエーションへの参加は行いません。パケットメタデータから転送先を決定し、セッションを所有するトランシーバーへパケットを届けます。クライアントから見ると、WebRTCセッションは何も変わりません。
ICE ufrag によるルーティング
このアーキテクチャの鍵となるのが ICE userfragment(ufrag) を使ったファーストパケットルーティングです。
WebRTCのすべてのセッションには、セッション確立時にSTUNコネクティビティチェックに含まれるufragという短い識別子が存在します。OpenAIはサーバー側のufragを生成する際、そこに宛先クラスターとオーナートランシーバーを特定できるルーティングメタデータを埋め込みます。
シグナリング時にトランシーバーはセッション状態をアロケートし、SDPアンサーに共有リレーVIP(Virtual IP)とUDPポートを返します。これによりクライアントは、多数のリレーインスタンスが背後に存在していても 203.0.113.10:3478 のような単一の安定した宛先を持ちます。
リレーは最初のSTUNパケットからufragを読み取り、ルーティングヒントをデコードし、オーナートランシーバーへ転送します。各トランシーバーは共有UDPソケット(1セッション1ソケットではなく内部IP:portにバインドされた1つのOSエンドポイント)でリッスンしています。セッション確立後のDTLS・RTP・RTCPパケットはufragの再デコードなしにセッション内を流れます。
リレーが再起動してセッションを失っても、次のSTUNパケットがufragのルーティングヒントからセッションを再構築します。さらに信頼性を高めるため、<クライアントIP+Port, トランシーバーIP+Port> のマッピングを Redisキャッシュ に保持し、次のSTUNパケット到達前に早期復元できるようにしています。
Global Relay とジオステアリング
公開UDPを少数の安定したアドレス・ポートに集約したことで、同じリレーパターンをグローバルに展開できるようになりました。Global Relay は地理的に分散したリレーイングレス拠点のフリートで、すべて同じパケット転送ロジックを実装しています。
シグナリングには Cloudflare のジオ・近接ステアリング を使用し、最初のHTTPまたはWebSocketリクエストが近くのトランシーバークラスターへ到達するようにしています。SDPアンサーがGlobal Relayアドレスを提供し、ufragがGlobal Relayからターゲットクラスターへのメディアルーティングとトランシーバーへの転送に必要な情報を持ちます。
ジオステアリングとGlobal Relayの組み合わせにより、セットアップとメディアの両方をユーザー近傍のエントリーポスに乗せながら、セッションを1つのトランシーバーに固定できます。
リレーの実装と性能最適化
リレーサービスはGoで実装し、意図的に実装範囲を絞っています。主な設計判断は以下のとおりです。
- プロトコルターミネーションなし: STUNヘッダー・ufragのみを解析し、後続のDTLS・RTP・RTCPはキャッシュ済み状態を使用。パケットは不透明なまま転送
- エフェメラルな状態: クライアントアドレスからトランシーバー宛先へのマッピングを小さく短タイムアウトなインメモリマップで保持
- 水平スケーラビリティ: ロードバランサー背後で複数のリレーインスタンスが並列動作。状態はハードなWebRTC状態ではないため、再起動時のトラフィックロスを最小化
効率化の主な施策:
SO_REUSEPORT: 同一マシン上の複数リレーワーカーが同じUDPポートにバインドできるLinuxソケットオプション。カーネルが受信パケットをワーカーへ分散し、シングルリードループのボトルネックを回避runtime.LockOSThread: UDPを読み込むgoroutineを特定のOSスレッドに固定。SO_REUSEPORTと組み合わせることで、同一フローのパケットが同じCPUコアに留まりやすくなり、キャッシュ局所性が向上してコンテキストスイッチを削減- 事前アロケーションバッファと最小コピー: Goのガベージコレクションを避けるため、パースと割り当てのオーバーヘッドを低く抑える
この実装はカーネルバイパス(ユーザースペースプロセスがネットワークキューを直接ポーリングする手法)を使わずに、グローバルなリアルタイムメディアトラフィックを比較的小さなリレーフットプリントで処理できました。
設計から得られた教訓
このアーキテクチャにより、OpenAIは数千のUDPポートを公開することなくKubernetes上でWebRTCメディアを運用できるようになりました。主な成果と設計方針のまとめは以下のとおりです。
- エッジではプロトコルセマンティクスを維持する: クライアントは引き続き標準WebRTCを使用し、ブラウザとモバイルの相互運用性を保持
- ハードなセッション状態は1か所に集中させる: ICE・DTLS・SRTP・セッションライフサイクルはトランシーバーが所有し、リレーはパケット転送のみ
- セットアップ時にすでに存在する情報でルーティングする: ICE ufragがファーストパケットルーティングのフックを提供し、ホットパスでの外部ルックアップへの依存をなくす
- カーネルバイパスへ飛びつく前に一般ケースを最適化する:
SO_REUSEPORT・スレッドピニング・低割り当てパースを丁寧に使った素直なGo実装で十分なスループットを確保
より広い教訓として、複雑さを加える最善の場所は薄いルーティング層であり、すべてのバックエンドサービスでも、カスタムクライアント動作でもありません。プロトコルネイティブなフィールドにルーティングメタデータをエンコードしたことで、決定論的なファーストパケットルーティング・小さな公開UDPフットプリント・ユーザー近傍のグローバルイングレス配置の柔軟性、という3つを同時に実現しました。