Полный проект для приёма RTMP-потока (OBS) → HLS (nginx) → загрузка сегментов в S3 (Yandex.Cloud). Набор скриптов упрощает запуск, остановку и сбор записи в MP4.
Содержание репозитория
Dockerfile,docker-compose.yaml— образ с nginx + RTMP + upload-скриптомnginx.conf— конфигурация nginx с RTMP/HLSupload2s3.py— наблюдатель папки HLS и загрузчик сегментов в S3s3config— env-файл с параметрами доступа к S3 (не храните секреты в git)start.sh— собрать и поднять сервисstop.sh— скачать сегменты, собратьoutput.mp4, удалить сегменты из S3 (опц.) и остановить стекdownload_and_concat.sh— вспомогательный скрипт для скачивания + склейки
Требования
- Docker и Docker Compose
ycCLI (опционально) — удобно для управления Yandex Cloudffmpeg(на хосте) — опционально; если нет,stop.shиспользует Docker-образ с ffmpeg
Безопасность
- Не кладите
s3configв репозиторий. Добавьтеs3configв.gitignore. - Ротируйте access-keys и используйте минимально необходимые роли.
- Быстрая схема работы
- Запускаете
./start.sh— поднимается nginx (RTMP/HLS) и скриптupload2s3.py. - В OBS стримите в
rtmp://<HOST>:1935/live/<STREAM_KEY>. - nginx пишет сегменты в
/tmp/hls;upload2s3.pyподхватывает файлы и загружает в S3. - После остановки стрима запускаете
./stop.sh— скачивание сегментов, сборoutput.mp4, очистка S3 (по желанию) иdocker compose down.
- Настройка Yandex.Cloud (создать сервисный аккаунт и ключи)
Через Web Console (GUI)
- Откройте Yandex.Cloud Console → IAM & Admin → Service accounts → Create service account.
- Выберите сервисный аккаунт → Keys → Create access key. Сохраните access key id и secret (secret показывается один раз).
- Object Storage → Create bucket (например
obs-test1). - IAM → Access bindings → Add binding: Subject = ваш service account, Role =
Storage Object Admin(или подходящая роль для работы с объектами).
Через yc CLI (автоматизация)
# получить folder id (если нужно)
yc config get folder-id
# создать сервисный аккаунт
yc iam service-account create --name obs-uploader --description "Uploader for HLS" --folder-id $(yc config get folder-id)
# получить ID созданного аккаунта (пример)
SA_ID=$(yc iam service-account list --folder-id $(yc config get folder-id) --format json | jq -r '.[] | select(.name=="obs-uploader") | .id')
# создать access-key (выведет JSON с ключом и секретом)
yc iam access-key create --service-account-id $SA_ID > key.json
cat key.json
# назначить роль storage.objectAdmin на уровень папки (или проект)
FOLDER_ID=$(yc config get folder-id)
yc resource-manager folder add-access-binding --folder-id $FOLDER_ID --role storage.objectAdmin --subject serviceAccount:$SA_ID
# создать бакет (если ещё нет)
yc storage bucket create --name obs-test1 --default-storage-class standardСкопируйте accessKeyId и secret в безопасное место и используйте их в s3config.
- Файл
s3config
Создайте s3config в корне проекта с содержимым:
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_ENDPOINT_URL=https://storage.yandexcloud.net
S3_BUCKET=obs-test1
# (опционально) PREFIX=hls/Пример: S3_BUCKET=obs-test1, AWS_ENDPOINT_URL=https://storage.yandexcloud.net.
- Запуск (start)
Собрать и поднять сервис:
./start.shЧто делает start.sh:
- проверяет наличие
s3config - запускает
docker compose up --build -d nginx-rtmp
Просмотр логов:
docker compose logs -f nginx-rtmp- Настройки OBS
- Service: Custom...
- Server:
rtmp://<HOST>:1935/live(если OBS на той же машине —localhost) - Stream Key: любое имя (например
mystream) — по ключу nginx создаёт HLS-плейлистmystream.m3u8 - Output → Encoder: x264 (или аппаратный)
- Rate control: CBR
- Bitrate: 2500–4000 kbps для 720p, 4500–6000 kbps для 1080p
- Keyframe interval: 2 (сек)
После старта стрима проверьте плейлист:
http://<HOST>:8080/hls/<STREAM_KEY>.m3u8
- Остановка и сбор (stop)
Запустить сбор записи и остановку:
./stop.shЧто делает stop.sh:
- скачивает сегменты из S3 в
./segments/(черезamazon/aws-cliконтейнер и./s3config) - формирует
concat.txtв натуральном порядке - склеивает
output.mp4(локально черезffmpegесли установлен, иначе через Docker image) - по умолчанию удаляет объекты в S3 в указанном префиксе (чтобы не накапливать сегменты)
- вызывает
docker compose down
Пропустить удаление сегментов из бакета:
SKIP_S3_CLEANUP=1 ./stop.shДополнительные опции (ручной режим)
- Посмотреть список объектов:
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls "s3://$S3_BUCKET/${PREFIX:-hls/}"'- Скачать сегменты вручную:
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 cp --recursive "s3://$S3_BUCKET/${PREFIX:-hls/}" ./segments/'- Troubleshooting
InvalidBucketName— проверьте имя бакета и наличие бакета в том же регионе/endpoint.AccessDenied— проверьте, что сервисный аккаунт имеет рольstorage.objectAdminна нужном ресурсе.403приhttp://<HOST>:8080/hls/— это нормально (директория без index). Открывайте конкретный*.m3u8.- Ошибки
FileNotFoundErrorпри upload — скрипт теперь игнорирует временные.bakфайлы и ждёт стабилизации размера файла перед upload.
- Security & best practices
- Не коммитьте
s3config. - Храните секреты в менеджере секретов (Yandex Lockbox, Vault и т.д.) для production.
- Выдавайте минимально необходимые права и ротируйте ключи.
- Возможные улучшения
- Собрать Docker image с awscli+ffmpeg для полноценной контейнерной сборки
stopбез ffmpeg на хосте. - Добавить флаги к
stop.sh(--no-clean,--output <file>,--keep-remote). - Добавить healthcheck и systemd unit для запуска в продакшн.
Если хотите — реализую любой из пунктов.
Небольшой проект: nginx-rtmp пишет HLS в /tmp/hls, процесс upload2s3.py загружает сегменты в S3 (например Yandex Cloud Object Storage). В репо есть вспомогательные скрипты для запуска и остановки.
Содержание
Dockerfile,docker-compose.yaml— образ с nginx + модулем rtmp и скриптом загрузкиnginx.conf— конфиг nginx (RTMP + HLS + HTTP)upload2s3.py— наблюдатель, загружает файлы в S3 (использует env)s3config— env-файл с параметрами S3 (не храните секреты в git)download_and_concat.sh— локальный скрипт для скачивания сегментов и склейкиstart.sh,stop.sh— helper-скрипты (start/stop/collect)
Требования
- Docker & Docker Compose
- (опционально)
ffmpegна хосте — если нет,stop.shпопытается использовать Docker image с ffmpeg s3configв корне репо с переменными (пример ниже)
Конфигурация (s3config)
Создайте (или отредактируйте) файл s3config в корне репо со следующими переменными:
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_ENDPOINT_URL=https://storage.yandexcloud.net
S3_BUCKET=obs
# (опционально) PREFIX=hls/
ВАЖНО: secret показывается только один раз при создании access key. Если вы потеряли secret — создайте новый ключ и удалите старый.
Запуск (быстро)
- Убедитесь, что
s3configзаполнен и находится в корне проекта. - Запустите сервисы:
./start.shOBS: настройки для OBS
- Service: Custom
- Server: rtmp://:1935/live
- Stream Key: любое имя (например
mystream) — это имя плейлистаmystream.m3u8 - Output: x264 (или HW), CBR, Keyframe interval = 2, bitrate в зависимости от разрешения
Остановка и сбор записей После завершения стрима выполните:
AWS_ACCESS_KEY_ID=... AWS_REGION=... AWS_DEFAULT_REGION=... AWS_SECRET_ACCESS_KEY=... AWS_ENDPOINT_URL=https://storage.yandexcloud.net S3_BUCKET=... ./stop.shЧто делает stop.sh:
- скачивает все
.tsсегменты из S3 (s3://$S3_BUCKET/${PREFIX:-hls/}) в./segments/(используетamazon/aws-cliконтейнер и./s3configдля env) - собирает
concat.txtв правильном порядке (natural sort) - склеивает
output.mp4(локально черезffmpegесли установлен, иначе через Docker image) - останавливает docker compose (
docker compose down)
Дополнение: очистка бакета
- По умолчанию
stop.shпосле успешной сборки видео удаляет все объекты в указанном префиксе (s3://$S3_BUCKET/${PREFIX:-hls/}), чтобы не накапливать сегменты в бакете. - Если вы хотите оставить сегменты в бакете, установите переменную окружения
SKIP_S3_CLEANUP=1перед запускомstop.sh.
Пример (без удаления удалённых объектов):
SKIP_S3_CLEANUP=1 ./stop.sh
Команды вручную
- Просмотреть содержимое бакета:
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls "s3://$S3_BUCKET/hls/"'- Скачать сегменты (в папку
segments):
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 cp --recursive "s3://$S3_BUCKET/hls/" ./segments/'Ошибки и отладка
- Если
upload2s3.pyсообщаетAccessDenied— назначьте сервисному аккаунту рольstorage.objectAdminв Yandex Cloud для проекта/папки или соответствующую роль на ресурс бакета. nginxможет отдавать 403 на корень/hls/— это нормально (нет index). Открывайте конкретный плейлист:http://<HOST>:8080/hls/<STREAM_KEY>.m3u8.
Безопасность
- Не храните
s3configв git. Добавьтеs3configв.gitignore. - Для продакшна используйте секретный менеджер вместо плоского env-файла.
Если нужно, могу добавить: единый Docker image с aws+ffmpeg и пример systemd unit или инструкцию для CI.
Коротко: nginx-rtmp пишет HLS в /tmp/hls, upload2s3.py загружает сегменты в S3 (Yandex Cloud), скрипты помогают запускать и останавливать сервис и собирать запись.s3.py загружает сегменты в S3 (Yandex Cloud), скрипты помогают запускать и останавливать сервис и собирать запись.
Файлы конфигурации
s3config— env-файл с ключами и настройками S3 (уже в репо). Пример:AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_ENDPOINT_URL=https://storage.yandexcloud.net S3_BUCKET=obs # (необязательно) PREFIX=hls/ ``` ``` ВАЖНО: не коммитьте секреты в git; добавьте `s3config` в .gitignore в продакшн.
Запускуск
-
Собрать и поднять сервис:сервис:
./start.sh
Скрипт делает
docker compose up --build -d nginx-rtmp.Скрипт делаетdocker compose up --build -d nginx-rtmp. -
Настройки OBS:
- Service: Custom - Service: Custom
- Server: rtmp://:1935/live- Server: rtmp://:1935/live
- Stream Key: любое имя (например
mystream)я (напримерmystream) - Output: CBR, keyframe interval 2, bitrate по разрешению (см. примеры в проекте)- Output: CBR, keyframe interval 2, bitrate по разрешению (см. примеры в проекте)
Остановка и сборка видео
- Остановить сервис и скачать сегменты + собрать video:- Остановить сервис и скачать сегменты + собрать video:
Что делает: Что делает:
./stop.sh ./stop.sh
- вызывает aws-cli в контейнере для копирования
s3://$S3_BUCKET/$PREFIX→./segments/вызывает aws-cli в контейнере для копированияs3://$S3_BUCKET/$PREFIX→./segments/ - генерирует
concat.txtв правильном порядкеxt` в правильном порядке - если ffmpeg есть на хосте — использует его; иначе выполняет ffmpeg в Docker (образ
jrottenberg/ffmpeg) если ffmpeg есть на хосте — использует его; иначе выполняет ffmpeg в Docker (образjrottenberg/ffmpeg) - сохраняет итог в
output.mp4- сохраняет итог вoutput.mp4 - затем выполняет
docker compose down``docker compose down
- вызывает aws-cli в контейнере для копирования
Примеры ручного выполнения (если нужно только скачать):
# посмотреть список# посмотреть список
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls "s3://$S3_BUCKET/hls/"' --entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls "s3://$S3_BUCKET/hls/"'
# скачать сегменты# скачать сегменты
docker run --rm -v "$PWD":/work -w /work --env-file ./s3config \
--entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 cp --recursive "s3://$S3_BUCKET/hls/" ./segments/' --entrypoint sh amazon/aws-cli:latest -c 'aws --endpoint-url "$AWS_ENDPOINT_URL" s3 cp --recursive "s3://$S3_BUCKET/hls/" ./segments/'Требования
- Docker + docker compose
- (опционально) ffmpeg на хосте — если нет, stop.sh использует ffmpeg в контейнере
Права и безопасность
- Дайте сервис-аккаунту в Yandex Cloud роль
storage.objectAdminдля работы с объектами в бакете.- Дайте сервис-аккаунту в Yandex Cloud рольstorage.objectAdminдля работы с объектами в бакете. - Секреты храните в менеджере секретов в продакшн.
Если нужно — могу добавить Docker image с aws+ffmpeg для полного offline запуска скрипта.