Files
pipu-music/docs/deployment.md
T
mrleo1nidandClaude Opus 5.5 6fe76aea90 fix(deploy): mount the local music folder read-only in storage-agent
Upload import is not implemented yet, so nothing needs write access to
LIBRARY_HOST. Write access will come back together with the import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 05:51:50 +03:00

111 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Развёртывание
> Пошаговая инструкция «с нуля до работающего сервера на домене» — [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`):
каталог хоста монтируется во все сервисы, включая `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` (миграции пишутся обратно-совместимыми в пределах минорной версии: сначала добавить,
потом — в следующем релизе — удалить).