Применение изменений
Три класса изменений конфигурации, проверка файла до перезапуска, разница между reload и restart, судьба сессий зрителей и откат к прошлой версии.
Изменение в hastreamer.conf попадает в один из трёх классов. Класс определяет, что произойдёт
после systemctl reload: изменение применится немедленно, будет записано в файл, но дождётся
systemctl restart, либо перезапустит один поток, не тронув остальные.
Сервер классифицирует каждое изменение сам и пишет вердикт в журнал — угадывать не нужно.
Порядок безопасного изменения#
# 1. Сохраните копию — откат из файла всегда быстрее откатa через API. sudo cp /etc/hastreamer/hastreamer.conf /root/hastreamer.conf.bak # 2. Отредактируйте файл. sudo -e /etc/hastreamer/hastreamer.conf # 3. Проверьте синтаксис и правила БЕЗ применения. sudo hastreamer --validate /etc/hastreamer/hastreamer.conf # 4. Примените горячую часть. Ошибочный файл будет отвергнут, # работающая конфигурация останется прежней. sudo systemctl reload hastreamer # 5. Прочитайте вердикт: что применилось, что ждёт перезапуска. sudo journalctl -u hastreamer -n 30 --no-pager # 6. Только если в вердикте есть строки о перезапуске. sudo systemctl restart hastreamer
reload при ошибочном файле оставляет сервер работать. restart при ошибочном файле останавливает
его: процесс завершится с кодом 1, а Restart=on-failure будет поднимать его каждые 2 секунды —
получится цикл перезапусков без видео. Всегда проверяйте файл через --validate и reload.
Три класса изменений#
| Класс | Что делает systemctl reload |
Кого затрагивает |
|---|---|---|
| Горячее | применяет немедленно, на следующем запросе | никого: соединения живут |
| Требует перезапуска | записывает в файл и сообщает об этом в журнале; работающий процесс продолжает со старым значением | все сессии — при последующем restart |
| Перезапуск потока | пересоздаёт только изменившиеся потоки | зрителей этих потоков |
Горячие изменения#
Применяются сразу и без разрыва соединений. Политика доступа перечитывается на каждый запрос, даже внутри уже открытого keep-alive-соединения, поэтому включение, отключение и ротация авторизации не требуют перезапуска.
| Директива или блок | Что меняется |
|---|---|
log_level |
минимальная тяжесть записей в журнале |
trusted_proxies |
список доверенных обратных прокси |
allow_origins |
список источников CORS |
public_paths |
пути, освобождённые от авторизации просмотра |
auth { } |
пароль администратора, статические API-токены, привязка к IP, секрет подписи |
auth_backend { } |
авторизация просмотра: тип, ключи, издатель, список отзыва |
principal <имя> { } |
встроенные учётные записи и их права |
session_keys { } |
срок жизни и привязка билетов сессии |
tls { } |
сертификат и ключ по умолчанию |
certificate <имя> { } |
сертификаты SNI, включая выпуск по ACME |
Изменение session_keys { secret } или auth { session_secret } меняет корень, из которого
выводится ключ подписи билетов сессии. Все выданные билеты становятся недействительными: зрители
получат 401 и переустановят сессию на следующем запросе плейлиста. Разрыва видео при этом нет,
если плеер умеет повторить запрос.
Изменения, требующие перезапуска#
Двадцать три параметра привязаны к моменту запуска процесса: порты слушателей, раскладка по ядрам,
глобальные параметры приёма и раздачи. Их новое значение попадает в файл сразу, но применяется
только на systemctl restart.
| Директива | Формулировка в журнале |
|---|---|
http |
HLS/API listener (http_port 8080 → 9443) |
https |
HTTPS listener (https_port …) |
rtsp |
RTSP listener (rtsp_port …) |
rtsps |
RTSPS listener (rtsps_port …) |
rtmp |
RTMP listener (rtmp_port …) |
rtmps |
RTMPS listener (rtmps_port …) |
webrtc (порт) |
WebRTC media port (webrtc_udp_port …) |
webrtc … public_ip= |
WebRTC identity (webrtc_public_ip …) |
cores |
worker cores (cores …) |
low_latency |
LL-HLS producer (low_latency …) |
rtsp_frame_source |
ring mode (rtsp_frame_source …) |
source_transport |
puller default (source_transport …) |
rtp_max_payload |
RTP packetizer (rtp_max_payload …) |
max_sessions |
session gate (max_sessions …) |
dvr_cleanup_secs |
DVR evictor (dvr_cleanup_secs …) |
media_timeout |
media-stall watchdog (media_timeout …) |
on_demand_grace |
on-demand grace (on_demand_grace …) |
udp_ingest_rcvbuf |
UDP-ingest buffer (udp_ingest_rcvbuf …) |
udp_ingest_shards |
UDP-ingest routers (udp_ingest_shards …) |
udp_ingest_pool_slots |
UDP-ingest ring (udp_ingest_pool_slots …) |
udp_pull_rate |
UDP pull pacing (udp_pull_rate …) |
acme_directory |
ACME directory (acme_directory …) |
acme_contact |
ACME contact (acme_contact …) |
Смена порта не действует после reload. Сервер об этом сообщает, но только в журнале. Если вы
поменяли http 8080; на http 80;, сделали reload и увидели, что старый порт всё ещё отвечает —
это не сбой, а именно этот класс. Нужен systemctl restart.
cluster требует перезапуска, но в вердикт не попадаетЧленство в кластере, канал между узлами и его общий ключ строятся один раз при запуске процесса.
В таблице выше блока cluster нет, поэтому reload о нём промолчит — и при этом не применит
его. Любую правку cluster — ключ, адрес узла, состав узлов, переопределения портов —
завершайте systemctl restart. Единственное, что пересчитывается живо, — размещение потоков:
директивы host и restream внутри блоков stream.
Перезапуск отдельного потока#
Блоки stream, template, storage и vod сверяются по одному. Перезапускается только тот
поток, чей отпечаток изменился; остальные сохраняют аптайм, буферы и сессии.
Отпечаток потока — это весь его блок, кроме labels. Отсюда правила:
| Что вы изменили | Что перезапустится |
|---|---|
input, dvr, segment_seconds, on_demand, caps, любой другой параметр потока |
только этот поток |
только labels |
ничего; новые метки авторизации действуют сразу |
path, segment_secs или identity у storage |
каждый поток, который в это хранилище пишет |
адрес, ключи или tls_verify у storage … s3 |
каждый поток, который туда копирует |
добавили блок stream |
новый поток запускается, существующие не трогаются |
удалили блок stream |
этот поток останавливается, его сессии закрываются |
Тот же перезапуск можно вызвать без правки конфигурации — например, чтобы переподключить зависший источник:
curl -sS -X POST "https://media.example.com/api/v1/streams/cam1/restart" \ -H "Authorization: Bearer $TOKEN"
{"path":"cam1","restarted":true}
labels намеренно исключены из отпечатка. Перевод камеры в другую группу доступа не прерывает
трансляцию и не сбрасывает зрителей — это единственное изменение внутри stream, которое
гарантированно бесплатно. См. Метки потоков.
Проверка файла до применения#
hastreamer --validate разбирает файл, прогоняет все правила и завершается, ничего не запуская и
ничего не меняя.
sudo hastreamer --validate /etc/hastreamer/hastreamer.conf
/etc/hastreamer/hastreamer.conf: OK (2 streams)
При ошибке разбора печатается номер строки:
parse /etc/hastreamer/hastreamer.conf: line 3: unknown directive "cors"
При ошибке правил валидатор накапливает все ошибки и печатает их через ; — чинить файл
можно за один проход, а не по одной ошибке за попытку:
invalid config /etc/hastreamer/hastreamer.conf: stream "cam": needs `source` or a non-empty `sources` chain; http_port and rtsp_port must be non-zero; http_port and rtsp_port must differ (both 0); stream "cam" has an empty source
Коды завершения:
| Код | Значение |
|---|---|
0 |
файл корректен |
1 |
ошибка разбора, ошибка правил или файл не читается |
2 |
путь к файлу не передан (usage: hastreamer --validate <config> [-j]) |
Флаг -j печатает каноническую JSON-форму конфигурации вместо строки OK. Это ровно то тело,
которое принимает PUT /api/v1/config:
sudo hastreamer --validate /etc/hastreamer/hastreamer.conf -j | head -20
Команда завершилась кодом 0 и напечатала OK (N streams), где N — число блоков stream в
файле. Если число не совпадает с ожидаемым, вы либо забыли блок, либо два блока имеют одинаковый
путь — тогда была бы ошибка duplicate stream path "…".
Ту же проверку можно выполнить по сети, не имея доступа к оболочке сервера. POST /api/v1/config/validate
разбирает переданный текст и возвращает классификацию, ничего не записывая:
curl -sS -X POST "https://media.example.com/api/v1/config/validate" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$(python3 -c 'import json,sys; print(json.dumps({"text": open("/etc/hastreamer/hastreamer.conf").read()}))')"
{ "classification": { "added": ["cam2"], "removed": [], "respawn": ["cam1"], "restart_required": ["HLS/API listener (http_port 8080 → 80)"], "untouched": 7 }, "ok": true }
POST /api/v1/config/validate отвечает 200 и в успешном, и в неуспешном случае. Признак —
поле ok. При "ok": false текст ошибок лежит в поле errors, поля classification нет.
Проверяйте ok, а не HTTP-код.
reload против restart#
systemctl reload hastreamer |
systemctl restart hastreamer |
|
|---|---|---|
| Механизм | SIGHUP, процесс не завершается |
процесс завершается и запускается заново |
| Файл с ошибкой | отвергается, сервер продолжает работу | процесс не стартует, цикл перезапусков |
| Порты слушателей | не меняются | применяются новые |
| Сессии зрителей | сохраняются, кроме потоков с изменившимся отпечатком | все закрываются |
| Незакрытые записи архива | не затрагиваются | закрываются корректно, окно слива 5 секунд |
| Время недоступности | нет | секунды |
Вердикт reload пишется в журнал одной из трёх строк:
config reload: applied epoch 9 (hash 4a186afe976f6ef3) config reload: no change config reload REJECTED: <текст ошибок> (running config unchanged)
Отвергнутый reload не меняет ничего: работающая конфигурация остаётся прежней, видео не
прерывается. Поэтому reload безопасно делать даже наугад — в худшем случае вы получите строку
REJECTED и текст ошибки.
Что происходит с сессиями зрителей#
| Событие | HLS, LL-HLS, DASH | MSE-WS, HTTP-TS | RTSP | WebRTC (WHEP) | Архив и VoD |
|---|---|---|---|---|---|
| Горячее изменение | ничего | ничего | ничего | ничего | ничего |
| Перезапуск одного потока | перерыв на время пересоздания, живое окно строится заново | соединение закрывается | сессия закрывается | сессия закрывается | чтение записанного не прерывается |
systemctl restart |
перерыв на время перезапуска | закрывается | закрывается | закрывается | закрывается |
| Смена источника внутри потока (резервирование) | не прерывается | не прерывается | не прерывается | не прерывается | — |
Переподключение источника по media_timeout |
не прерывается | не прерывается | не прерывается | не прерывается | — |
Различие принципиальное. Опросные протоколы (HLS, LL-HLS, DASH) не держат соединения: после перезапуска потока плеер, который повторяет запрос плейлиста, продолжит воспроизведение с нового живого края сам. Постоянные соединения (MSE-WS, HTTP-TS, RTSP, WebRTC) закрываются, и повторное подключение — задача плеера; встроенный плеер это делает, сторонний может не делать.
Чтение уже записанного архива и файлов VoD от перезапуска потока не зависит: запись на диск и её раздача — независимые пути.
Обрыв связи с камерой, переход на резервный источник и срабатывание сторожевого таймера
media_timeout не закрывают ни одной сессии зрителя: кольцевые буферы, сессии и запись потока
в каталоге сохраняются. В HLS появляется отметка разрыва, но плеер не получает 404. См.
Резервирование источников.
История конфигурации и откат#
Каждое принятое изменение записывается в журнал версий рядом с файлом конфигурации:
/etc/hastreamer/hastreamer.conf
/etc/hastreamer/hastreamer.conf.history/
index.jsonl — по строке на версию
1.conf — точные байты версии 1
2.conf
…
| Свойство | Значение |
|---|---|
| Глубина | последние 32 версии |
| Права | каталог 0700, файлы 0600 — в конфигурации лежат пароли и учётные данные камер |
Номер версии (epoch) |
монотонно растёт и никогда не уменьшается, в том числе при откате |
| Что записывается | точные байты применённой конфигурации плюс автор и способ изменения |
Журнал пополняется при любом принятом применении. Поле via называет источник изменения, поле
actor — кто его сделал:
via |
Источник изменения |
|---|---|
boot |
запуск процесса |
reload |
systemctl reload — actor при этом всегда signal |
api |
PUT /api/v1/config — применение текста конфигурации целиком |
globals |
PATCH /api/v1/config/globals — точечная правка глобальных директив |
apply |
создание, изменение или удаление потока либо хранилища через API |
rollback |
POST /api/v1/config/rollback |
cert-upload, cert-delete, cert-acme |
загрузка, удаление и автоматический выпуск сертификата |
template-save, template-delete |
изменение шаблонов |
vod-lib-save, vod-lib-delete |
изменение библиотек VoD |
Прочитать историю:
curl -sS "https://media.example.com/api/v1/config/history" \ -H "Authorization: Bearer $TOKEN"
[{"epoch":1,"hash":"d9b4d2d2a819ff24","shared_hash":"85851d2f59d9e592", "ts_ms":1786787600937,"actor":"system","via":"boot"}]
Откатиться к записанной версии:
curl -sS -X POST "https://media.example.com/api/v1/config/rollback" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"epoch": 7}'
{"epoch":10,"hash":"85851d2f59d9e592","changed":true,"rolled_from":7}
Версии старше 32 последних в журнале уже нет — откат к ней вернёт
404 {"title":"version not found"}. Если вам нужен долгий архив конфигураций, копируйте файл во
внешнее хранилище: см. Резервное копирование.
Байты версии 7 применяются как версия 10. История только дополняется, номер версии не уменьшается. Поэтому откат можно откатить.
rollback проходит ту же классификацию, что и обычное применение. Если откатываемая версия
отличается портом или другим параметром из таблицы выше, откат вернёт файл, но не работающее
значение — нужен systemctl restart. Ответ на откат этого не показывает: смотрите журнал.
Та же история доступна в панели управления: Конфигурация → История, с кнопкой отката у каждой записи. См. Веб-интерфейс.
Запрет изменений через API#
Создайте файл .locked рядом с конфигурацией — и любой изменяющий вызов API вернёт 423 Locked:
sudo touch /etc/hastreamer/hastreamer.conf.locked
{"type":"about:blank","title":"config locked","status":423, "detail":"the config is locked (/etc/hastreamer/hastreamer.conf.locked exists) — remove that file to enable edits via the API/UI"}
Чтение конфигурации, перезапуск службы и правка файла вручную продолжают работать — блокируются только записи через API и панель. Снимается удалением файла.
.locked — это защита от случайного изменения через панель, а не права доступа. Кто может писать
в /etc/hastreamer/, тот может удалить и сам файл блокировки. Разграничивайте доступ правами
файловой системы.
Что дальше#
- hastreamer.conf — все директивы — какой класс у конкретной директивы указан в колонке «Применение».
- Порты и сеть — что придётся согласовать с межсетевым экраном при смене порта.
- Диагностика неисправностей — если после применения поток не поднялся.