Implemented new storage endpoints in the API and updated the dependency injection to include storage-related services. Enhanced the frontend by adding storage routes and links in the admin layout, along with new types and localization for storage management. Updated documentation to reflect the new storage features and their usage in the admin interface.
272 lines
14 KiB
Markdown
272 lines
14 KiB
Markdown
# Подготовка диска под медиахранилище (tvvm)
|
||
|
||
Runbook: разметка, форматирование и монтирование диска `sdb` (700G) под хранилище TeleWave.
|
||
Выполняется **один раз** на сервере `tvvm` перед запуском фичи медиа. Все команды — от root
|
||
(`sudo -i` или с `sudo` перед каждой).
|
||
|
||
> ⚠️ Команды разметки **уничтожают данные** на целевом диске. Диск `sdb` сейчас пустой (нет
|
||
> разделов), но каждый шаг ниже содержит проверку — не пропускайте их.
|
||
|
||
Итог: раздел `sdb1` (ext4) смонтирован в `/srv/telewave/media`, внутри созданы рабочие каталоги,
|
||
запись из контейнера (работает под непривилегированным uid 1654) возможна.
|
||
|
||
---
|
||
|
||
## 0. Проверить, что это тот самый диск
|
||
|
||
```bash
|
||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS,FSTYPE /dev/sdb
|
||
```
|
||
|
||
Ожидается: `sdb` размером `700G`, тип `disk`, **без разделов и без точек монтирования**. Убедитесь
|
||
дополнительно, что на диске нет файловой системы и подписей:
|
||
|
||
```bash
|
||
sudo wipefs -n /dev/sdb # -n = «сухой прогон», ничего не меняет
|
||
```
|
||
|
||
Если вывод пустой — диск чист, продолжаем. Если что-то нашлось (следы старой ФС/RAID) —
|
||
**остановитесь** и разберитесь, что это, прежде чем идти дальше.
|
||
|
||
---
|
||
|
||
## 1. Создать таблицу разделов и один раздел
|
||
|
||
GPT + один раздел на весь диск:
|
||
|
||
```bash
|
||
sudo parted -s /dev/sdb mklabel gpt
|
||
sudo parted -s -a optimal /dev/sdb mkpart primary ext4 0% 100%
|
||
sudo partprobe /dev/sdb
|
||
lsblk /dev/sdb
|
||
```
|
||
|
||
Должен появиться `sdb1` размером ~700G.
|
||
|
||
---
|
||
|
||
## 2. Отформатировать в ext4
|
||
|
||
`-m 1` уменьшает резерв под root с 5% до 1% (на диске данных резерв в 35 ГБ не нужен), `-L` даёт
|
||
метку тома:
|
||
|
||
```bash
|
||
sudo mkfs.ext4 -m 1 -L telewave-media /dev/sdb1
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Создать точку монтирования
|
||
|
||
```bash
|
||
sudo mkdir -p /srv/telewave/media
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Прописать в /etc/fstab (монтирование по UUID)
|
||
|
||
Монтируем по UUID, а не по имени `sdb1` — имя может измениться при добавлении дисков. Узнать UUID:
|
||
|
||
```bash
|
||
sudo blkid /dev/sdb1
|
||
```
|
||
|
||
Скопируйте значение `UUID="..."` и добавьте строку в `/etc/fstab` (подставьте свой UUID):
|
||
|
||
```
|
||
UUID=ВАШ-UUID /srv/telewave/media ext4 defaults,noatime,nofail,x-systemd.device-timeout=10 0 2
|
||
```
|
||
|
||
Пояснения к опциям:
|
||
- `noatime` — не обновлять время доступа при чтении сегментов; заметно снижает лишние записи при
|
||
раздаче видео.
|
||
- `nofail` — если диск не подключился, система всё равно загрузится (приложение просто не сможет
|
||
писать, а не «висит» на загрузке).
|
||
- `x-systemd.device-timeout=10` — не ждать диск дольше 10с при старте.
|
||
|
||
Проверить, что fstab корректен, и смонтировать:
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo mount -a
|
||
findmnt /srv/telewave/media
|
||
```
|
||
|
||
`findmnt` должен показать смонтированный `ext4` на `/dev/sdb1`. **Ошибка на этом шаге лучше, чем
|
||
на следующей перезагрузке** — если `mount -a` ругается, чините fstab сейчас.
|
||
|
||
---
|
||
|
||
## 5. Рабочие каталоги и права
|
||
|
||
Создать структуру, ожидаемую приложением:
|
||
|
||
```bash
|
||
sudo mkdir -p /srv/telewave/media/{inbox,manual,uploads,originals,assets}
|
||
```
|
||
|
||
**Владелец.** Контейнер работает под непривилегированным пользователем `app` образа aspnet —
|
||
**uid/gid 1654** (`USER $APP_UID` в Dockerfile). Bind-mount пробрасывает права хоста внутрь как
|
||
есть, никакого маппинга uid не происходит: контейнер увидит ровно те номера, что стоят на хосте.
|
||
Значит, владельцем хранилища должен быть 1654.
|
||
|
||
Имени `app` на хосте нет — заводим группу с этим gid, чтобы права были читаемыми в `ls` и чтобы
|
||
в неё можно было добавить себя:
|
||
|
||
```bash
|
||
sudo groupadd -g 1654 telewave # «уже существует» — не ошибка, идём дальше
|
||
sudo usermod -aG telewave "$USER" # чтобы класть файлы в inbox/manual под собой
|
||
```
|
||
|
||
> Членство в группе применяется **только к новым сессиям**: перелогиньтесь (или `newgrp telewave`),
|
||
> иначе следующая команда отработает, а записать файл вы всё равно не сможете. Проверка — `id`
|
||
> должен показать `telewave` в списке групп.
|
||
|
||
Теперь владелец и базовые права:
|
||
|
||
```bash
|
||
sudo chown -R 1654:1654 /srv/telewave/media
|
||
sudo chmod -R 750 /srv/telewave/media
|
||
```
|
||
|
||
Два каталога наполняете вы, а не приложение:
|
||
|
||
- `inbox/` — разбирается сканером автоматически: файл с допустимым расширением и стабильным
|
||
размером регистрируется сам и уходит в обработку;
|
||
- `manual/` — **ручной разбор**: сканер сюда не заглядывает. Файлы видны в админке
|
||
(Медиа → «Из папки manual»), выбираются галочками и сразу привязываются к шоу. Импортированные
|
||
файлы уходят из каталога так же, как из `inbox/`.
|
||
|
||
Их открываем группе на запись, плюс setgid (`2` в начале режима) — чтобы файлы, положенные вами по
|
||
SFTP/rsync, наследовали группу `telewave`, а не вашу личную, и приложение их видело:
|
||
|
||
```bash
|
||
sudo chmod 2775 /srv/telewave/media/inbox /srv/telewave/media/manual
|
||
```
|
||
|
||
Забрать **файл** из этих каталогов приложение сможет в любом случае: удаление зависит от прав на
|
||
каталог (владелец — 1654), а не на сам файл, поэтому чужой umask у ваших загрузок импорту не мешает.
|
||
|
||
А вот **вложенные папки** — мешают, и одним `chmod` это не закрыть. В `manual/` обычно кладут не
|
||
файлы, а каталог раздачи целиком. Владельцем такого каталога станете вы, режим ему выставит umask
|
||
клиента (у SFTP это обычно 755), и группе достанется `r-x` — приложение не сможет ни вынести файл
|
||
в `originals/` при импорте, ни подчистить спутники и опустевший каталог. Права на новое внутри
|
||
задаются дефолтными ACL — они действуют независимо от того, каким клиентом и с каким umask файлы
|
||
приехали:
|
||
|
||
```bash
|
||
sudo apt install -y acl
|
||
sudo setfacl -R -m g:telewave:rwx -m d:g:telewave:rwx \
|
||
/srv/telewave/media/inbox /srv/telewave/media/manual
|
||
```
|
||
|
||
Проверить — `getfacl /srv/telewave/media/manual`: нужны строки `group:telewave:rwx` и
|
||
`default:group:telewave:rwx`. Без ACL альтернатива только клиентская (принудительные права `775`
|
||
на загрузку в WinSCP, umask 002 у rsync) — держится на настройке каждого, кто заливает файлы,
|
||
и ломается при первой же загрузке другим инструментом.
|
||
|
||
**Обновление существующей установки.** Если хранилище было заведено раньше, когда контейнер работал
|
||
под root, — те же команды и есть вся миграция: выполните их на остановленном контейнере
|
||
(`docker compose down`), затем поднимайте новый образ. До смены владельца приложение стартует, но
|
||
любая запись в `/media` будет падать с `Permission denied`.
|
||
|
||
---
|
||
|
||
## 6. Проброс в контейнер (bind mount)
|
||
|
||
В `docker-compose.yml`, в сервис `app`, добавить том (этого пока **нет** в репозитории — появится
|
||
вместе с реализацией фичи, здесь для справки):
|
||
|
||
```yaml
|
||
services:
|
||
app:
|
||
# ... существующая конфигурация ...
|
||
volumes:
|
||
- /srv/telewave/media:/media
|
||
mem_limit: 3g
|
||
```
|
||
|
||
И переменные окружения в `.env` (см. `docs/media-storage-and-streaming.md`):
|
||
|
||
```
|
||
Storage__RootPath=/media
|
||
```
|
||
|
||
Выбран **bind mount**: диск смонтирован в ОС через fstab (шаги 1–5) и виден всегда, независимо от
|
||
Docker — это важно, потому что файлы в `inbox/` кладутся вручную по SFTP/rsync, в том числе когда
|
||
контейнер остановлен.
|
||
|
||
> **Альтернатива — named volume, монтируемый самим Docker** (не используем, для справки). Позволяет
|
||
> пропустить шаги 3–5, но диск тогда доступен только при запущенном контейнере, а подкаталоги всё
|
||
> равно надо создавать заранее. Пример:
|
||
>
|
||
> ```yaml
|
||
> services:
|
||
> app:
|
||
> volumes:
|
||
> - media:/media
|
||
> mem_limit: 3g
|
||
> volumes:
|
||
> media:
|
||
> driver: local
|
||
> driver_opts:
|
||
> type: ext4
|
||
> device: /dev/disk/by-uuid/ВАШ-UUID
|
||
> o: noatime
|
||
> ```
|
||
>
|
||
> Разметка и `mkfs.ext4` (шаги 1–2) обязательны в любом случае.
|
||
|
||
---
|
||
|
||
## 7. Проверка записи из контейнера
|
||
|
||
После добавления тома и пересборки образа убедиться, что контейнер работает под нужным uid и пишет
|
||
на диск:
|
||
|
||
```bash
|
||
docker compose exec app id # ожидается uid=1654 gid=1654
|
||
docker compose exec app sh -c 'touch /media/.wtest && ls -l /media/.wtest && rm /media/.wtest'
|
||
```
|
||
|
||
И что вы сами можете класть файлы вручную (под своим пользователем, не через sudo), а приложение —
|
||
убирать созданное вами, в том числе внутри вложенных каталогов:
|
||
|
||
```bash
|
||
touch /srv/telewave/media/inbox/.wtest && rm /srv/telewave/media/inbox/.wtest
|
||
mkdir -p /srv/telewave/media/manual/.wtest && docker compose exec app sh -c 'rmdir /media/manual/.wtest'
|
||
```
|
||
|
||
Все команды должны отработать без ошибок доступа. `Permission denied` в первой — не сделан
|
||
`chown -R 1654:1654` из шага 5; во второй — вы не в группе `telewave` либо не перелогинились после
|
||
`usermod` (в SFTP-клиенте — не переподключили сессию); в третьей — не выставлены дефолтные ACL,
|
||
и приложение не достаёт файлы из ваших подкаталогов. На этом подготовка хоста завершена — дальнейшее (создание `MediaAsset`, нарезка ffmpeg)
|
||
делает уже само приложение.
|
||
|
||
---
|
||
|
||
## 8. Что дальше смотреть в админке
|
||
|
||
Заполненность диска видна в разделе **Админка → Хранилище**: сколько занято на томе целиком,
|
||
сколько из этого приходится на TeleWave, и на что именно оно ушло — сегменты программ и заставок,
|
||
исходники, inbox, изображения. Отдельной строкой считается «прочее под корнем»: если там не ноль,
|
||
значит на томе лежит что-то, чего приложение не создавало.
|
||
|
||
Цифра считается обходом дерева и потому кэшируется на пять минут; кнопка «Пересчитать» заставляет
|
||
пересчитать сразу. Когда свободного места остаётся меньше `Storage:MinFreeSpaceBytes`, страница
|
||
показывает предупреждение — с этого порога сервер начинает отклонять загрузку новых файлов.
|
||
|
||
---
|
||
|
||
## Приложение: если диск нужно расширить в будущем
|
||
|
||
Если VM отдаст диску больше места (например `sdb` вырастет с 700G), после увеличения на стороне
|
||
гипервизора:
|
||
|
||
```bash
|
||
sudo growpart /dev/sdb 1 # расширить раздел на весь диск
|
||
sudo resize2fs /dev/sdb1 # расширить ext4 (можно на смонтированном)
|
||
df -h /srv/telewave/media
|
||
```
|