
Audio Video Testing(webRTCおよびUDP)向けのChaos Monkey
ビデオ会議システムの負荷テストのための分散型カオスエンジニアリングプラットフォーム。H.264/Opusストリームで1500以上のWebRTC参加者をシミュレートし、ネットワークカオススパイクを注入して、劣化条件下でのシステムの回復力を検証します。
メディア処理パイプライン:
コントロールプレーン:
参加者プール:
participant_id % total_partitions = partition_idKubernetes自動構成:
orchestrator-3 → PARTITION_ID=3base_port + (partition_id × 10000) + participant_indexUDPリレーチェーン(Kubernetesのみ):
オーケストレーターポッド (10×) → UDP :5000 → udp-relay ポッド (Python)
→ 長さプレフィックス付きTCP :5001 → kubectl port-forward 15001:5001
→ tools/udp-relay (Go) → UDP :5002 → 受信側
WebRTCインフラ:
クライアント統合:
可観測性スタック(オプション):
/metricsエンドポイントを5秒ごとにスクレイピングprometheus.io/scrape: "true"各仮想参加者が実際のメディアストリームを生成:
5種類のスパイクが実際のネットワーク状態をシミュレート:
スパイクは設定可能な戦略を使用してテスト期間全体に分散:
Kubernetesデプロイメントでは、参加者のパーティショニングによって水平スケーリングを実現:
participant_id % total_partitions == partition_id を処理base_port + (partition_id * 10000) + participant_index最適: 開発、デバッグ、小規模テスト(1~100参加者)
# オーケストレーターを起動
go run cmd/main.go
# 別のターミナルで: UDPレシーバーを起動
go run examples/go/udp_receiver.go 5002
# config/config.jsonを編集して num_participants: 10 を設定
# カオステストを実行
go run tools/chaos-test/main.go -config config/config.json
実行の流れ:
:8080 で動作127.0.0.1:5002 に送信設定(config/config.json):
{
"base_url": "http://localhost:8080",
"media_path": "public/rick-roll.mp4",
"num_participants": 10,
"duration_seconds": 300,
"spikes": {
"count": 20,
"interval_seconds": 5,
"types": { "rtp_packet_loss": {...}, "network_jitter": {...} }
},
"spike_distribution": {
"strategy": "random",
"min_spacing_seconds": 5,
"jitter_percent": 15
}
}
最適: 隔離されたテスト、CI/CD、中規模テスト(100~500参加者)
前提条件:
docker-compose がインストールされていること# オーケストレーターコンテナをビルドして起動
./scripts/start_everything.sh build
# 別のターミナルで: UDPレシーバーを起動
go run examples/go/udp_receiver.go 5002
# config/config.jsonを編集して num_participants: 100 を設定
# カオステストを実行(コンテナを対象)
go run tools/chaos-test/main.go -config config/config.json
リソース制限(docker-compose.yaml を編集):
services:
orchestrator:
deploy:
resources:
limits:
cpus: "14.0"
memory: 6G # 参加者を増やすには増やす
スケーリングガイド:
| Dockerメモリ | 最大参加者数 | CPUコア数 |
|---|---|---|
| 8 GB | ~100 | 4 |
| 16 GB | ~250 | 8 |
| 24 GB | ~400 | 12 |
| 32 GB | ~500 | 14 |
最適: 大規模テスト(500~1500参加者)、水平スケーリング、本番検証
前提条件:
# Nixが提供: Go、Docker、kubectl、kind、ffmpeg
nix develop
# またはdirenvを使用して自動アクティベーション
echo "use flake" > .envrc
direnv allow
# 最適な設定で自動デプロイ(システムリソースを検出)
./scripts/start_everything.sh run -config config/config.json
# またはカスタムメディアファイルを指定
./scripts/start_everything.sh run --media=path/to/video.mp4 -config config/config.json
実行の流れ:
kubectl port-forward を設定オプションA: UDPレシーバー(Kubernetes推奨)
# 1500人の全参加者からの集約ストリームを受信
go run ./examples/go/udp_receiver.go 5002
オプションB: WebRTCレシーバー(複数参加者)
# WebRTCを介して最大150参加者に接続
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id> 150
アーキテクチャの流れ:
10ポッドに分散した1500参加者
→ 各ポッド: 150参加者
→ participant_id % 10 でパーティション分割
→ 全員がUDPをudp-relay:5000に送信
→ UDPリレーが集約 → TCP :5001
→ kubectl port-forward 15001:5001
→ ローカルリレーがTCPをUDP :5002に変換
→ 受信側が1500ストリームすべてを受信
注意: start_everything.sh スクリプトが自動的に以下を設定:
# イメージのビルドとロード
docker build -t chaos-monkey-orchestrator:latest .
kind load docker-image chaos-monkey-orchestrator:latest
# デプロイ
kubectl apply -f k8s/orchestrator/orchestrator.yaml
kubectl apply -f k8s/udp-relay/udp-relay.yaml
# ポッドが準備できるまで待機
kubectl wait --for=condition=ready pod -l app=orchestrator --timeout=300s
# UDPリレーのポートフォワード
kubectl port-forward udp-relay 15001:5001 &
# ローカルTCP→UDPリレーを起動
go run tools/udp-relay/main.go &
# 別のターミナルで: レシーバーを起動
go run ./examples/go/udp_receiver.go 5002
# 別のターミナルで: カオステストを実行
go run tools/chaos-test/main.go -config config/config.json
# Kubernetesリソースを削除
./scripts/cleanup.sh
# またはクラスター全体を削除
kind delete cluster --name av-chaos-monkey
# Linux x86_64向けにビルド(最も一般的)
nix build .#packages.x86_64-linux.av-chaos-monkey
# ARM64向けにビルド(Raspberry Pi、AWS Graviton)
nix build .#packages.aarch64-linux.av-chaos-monkey
# macOS Intel向けにビルド
nix build .#packages.x86_64-darwin.av-chaos-monkey
# macOS Apple Silicon向けにビルド
nix build .#packages.aarch64-darwin.av-chaos-monkey
# バイナリの場所
./result/bin/main
# テスト作成
POST /api/v1/test/create
{
"test_id": "optional_id",
"num_participants": 100,
"video": {...},
"audio": {...},
"duration_seconds": 600,
"spikes": [...],
"spike_distribution": {
"strategy": "even",
"min_spacing_seconds": 5,
"jitter_percent": 15
}
}
# テスト開始
POST /api/v1/test/{test_id}/start
# メトリクス取得
GET /api/v1/test/{test_id}/metrics
# テスト停止
POST /api/v1/test/{test_id}/stop
# SDPオファーを取得
GET /api/v1/test/{test_id}/sdp/{participant_id}
# SDPアンサーを設定
POST /api/v1/test/{test_id}/sdp/{participant_id}
{"sdp_answer": "v=0..."}
# スパイク注入
POST /api/v1/test/{test_id}/spike
{
"spike_id": "unique_id",
"type": "rtp_packet_loss",
"duration_seconds": 30,
"participant_ids": [1001, 1002],
"params": {"loss_percentage": "15"}
}
| 種類 | パラメータ | 効果 |
|---|---|---|
rtp_packet_loss | loss_percentage (0-100) | RTP層でパケットをドロップ |
network_jitter | base_latency_ms, jitter_std_dev_ms | 遅延変動を追加 |
bitrate_reduce | new_bitrate_kbps | ビデオエンコードをスロットル |
frame_drop | drop_percentage (0-100) | ビデオフレームをスキップ |
bandwidth_limit | bandwidth_kbps | 総スループットを上限 |
{
"spike_distribution": {
"strategy": "even",
"min_spacing_seconds": 5,
"jitter_percent": 15,
"respect_min_offset": true
}
}
# RTP解析機能付きの提供レシーバー
go run examples/go/udp_receiver.go 5002
出力:
Listening for RTP packets on UDP port 0.0.0.0:5002
Packet #100 from 127.0.0.1:xxxxx:
Participant ID: 1001
Payload Type: 96 (H.264 video)
Sequence: 1234
Timestamp: 90000
SSRC: 1001000
Payload Size: 1200 bytes
═══════════════════════════════════════════════════════════
パケット統計
═══════════════════════════════════════════════════════════
Duration: 60s
Total Packets: 180000 (3000 pkt/s)
Total Bytes: 450 MB (60 Mbps)
Media Type Breakdown:
Video (H.264): 120000 packets (66.7%)
Audio (Opus): 60000 packets (33.3%)
Unique Streams (SSRCs): 1500
Unique Participants: 1500
# 単一参加者
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id>
# 複数参加者(最大150)
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id> 150
# 実際のテストIDを使用した例
go run ./examples/go/webrtc_receiver.go http://localhost:8080 chaos_test_1770831684 150
注意: WebRTCは1:1接続が必要です。Kubernetesの場合は、全参加者を自動集約するUDPレシーバーを使用してください。
RTPパケットフォーマット:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension ID=1 | Length=4 | Participant ID (uint32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| H.264/Opus Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ペイロードタイプ:
96: H.264ビデオ(RFC 6184)111: Opusオーディオ(RFC 7587)参加者IDの抽出:
// 拡張ビットが設定されているか?
if (packet[0] & 0x10) != 0 {
offset := 12 + int(packet[0]&0x0F)*4 // CSRCをスキップ
extID := binary.BigEndian.Uint16(packet[offset:])
if extID == 1 {
participantID := binary.LittleEndian.Uint32(packet[offset+4:])
}
}
| 参加者数 | メモリ | CPU | 帯域幅 |
|---|---|---|---|
| 100 | 2GB | 2コア | 250 Mbps |
| 500 | 6GB | 8コア | 1.2 Gbps |
| 1000 | 12GB | 16コア | 2.5 Gbps |
| 1500 | 18GB | 24コア | 3.7 Gbps |
参加者あたり(1280x720@30fps + Opus):
# /metrics エンドポイントで公開
av_chaos_monkey_participants_total
av_chaos_monkey_packets_sent_total
av_chaos_monkey_bytes_sent_total
av_chaos_monkey_spikes_active
av_chaos_monkey_packet_loss_percent
av_chaos_monkey_jitter_ms
# Dockerモード: 監視スタックを起動
docker-compose --profile monitoring up
# Kubernetesモード: 監視をデプロイ
kubectl apply -f k8s/monitoring/prometheus-rbac.yaml
kubectl apply -f k8s/monitoring/prometheus.yaml
kubectl apply -f k8s/monitoring/grafana.yaml
# Grafanaにアクセス
# Docker: http://localhost:3000
# Kubernetes: http://localhost:30030 (NodePort)
# デフォルト認証情報: admin/admin
# Prometheusにアクセス
# Docker: http://localhost:9091
# Kubernetes: http://localhost:30090 (NodePort)
Kubernetes自動検出:
prometheus.io/scrape: "true" のアノテーションが付与されている/metrics を5秒ごとにスクレイピング# テストメトリクスを取得
curl http://localhost:8080/api/v1/test/{test_id}/metrics | jq
# 出力
{
"aggregate": {
"total_frames_sent": 45000,
"total_packets_sent": 180000,
"total_bitrate_kbps": 250000,
"avg_jitter_ms": 12.5,
"avg_packet_loss": 2.3,
"avg_mos_score": 4.1
}
}
# UDPターゲット設定を確認
kubectl logs orchestrator-0 | grep "UDP transmission enabled"
# UDPリレーが動作しているか確認
kubectl get pod udp-relay
# ポートフォワードを確認
ps aux | grep "kubectl port-forward"
# UDP接続をテスト
nc -u -z localhost 5002
# TURNサーバーを確認
kubectl get svc coturn-lb
# ICE候補を確認
kubectl logs orchestrator-0 | grep "ICE"
# TURN接続をテスト
turnutils_uclient -v -u webrtc -w webrtc123 <turn-server>:3478
# ポッドあたりの参加者数を確認
kubectl exec orchestrator-0 -- curl -s http://localhost:8080/api/v1/test/{test_id}/metrics | jq '.participants | length'
# 参加者を減らすかポッド数を増やす
go run tools/k8s-start/main.go -replicas 10 -participants 1000
# Dockerメモリを増やす(Docker Desktop)
# 設定 → リソース → メモリ → 16GB
単一のUDPソケットは、カーネルバッファのオーバーフローなしで3000以上の同時ストリームを処理できません。解決策:
setsockopt(SO_RCVBUF, 8MB)BSD 3-Clause License
貢献歓迎!主な領域: