
Chaos Monkey, но для тестирования аудио/видео (webRTC и UDP)
Распределенная платформа хаос-инжиниринга для нагрузочного тестирования систем видеоконференций. Симулирует 1500+ участников WebRTC с потоками H.264/Opus и инжектирует пики сетевого хаоса для проверки устойчивости системы в условиях деградации.
Конвейер обработки медиа:
Плоскость управления:
Пул участников:
participant_id % total_partitions = partition_idАвтоконфигурация Kubernetes:
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"Каждый виртуальный участник генерирует реальные медиапотоки:
Пять типов пиков симулируют реальные сетевые условия:
Пики распределяются по длительности теста с помощью настраиваемых стратегий:
В развёртываниях Kubernetes используется разделение участников на партиции для горизонтального масштабирования:
participant_id % total_partitions == partition_idbase_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
Что происходит:
:8080127.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 ГБ | ~100 | 4 |
| 16 ГБ | ~250 | 8 |
| 24 ГБ | ~400 | 12 |
| 32 ГБ | ~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 для UDP-ретранслятораВариант A: UDP-приёмник (рекомендуется для Kubernetes)
# Получает агрегированный поток от всех 1500 участников
go run ./examples/go/udp_receiver.go 5002
Вариант B: WebRTC-приёмник (множество участников)
# Подключиться до 150 участников через WebRTC
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id> 150
Архитектурный поток:
1500 участников на 10 подах
→ Каждый под: 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 | 2 ГБ | 2 ядра | 250 Мбит/с |
| 500 | 6 ГБ | 8 ядер | 1.2 Гбит/с |
| 1000 | 12 ГБ | 16 ядер | 2.5 Гбит/с |
| 1500 | 18 ГБ | 24 ядра | 3.7 Гбит/с |
На одного участника (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"# Получение метрик теста
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)
# Настройки → Ресурсы → Память → 16 ГБ
Один UDP-сокет не может обработать 3000+ одновременных потоков без переполнения буфера ядра. Решения:
setsockopt(SO_RCVBUF, 8MB)BSD 3-Clause License
Приветствуются вклады! Ключевые области: