Эфир идёт четвёртый час. В чате спокойно, зрители слушают докладчика, в панели зелёные индикаторы. А на диске сервера тем временем остаётся всё меньше места: запись не знает, что скоро упрётся в потолок, и продолжает честно дописывать байты. Когда свободное место кончится, пострадает не только последний файл. Остановиться может всё, что пишет на этот же раздел: журналы, база данных, временные файлы, плейлисты.
Хорошая новость в том, что такая история почти целиком предсказуема. Объём записи складывается из нескольких чисел, которые известны заранее, а удалять файлы можно по правилу, которое придумано в спокойный день. Разберём, как к этому прийти: от арифметики битрейта до порядка, при котором место освобождается раньше, чем оно понадобится эфиру.
Сколько весит час эфира: биты, байты и честное округление
Начнём с главной путаницы. Битрейт потока почти всегда называют в битах в секунду (килобитах или мегабитах), а место на диске измеряют в байтах. В байте восемь бит, и если забыть об этом, оценка ошибётся в восемь раз, причём в опасную сторону: вы решите, что места с запасом, а его не хватит.
Возьмём условный пример, не измерение с реального проекта. Пусть видео идёт на 4 000 кбит/с, звук на 128 кбит/с. Суммарно получается 4 128 кбит/с, или 4,128 Мбит/с.
- В секунду: 4 128 000 бит ÷ 8 = 516 000 байт, то есть около 0,516 МБ.
- В час: 516 000 × 3 600 = 1 857 600 000 байт, примерно 1,86 ГБ (в десятичных единицах) или около 1,73 ГиБ (в двоичных).
- В сутки непрерывной записи: 1,8576 ГБ × 24 ≈ 44,6 ГБ.
Теперь сравните с ресурсами. На плане Ba с 40 ГБ NVMe такой круглосуточный поток заполнил бы весь диск меньше чем за сутки, даже не оставив места системе. На Uab с 80 ГБ хватило бы примерно на полтора суток в теории, а на практике меньше: часть диска занимает операционная система, пакеты, журналы и сами приложения. Эти ресурсы в плане — отправная точка для теста, а не обещание ёмкости, поэтому финальную цифру стоит получить на своём потоке.
Две оговорки к расчёту. Первая: битрейт в настройках энкодера обычно целевой. Реальный поток колеблется вокруг него, особенно при режиме с переменным битрейтом, где сцена с быстрым движением съедает больше, чем статичная заставка. Поэтому к расчётной цифре полезно добавлять запас, скажем, десятую часть сверху, а лучше записать тестовый час и посмотреть на реальный размер файла. Вторая: контейнер добавляет накладные расходы на служебные данные. Они обычно невелики по сравнению с самим потоком, но не равны нулю.
Если вы записываете несколько вариантов одновременно (например, исходный поток и пониженные копии для разных скоростей соединения), объёмы складываются. В HLS клиент выбирает вариант из главного плейлиста в зависимости от доступной полосы, как описано в RFC 8216. Но это про раздачу зрителям: хранить на диске вы можете один вариант, а можете все, и от этого выбора зависит расчёт. Решите заранее, какой именно вариант считается архивным.
Формула, которую стоит держать под рукой
Объём в гигабайтах за сутки примерно равен суммарному битрейту в Мбит/с × 0,45 × число часов записи в сутки × число одновременно сохраняемых вариантов. Коэффициент 0,45 получается из 3 600 секунд, делённых на 8 и на 1 000. Для нашего примера: 4,128 × 0,45 ≈ 1,86 ГБ в час, как и выше. Умножив на число дней хранения, вы получаете нижнюю границу нужного диска, к которой ещё добавляется запас.
Один бесконечный файл или нарезка на сегменты
Можно писать эфир в один файл от начала до конца. Это просто, пока всё идёт хорошо. Но у такого подхода три слабых места. Файл на несколько часов неудобно копировать и загружать: если соединение оборвалось на 90 процентах, начинать приходится заново. Удалить можно только целиком или никак, поэтому ротация по возрасту теряет смысл. А главное, многие контейнеры записывают служебные данные, нужные для нормального открытия, только при корректном завершении. Если процесс упал посреди файла, вы рискуете получить много гигабайт, которые плеер не умеет открывать.
Нарезка на сегменты решает все три проблемы разом. В MediaMTX за это отвечают параметры записи: документация описывает, что record включает запись, а recordSegmentDuration управляет длительностью сегментов. Пример фрагмента конфигурации:
record: yes
recordSegmentDuration: 1h
recordDeleteAfter: 72hЗначения здесь иллюстративные, подбирайте под свою задачу. Вот что даёт сегментация на практике:
- Ограниченный ущерб. Если что-то пошло не так, пострадает текущий сегмент, а не весь эфир.
- Удобные единицы. Часовой файл на 1,86 ГБ в нашем примере можно проверить, загрузить во внешнее хранилище, отдать монтажёру.
- Осмысленное удаление. Возраст файла становится понятным критерием.
Цена у сегментации тоже есть. Эфир превращается в набор файлов, и их нужно склеивать или хотя бы воспроизводить по порядку. Слишком короткие сегменты, например по минуте, дадут тысячи файлов за сутки и лишнюю возню с именами. Слишком длинные возвращают прежнюю проблему. Для длительных эфиров разумная середина лежит где-то между десятками минут и часом, но это ориентир, а не правило: ориентируйтесь на то, какой кусок вам не страшно потерять и удобно переслать.
Что значит «файл завершён»
Отдельно стоит понять различие, которое проводит документация MediaMTX: между небольшими частями, которые пишутся по ходу, и завершёнными сегментами. Для вас это граница между двумя состояниями файла. Пока сегмент пишется, он принадлежит процессу записи, и трогать его нельзя: ни копировать как готовый, ни удалять, ни отдавать на обработку. Завершённым он становится только когда процесс закрыл его сам.
Отсюда практическое правило для любых ваших скриптов. Не ориентируйтесь на то, что файл «уже существует» или «уже большой». Считайте файл готовым, если он старше длительности сегмента с запасом, или если есть явный сигнал завершения. Самый простой надёжный способ: брать в обработку только файлы, которые не менялись дольше, чем заведомо длится один сегмент плюс небольшой запас. Тогда текущий файл никогда не попадёт под руку.
Две корзины: что уже опубликовано и что ещё ждёт обработки
Теперь главный вопрос ротации: сколько хранить. Ответ «трое суток» звучит разумно, пока не выясняется, что запись пятничного эфира монтажёр возьмёт в работу только в среду. Тогда автоматическое удаление по возрасту аккуратно уничтожит материал, который ещё нужен. Поэтому записи удобно разделить на два состояния, у которых разные правила.
| Состояние | Что это значит | Что можно делать |
|---|---|---|
| Сырые, необработанные | Эфир записан, но не смонтирован и не выложен | Хранить, пока не закончена обработка; удалять только вручную или по очень длинному сроку |
| Обработанные и опубликованные | Итоговое видео где-то лежит и проверено | Сырой сегмент можно удалять по короткому сроку или переносить в архив |
Встроенное удаление по возрасту устроено просто: recordDeleteAfter задаёт, через какое время записи удаляются. По документации значение 0s отключает удаление. Это важный нюанс: параметр не знает, обработан ли файл. Он смотрит только на возраст. Значит, у вас два пути.
Первый путь: поставить срок хранения с запасом, превышающим самый долгий реалистичный путь записи до публикации. Если монтаж обычно занимает два дня, а в отпуск бывает и неделя, срок в трое суток опасен. Второй путь: отключить автоматическое удаление значением 0s и удалять файлы собственным скриптом, который сначала проверяет, что итог опубликован, и только потом чистит. Второй путь надёжнее, но требует дисциплины: забытый скрипт превращает диск в склад, и мы возвращаемся к исходной проблеме.
Практичный компромисс состоит из двух слоёв. Автоматическое удаление срабатывает на длинном сроке и служит последней страховкой от разрастания. А рядом работает ваш более умный процесс, который освобождает место раньше, когда ясно, что файл больше не нужен. Страховка по возрасту не заменяет чистку по смыслу, она лишь не даёт диску расти бесконечно, если чистка сломалась.
Пример расписания для условной студии
Допустим, раз в неделю идёт четырёхчасовой эфир по нашей прежней цифре, 4 × 1,86 ≈ 7,4 ГБ за раз. Это условный пример для расчёта. Сырой материал хранится до публикации итогового ролика, максимум десять дней, потом уходит. За десять дней на диске лежит не больше двух эфиров, то есть около 15 ГБ. Прибавим десятую часть на колебания битрейта и получим около 16 ГБ. Это уже можно сравнивать с планом: на 40 ГБ остаётся место для системы и запаса, но граница не такая широкая, как кажется. Если эфиров станет два в неделю, расчёт придётся пересчитать.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Когда процесс падает посреди записи
Сбои случаются: перезапуск службы, нехватка памяти, аварийное завершение, перезагрузка сервера при обновлении. Важно честно понимать, что при этом происходит с записью, а не надеяться на лучшее.
Документация MediaMTX прямо предупреждает: при сбое последняя часть записи может быть потеряна. Это нормальное свойство потоковой записи, а не ошибка настройки. Данные идут небольшими порциями, и то, что не успело быть сохранено в завершённом виде, пропадает. Поэтому в статье нет обещания, что любой оборванный файл можно восстановить. Иногда получится открыть всё, кроме последних секунд. Иногда нет. Закладывайтесь на потерю хвоста и не строите планы на спасение.
Из этого вытекают конкретные решения.
- Короткие сегменты уменьшают масштаб потери. При часовом сегменте худший случай без доступного файла стоит до часа материала, при десятиминутном намного меньше. Платите за это количеством файлов.
- Проверяйте файл после перезапуска. Не считайте, что раз запись возобновилась, всё в порядке. Откройте последний завершённый сегмент и сегмент, который писался в момент сбоя, и убедитесь, что они воспроизводятся.
- Отделяйте «сомнительные» файлы. Сегмент, на котором произошёл обрыв, не нужно сразу отправлять в обработку или удалять. Переместите его в отдельное место и посмотрите вручную.
- Не удаляйте то, что открыто процессом. Если ваш скрипт чистки совпал по времени со сбоем, он может принять оборванный файл за старый и удалить его. Критерий «не менялся дольше длительности сегмента» защищает и здесь.
Отдельное напоминание про диск. Сбой из-за заполнения места сам по себе способен оборвать запись, и тогда последний файл как раз окажется самым пострадавшим. То есть нехватка места не только мешает новым записям, она портит уже идущую. Именно поэтому место нужно освобождать заранее, а не в момент, когда процесс начал спотыкаться.
Как проверить, что архив открывается
Проверка не должна быть ритуалом. Хороший минимальный сценарий такой: после тестового эфира принудительно перезапустите службу записи прямо посреди сегмента, затем откройте три файла: последний полностью завершённый, оборванный и первый после перезапуска. Запишите, что получилось с каждым. Так вы узнаете реальное поведение вашей связки, а не чужое из документации. Заодно увидите, как называются файлы после перезапуска и не перезаписывает ли новый сегмент старый. Всё это придётся определить на вашем стенде, потому что именно от него зависит безопасная схема.
Загрузка наружу — это не то же самое, что запись на диск
Документация отдельно подчёркивает: запись на локальный диск и загрузка завершённых файлов на удалённое хранилище — разные действия. Это легко упустить, когда обе вещи происходят «автоматически». Диск сервера в такой схеме — буфер, а не архив. Файл сначала появляется локально, потом его забирает загрузка, и лишь после подтверждённой выгрузки локальную копию можно считать лишней.
Здесь легко ошибиться порядком. Если чистка по возрасту сработала раньше загрузки, например, хранилище было недоступно сутки, вы теряете файл, который ещё не уехал. Поэтому между удалением и переносом нужна связь: удалять локально можно только то, что уже проверено на удалённой стороне. Проверка может быть простой: сравнить размер или контрольную сумму, убедиться, что объект на месте. Просто «команда загрузки завершилась» — слабое доказательство, хотя для первой версии скрипта и этого достаточно, если дальше следует проверка размера.
Если ваши записи нужны редко, но хранить их долго, логично перенести их с быстрого диска сервера в хранилище, рассчитанное на объём. Для сервиса с файлами, который разворачивается на том же VPS, подойдёт, например, Nextcloud: в публичном каталоге MAATRIX он предлагается с установкой, а точное состояние наличия и операционную систему стоит проверить в форме заказа. Но такое решение съедает тот же диск, поэтому оно не отменяет расчёт объёма, а только меняет, что именно лежит на NVMe.
Свободный запас как отдельная статья расхода
Расчёт объёма записи — это только половина арифметики. Вторая половина — сколько места должно оставаться свободным в любой момент, чтобы эфир не упёрся в потолок. Я предлагаю думать так: запас — это не «остаток, как получится», а намеренно зарезервированная величина, которую вы не трогаете.
Из чего он складывается:
- Длина самого длинного эфира. Если вы допускаете эфир в шесть часов, при нашем битрейте это около 11,1 ГБ нового материала, и они должны поместиться на диск целиком, даже если предыдущая чистка не отработала.
- Время реакции на сбой чистки. Если скрипт упал в ночь на воскресенье, а заметили вы в понедельник, диск продолжает наполняться. Запас должен покрывать это окно.
- Системные нужды. Журналы, обновления пакетов, временные файлы. Журналы тоже растут и тоже требуют ротации, о настройке которой рассказано в материале про logrotate.
Сложим для условного примера: эфир на шесть часов (11,1 ГБ), окно сбоя чистки в один эфир (ещё около 11,1 ГБ) и примерно 5 ГБ системе. Получается около 27 ГБ запаса. На диске в 80 ГБ плана Uab остаётся примерно 53 ГБ под сами записи, то есть около семи эфиров по четыре часа или около четырёх по шесть. Это конкретная картинка: она показывает, что диск на 80 ГБ — не «много места», а ограниченный ресурс, который нужно планировать.
Порог, при котором вы начинаете действовать
Запас полезен, только если вы замечаете его нарушение. Поставьте порог, при пересечении которого вы получаете уведомление: например, когда свободно меньше размера запаса. Порог должен срабатывать с опережением, пока вы ещё можете что-то сделать, а не когда осталось полчаса записи. Сам механизм уведомления зависит от вашего мониторинга, здесь важно только правило: сигнал приходит заранее, а не на краю.
Ещё одно разумное правило: проверять свободное место перед началом эфира. Если под ожидаемую длительность места не хватает, лучше узнать об этом за десять минут до выхода в эфир, чем на третьем часу. Это можно сделать простой проверкой в скрипте запуска: сравнить свободное место с расчётным объёмом эфира плюс запас, и если не хватает, сначала освободить место, а не надеяться.
Что делать с местом: удалять, переносить или расширять
Когда расчёт показывает, что места мало, вариантов три, и у каждого своя цена.
| Способ | Когда подходит | Что надо учесть |
|---|---|---|
| Удалять раньше | Итог опубликован и проверен, сырой материал не нужен | Удаление необратимо; убедитесь, что итог открывается |
| Переносить в архив | Материал нужен, но редко | Сначала проверить, что копия на месте; перенос — отдельное действие от записи |
| Брать план побольше | Расчётный объём стабильно близок к границе | Больше места не отменяет политику хранения, а только отодвигает проблему |
Последний способ часто выбирают первым, потому что он не требует думать. Но диск побольше без правила хранения заполняется так же уверенно, просто позже. Разница между планами хорошо видна на нашем расчёте: 80 ГБ у Uab, 160 ГБ у Heka, 240 ГБ у Maat. Это ресурсы по плану, а чтобы выбрать, начните с расчёта суточного объёма и срока хранения, потом поставьте тестовую запись и сверьте с реальным размером файлов. Если сервер ещё и раздаёт эфир зрителям, помните, что диск — лишь одна из потребностей, и процессор с сетью придётся проверить отдельным тестом.
Если вы только подбираете сервер под задачу, в публичном предложении MAATRIX указаны NVMe, процессоры AMD EPYC и root-доступ, а локации перечислены: UK, US, RU и FR. Оплата возможна картой РФ или через СБП, и VPS создаётся автоматически за пару минут после оплаты. Для записи эфира это значит, что поменять план или развернуть тестовый стенд можно без долгой подготовки, но расчёт объёма всё равно остаётся вашей работой.
Порядок действий, который собирает всё вместе
Сведём рассуждение в последовательность, которую можно проверить по шагам.
- Посчитайте суточный объём: суммарный битрейт в битах, делите на восемь, умножайте на секунды. Добавьте запас на колебания.
- Включите запись с сегментами такой длины, потерю которой вы готовы пережить.
- Решите, какие записи считаются необработанными, и защитите их от удаления по возрасту.
- Автоматическое удаление поставьте как длинную страховку, а основную чистку делайте после подтверждения, что итог опубликован или файл перенесён.
- В скриптах работайте только с файлами, которые процесс записи уже закончил.
- Оставьте свободный запас на самый длинный эфир, сбой чистки и нужды системы.
- Проверьте принудительным перезапуском, что оборванный сегмент не портит остальные, и не рассчитывайте на восстановление хвоста.
Ни один шаг здесь не сложен по отдельности. Трудность в том, что они должны согласоваться между собой: срок хранения не короче времени обработки, чистка не быстрее загрузки, запас не меньше самого длинного эфира. Нарушение любого звена превращает аккуратную схему в сюрприз.
Правило, которое срабатывает раньше эфира
Сформулируем его одной фразой: перед началом каждого эфира на диске должно быть свободно не меньше, чем длится самый длинный допустимый эфир плюс запас на сбой, и если места меньше, чистка запускается до выхода в эфир, а не во время него. Диск, который освобождают на ходу, уже опоздал: запись в этот момент пишет, а не ждёт. Диск, который освободили заранее, не требует от вас ни героизма, ни ночных дежурств.
Четыре часа эфира, 1,86 ГБ в час, и ни одного неприятного сюрприза в панели мониторинга: это и есть то, к чему ведёт арифметика битов и байтов.