A backup container dumps the database every night, keeps 14 daily and 8 weekly copies and once a week restores a copy into a scratch database to check tables and row counts. update.sh takes a copy before updating, restore.sh brings one back and keeps the old database aside. CI runs the dump, verify and restore cycle on the database the end-to-end journey filled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 KiB
Развёртывание
Пошаговая инструкция «с нуля до работающего сервера на домене» — install-guide.md. Здесь — справочник: требования, каталоги, сервисы, переменные.
Требования к серверу
- Linux x86_64 (нужен mount propagation для storage-agent), Docker 24+, Docker Compose v2.
- 4+ ядра, 8+ ГБ RAM (аудиоанализ при первом запуске ест CPU; TensorFlow-модели ~1–2 ГБ RAM).
- Диск: ~5 ГБ под систему и БД + кэш транскодов (по умолчанию 50 ГБ) + обложки (~1 ГБ на 10k альбомов).
- Сетевой доступ к NFS-серверам.
Каталоги на хосте
/srv/pipu/
├── library/ # точки монтирования источников (storage-agent, rshared)
├── library-host/ # папка для type=local по умолчанию; обычно вместо неё LIBRARY_HOST=/mnt/music
├── cache/
│ ├── transcode/
│ └── covers/
├── postgres/
├── meili/
├── redis/
├── models/ # модели Essentia (скачиваются при первом старте analyzer)
├── updates/ # кэш APK релизов
├── uploads/ # staging очереди на добавление (размер ≈ лимит очереди, по умолчанию 10 ГБ)
├── backups/
└── caddy/ # сертификаты Let's Encrypt (если используется compose.tls.yaml)
Музыка на локальном диске сервера подключается через LIBRARY_HOST в .env (например, /mnt/music):
каталог хоста монтируется во все сервисы, включая storage-agent, только на чтение (запись появится вместе
с импортом одобренных загрузок), внутри контейнеров он виден как /library-host.
Перед первым запуском: mkdir -p /srv/pipu/library && mount --bind /srv/pipu/library /srv/pipu/library && mount --make-rshared /srv/pipu/library
(делает deploy/install.sh, плюс systemd-unit для повторения после перезагрузки).
deploy/compose.yaml
Актуальная конфигурация — сам файл deploy/compose.yaml; здесь — как он устроен.
- Образы собираются на сервере из клонированного репозитория (
build:у каждого сервиса, контекст — корень репо), в реестр ничего не публикуется. Теги локальные:pipu-music-<сервис>:<версия>, версия —git describeтекущего тега (её проставляетupdate.shвPIPU_VERSION).
| Сервис | Собирается из | Особенности |
|---|---|---|
nginx |
apps/web/Dockerfile |
SPA + конфиг nginx; единственный открытый порт (HTTP_PORT) или за Caddy |
api |
services/api/Dockerfile, target api |
Миграции при старте |
storage-agent |
services/api/Dockerfile, target storage-agent |
CAP_SYS_ADMIN, apparmor:unconfined, библиотека с propagation: rshared |
recommender, analyzer |
services/recommender/Dockerfile (один образ) |
Команды serve и analyze --workers 4 |
postgres |
pgvector/pgvector:0.8.7-pg17 |
Healthcheck pg_isready |
redis |
redis:7.4-alpine |
AOF |
meilisearch |
getmeili/meilisearch:v1.11 |
|
caddy (из compose.tls.yaml) |
caddy:2.10-alpine |
HTTPS на DOMAIN, nginx тогда без открытых портов |
- Библиотека монтируется в
nginx,api,analyzerтолько на чтение сpropagation: rslave. - Наружу открыт только
nginx(илиcaddy). Все остальные — во внутренней сети compose.
Конфигурация (.env)
| Переменная | Пример / по умолчанию | Описание |
|---|---|---|
PIPU_VERSION |
проставляет update.sh |
Версия собранных образов (git describe) |
PUBLIC_URL |
https://music.home.lan |
Внешний адрес |
DATABASE_URL |
postgres://pipu:…@postgres:5432/pipu |
|
REDIS_URL |
redis://redis:6379/0 |
|
MEILI_URL, MEILI_MASTER_KEY |
||
JWT_PRIVATE_KEY |
base64 от 32 байт (seed Ed25519) | Подпись access-токенов. Создают install.sh / update.sh, если его нет; пусто — случайный ключ до перезапуска |
JWT_PREVIOUS_KEYS |
пусто | Прежние ключи через запятую — токены, подписанные ими, ещё принимаются (ротация) |
ACCESS_TOKEN_TTL |
15m |
Срок access-токена |
COOKIE_SECURE |
true |
Флаг Secure у refresh-cookie; false — только для разработки по http не на localhost |
STREAM_SIGNING_KEY |
32 байта base64 | HMAC подписанных ссылок |
INTERNAL_TOKEN |
случайный | Межсервисная аутентификация |
BOOTSTRAP_ADMIN_USERNAME |
admin |
|
BOOTSTRAP_ADMIN_PASSWORD |
пусто → сгенерировать и вывести в лог | |
TRANSCODE_CACHE_LIMIT_GB |
50 |
|
TRANSCODE_CONCURRENCY |
2 |
|
GITEA_URL, GITEA_REPO, GITEA_TOKEN |
https://git.home.lan, owner/pipu-music, read-only токен |
Для самообновления Android |
PIPU_FILE_UID, PIPU_FILE_GID |
1000 |
Владелец файлов, которые агент записывает в принимающие хранилища |
UPLOADS_DIR |
/uploads |
Staging очереди на добавление (лимит очереди задаётся в админке) |
LOG_LEVEL |
info |
TLS
- Свой домен (рекомендуется) —
deploy/compose.tls.yamlдобавляет Caddy, который получает и продлевает сертификат Let's Encrypt дляDOMAINи проксирует на nginx; nginx тогда наружу не открыт. Включается строкойCOMPOSE_FILE=compose.yaml:compose.tls.yamlв.env. - Свой reverse proxy (Traefik, NPM, nginx) — без
compose.tls.yaml, прокси направляется наHTTP_PORT(например127.0.0.1:8081); нужныX-Forwarded-*, тело до 16 МБ, без буферизации ответов. - nginx внутри образа
webберёт схему и IP клиента изX-Forwarded-Proto/X-Forwarded-For, если запрос пришёл из частной сети (docker, LAN). - Локальная сеть без домена — свой CA; сертификат CA нужно установить на устройства или включить пиннинг в мобильном (см. mobile.md).
Пошаговая установка на домен — install-guide.md.
Бэкапы
- Контейнер
backup(deploy/backup.sh loop): каждый день вBACKUP_HOUR:30 UTC (по умолчанию 3:30) —pg_dump -Fc→/srv/pipu/backups/daily/, хранитсяBACKUP_KEEP_DAILY= 14 последних; первая копия за неделю копируется вweekly/(BACKUP_KEEP_WEEKLY= 8). Рядом с копией —*.counts: число строк в каждой таблице. - Раз в неделю копия проверяется восстановлением: разворачивается во временную базу
pipu_verify, сверяются таблицы и число строк, база удаляется; время последней успешной проверки —backups/.last-verify, ошибки — вdocker compose logs backup(«ПРОВЕРКА НЕ ПРОЙДЕНА»). Тот же цикл копия → проверка → восстановление выполняется в CI (ci / db) на базе после сквозного сценария. update.shперед обновлением делает внеочередную копию.- Вручную:
docker compose run --rm backup once— копия сейчас;docker compose run --rm backup verify /backups/daily/<файл>. - Восстановление:
./restore.sh— список копий,./restore.sh daily/pipu-….dump— останавливает api, рекомендации и анализ, проверяет копию, переименовывает текущую базу вpipu_before_restore_<время>(страховка; удалить —docker compose exec postgres dropdb -U pipu <имя>), восстанавливает и запускает сервисы; поиск переиндексируется при старте api. Redis (сессии волны, очереди) не восстанавливается — пользователи просто начнут новую волну. - Копии лежат на том же сервере: для защиты от потери диска синхронизируйте
/srv/pipu/backupsна другую машину (например,rclone syncилиrsyncпо cron). - Meilisearch, кэши и модели не бэкапятся — восстанавливаются (
reindex, повторный транскод / анализ). - Признаки анализа хранятся в Postgres — после восстановления из бэкапа повторный многочасовой анализ не нужен.
- Сама музыка — на NFS, вне зоны ответственности приложения.
Обновление
cd /opt/pipu-music/deploy && ./update.sh # последний релизный тег
Скрипт: git fetch → checkout тега → docker compose build --pull → up -d → удаление старых образов →
ожидание /readyz; миграции применяются при старте api.
Конкретная версия или откат: ./update.sh v1.2.4 (миграции пишутся обратно-совместимыми в пределах минорной версии: сначала добавить,
потом — в следующем релизе — удалить).