ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / recommender (push) Canceled after 0s
ci / ts (packages + web) (push) Canceled after 0s
Add an install step for mounting the music disk via fstab and wire it into the stack: LIBRARY_HOST in .env points at the host folder, compose mounts it as /library-host (read-only for api, nginx and analyzer; writable only for storage-agent), nginx serves it through X-Accel, and install.sh validates the path and records it in .env. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
8.1 KiB
Markdown
111 lines
8.1 KiB
Markdown
# Развёртывание
|
||
|
||
> Пошаговая инструкция «с нуля до работающего сервера на домене» — [install-guide.md](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`):
|
||
каталог хоста монтируется в `api`, `nginx` и `analyzer` только на чтение, в `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`](../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_FILE` | `/run/secrets/jwt_ed25519.pem` | Генерируется `install.sh` |
|
||
| `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](install-guide.md).
|
||
|
||
## Бэкапы
|
||
|
||
- `pg_dump` ежедневно (cron-контейнер `backup`), хранить 14 дней + еженедельные 8 недель → `/srv/pipu/backups`.
|
||
- Meilisearch, кэши и модели **не бэкапятся** — восстанавливаются (`reindex`, повторный транскод / анализ).
|
||
- Признаки анализа хранятся в Postgres — после восстановления из бэкапа повторный многочасовой анализ не нужен.
|
||
- Сама музыка — на NFS, вне зоны ответственности приложения.
|
||
|
||
## Обновление
|
||
|
||
```bash
|
||
cd /opt/pipu-music/deploy && ./update.sh # последний релизный тег
|
||
```
|
||
Скрипт: `git fetch` → checkout тега → `docker compose build --pull` → `up -d` → удаление старых образов →
|
||
ожидание `/readyz`; миграции применяются при старте api.
|
||
Конкретная версия или откат: `./update.sh v1.2.4` (миграции пишутся обратно-совместимыми в пределах минорной версии: сначала добавить,
|
||
потом — в следующем релизе — удалить).
|