INFRA

LiveKit + Egress を Windows の Docker Desktop でローカル開発したときの詰まり所

ブラウザから複数人ライブ配信(LiveKit + Egress + メディアサーバ)を Windows の Docker Desktop でローカルに立てたら何ヶ所か詰まった。症状→原因→対処でまとめたメモ。一番効いたのは --node-ip。

ブラウザから複数人がライブ配信するやつを作るのに、LiveKit(WebRTC の SFU)と LiveKit Egress(部屋を1枚に合成して配信するやつ)をローカルの Docker で動かそうとしたら、Windows の Docker Desktop 特有のところで何ヶ所か詰まった。同じ構成でハマる人向けに、症状 → 原因 → 対処でメモしておく。

結論(先に)

  • 一番効いたのは LiveKit に --node-ip=ホストのLAN IP を広告させること。これが無いとホストのブラウザから WebRTC がつながらない。
  • LiveKit と Egress は 同じ Redis を共有しないとジョブが渡らない。
  • 設定ファイルを直したら docker compose restart(up -d だとマウント中身の変更を拾わない)。
  • Egress の RTMP 出力先は rtmp://host/{app}/{stream_key}(2セグメント) 必須。
  • Egress の合成は 部屋にトラックが無いと始まらない → 検証は Ingress + ffmpeg で。

作ろうとした構成

ブラウザ(配信者/ゲスト) ──WebRTC──▶ LiveKit(SFU)
└─ Egress が部屋を1枚に合成
└─RTMP──▶ メディアサーバ(MediaMTX)
└─HLS──▶ 視聴者(大量)

ステージに乗る少人数だけ WebRTC で LiveKit に入り、合成した1本を HLS にして視聴者は普通の HLS プレイヤーで見る(視聴者は SFU に入れない)。これを全部 Docker(LiveKit / Egress / メディアサーバ / Redis)でローカルに立てたかった。


詰まり① ホストのブラウザから WebRTC がつながらない(最重要)

症状:ブラウザから部屋に入ろうとすると could not establish pc connection。signaling(WebSocket)は通るのに、映像回線(WebRTC の PeerConnection)が張れない。

原因:LiveKit はデフォルトで、自分の到達先(ICE 候補)として Docker コンテナ内部の IP(192.168.x.x みたいなの) を広告する。コンテナ同士なら届くが、ホストで動いているブラウザからはこの内部 IP に到達できない。

対処:LiveKit に ホストの LAN IP を広告させる。起動オプションに --node-ip を付ける。

# docker-compose.yml(LiveKit サービス)
command: --config /etc/livekit.yaml --node-ip ${LIVEKIT_NODE_IP}
# .env(このマシンの LAN IP。DHCP なら変わるので注意)
# LIVEKIT_NODE_IP=192.168.x.x

こうすると ホストのブラウザも、他のコンテナ(Egress 等)も同じアドレスで LiveKit に届く。 ※本番(Linux サーバ)ではサーバの公開 IP、もしくは use_external_ip: true(STUN で自動検出)を使う。

一般化した教訓:ローカル Docker で WebRTC 系が「ホストのブラウザからつながらない」ときは、まず「広告している IP がホストから届くか」を疑う。signaling が通って映像だけダメなのは、ほぼ ICE 候補(IP)の問題。


詰まり② Egress がジョブを受け取らずハングする

症状:Egress を開始する API を叩くと応答が返らずタイムアウト。Egress のログは起動直後で止まったまま。

原因:LiveKit と Egress は Redis 経由でジョブを受け渡す。LiveKit 側に Redis 設定が無いと、Egress にジョブを配れずに待ち続ける(egress not connected (redis required))。

対処:LiveKit と Egress の両方に同じ Redis を向ける。

# LiveKit の config
redis:
address: redis:6379
# Egress の config も同じ Redis

詰まり③ 設定ファイルを直しても反映されない

症状:マウントした config(livekit.yaml など)を書き換えて docker compose up -d しても、古い設定のまま動く。

原因:docker compose up -d はサービス定義(compose ファイル)の差分でしか作り直さない。マウント先のファイル中身が変わっても検知しないので、コンテナは起動時に読んだ古い設定のまま。

対処:docker compose restart <service>(or up -d --force-recreate)で作り直して再読込させる。地味だが数十分溶かした。


詰まり④ Egress の RTMP 出力先 URL は「2セグメント」必須

症状:Egress の合成出力を RTMP でメディアサーバに送ろうとすると rtmp urls must be of format rtmp(s)://{host}(/{path})/{app}/{stream_key} で弾かれる。

原因:Egress は RTMP 出力先に rtmp://host/{app}/{stream_key}(ホストの後にパスが2つ) を要求する。rtmp://host/streamkey(1つ)だとダメ。

対処:メディアサーバ側のパスを2セグメントに合わせる。例えば MediaMTX なら「アプリ名 live + ストリームキー」で rtmp://mediamtx:1935/live/{id} の形にする。ついでに配信ソフト(OBS 等)からの入力(サーバ=rtmp://host/live, キー={id})と出力を同じパス=同じ HLS に寄せられて都合がいい。


詰まり⑤ 空の部屋だと Egress の合成が始まらない

症状:Egress を開始しても EGRESS_STARTING のまま。合成用の Chrome(ヘッドレス)は起動しているのに、いつまでも配信が始まらない(エラーも出ない)。

原因:部屋に映像トラックが1つも無い(誰も publish していない)と、合成する中身が無くて先に進まない。カメラの無いヘッドレス環境で検証しようとすると、この状態になる。

対処(カメラ無しで検証する方法):LiveKit Ingress を立て、ffmpeg のテスト映像を RTMP で流し込んで部屋に「参加者トラック」として publish する。これで合成 → HLS までヘッドレスで全ループを確認できる。

Terminal window
ffmpeg -re -f lavfi -i testsrc=size=640x480:rate=15 \
-f lavfi -i sine=frequency=800 \
-c:v libx264 -preset veryfast -tune zerolatency -pix_fmt yuv420p \
-c:a aac -f flv rtmp://localhost:<ingress_rtmp_port>/x/<stream_key>
  • Ingress の RTMP ポートは、メディアサーバの RTMP(1935)と衝突しないよう別ポートにマップする(例: ホスト側 1936)。
  • 実トラックが入ると Egress は EGRESS_ACTIVE になり、合成された HLS セグメントが出てくる。実際、テスト映像を流したら 10 秒ほどで HLS の .ts セグメント(数MB の実データ)が取れた。

まとめ

詰まり原因対処
① ブラウザから WebRTC 不通LiveKit がコンテナ内 IP を広告--node-ip=ホストLAN IP を広告
② Egress がハングLiveKit–Egress 間の Redis 未共有両方に同じ Redis
③ 設定が反映されないup -d はマウント中身の変更を見ないrestart で再読込
④ RTMP 出力が弾かれる2セグメント(/app/key)必須メディアサーバのパスを合わせる
⑤ 合成が始まらない部屋にトラックが無いIngress + ffmpeg で実トラック投入

一番効いたのは ①(--node-ip)。ローカル Docker で WebRTC が絡むときは「広告している IP がホストから届くか」を最初に確認するのが近道だった。②〜⑤も合わせると、Windows の Docker Desktop でもブラウザ実機なしで多人数ライブ配信の全ループを検証できるようになる。

(本番は Linux サーバ上での運用が前提。上記はローカル開発環境を成立させるための工夫。)