Files
pipu-music/docs/install-guide.md
T
mrleo1nidandClaude Opus 5.5 01319c58d7
ci / api (push) Successful in 50s
ci / recommender (push) Successful in 37s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Successful in 44s
release / android + release (push) Failing after 53s
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 45s
ci / ts (packages + web) (push) Successful in 37s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Successful in 1m30s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 52s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Successful in 28s
ci / mobile (Metro-бандл Android) (push) Successful in 49s
release / verify (push) Successful in 1m14s
build(deploy): build images on the server instead of publishing to a registry
- deploy/compose.yaml: every app service has `build:` (repo root context),
  local tags pipu-music-<service>:<version>
- deploy/update.sh: git fetch → checkout tag (latest release by default,
  or vX.Y.Z / main) → docker compose build --pull → up -d → prune → /readyz;
  re-executes from a temp copy because checkout may replace the script
- install.sh / .env.example: no REGISTRY, PIPU_VERSION set by update.sh,
  .env owned by the invoking user
- release.yml: drop image publishing; release ships APK + sha256 only
- ci.yml: new `docker` job builds all images without push
- docs: install guide uses git clone, deployment/ci-cd updated, ADR-013

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

294 lines
15 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.
# Установка сервера и подключение приложений
Пошаговая инструкция: от чистого Linux-сервера до работающего Pipu Music на своём домене
с HTTPS, плюс подключение веб- и Android-клиента. Справочник по устройству развёртывания —
[deployment.md](deployment.md).
В примерах:
- домен — `music.example.com`, замените на свой;
- репозиторий — `gitea.hsrv.site/mrleo1nid/pipu-music`;
- каталог установки — `/opt/pipu-music`.
Готовые Docker-образы не публикуются: сервер клонирует репозиторий и собирает образы сам
(`docker compose build`). Релизы в Gitea содержат только APK для Android.
> **Что умеет текущая версия.** В `v0.1.0` (этап M0) работают развёртывание, HTTPS и проверка связи
> клиентов с сервером. Вход, библиотека и плеер появятся в следующих версиях
> (см. [roadmap.md](roadmap.md)). Шаги, которые зависят от версии, помечены.
---
## 1. Что нужно заранее
| Что | Требование |
|---|---|
| Сервер | Linux x86_64 (Debian 12 / Ubuntu 22.04+), 4+ ядра, 8+ ГБ RAM, 30+ ГБ на диске (система, образы, кэш сборки, данные) |
| Доступ | root или пользователь с `sudo` |
| Домен | Поддомен, которым вы управляете, например `music.example.com` |
| Сеть | Входящие **80/tcp** и **443/tcp** (+ **443/udp** для HTTP/3) из интернета на сервер |
| Музыка | NFS-сервер с библиотекой (подключается позже из админки) |
| Интернет на сервере | Нужен при сборке: скачиваются базовые образы и зависимости Go / npm / Python |
| Gitea | Доступ к репозиторию на чтение (если он приватный — токен или SSH-ключ) |
---
## 2. DNS
Создайте у регистратора или в DNS-панели запись:
| Тип | Имя | Значение |
|---|---|---|
| `A` | `music` | публичный IPv4 сервера |
| `AAAA` | `music` | публичный IPv6 (если есть; если нет — запись не создавайте) |
Если сервер стоит дома за роутером, пробросьте на роутере порты 80 и 443 на внутренний IP сервера.
Проверка — команда должна вернуть IP вашего сервера:
```bash
dig +short music.example.com
```
> Сертификат Let's Encrypt выдаётся, только когда домен уже указывает на сервер и порт 80 доступен из интернета.
> Не запускайте шаг 6, пока эта проверка не проходит.
---
## 3. Docker
```bash
curl -fsSL https://get.docker.com | sudo sh
```
Добавьте своего пользователя в группу `docker`, чтобы запускать сборку без `sudo` (после этого перелогиньтесь):
```bash
sudo usermod -aG docker "$USER"
```
И `git`, если его нет:
```bash
sudo apt-get install -y git
```
Проверьте, что есть Docker Compose v2.24 или новее (нужен для `compose.tls.yaml`):
```bash
docker compose version
```
Откройте порты в файрволе, если он включён (пример для `ufw`):
```bash
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw allow 443/udp
```
---
## 4. Клонирование репозитория
```bash
sudo mkdir -p /opt/pipu-music && sudo chown "$USER" /opt/pipu-music
```
```bash
git clone https://gitea.hsrv.site/mrleo1nid/pipu-music.git /opt/pipu-music && cd /opt/pipu-music/deploy
```
> Если репозиторий приватный: клонируйте по SSH (`git@gitea.hsrv.site:mrleo1nid/pipu-music.git`) с ключом,
> добавленным в Gitea, или по HTTPS с токеном (право `read:repository`) — Git спросит логин и токен вместо пароля.
> Сохраните доступ (`git config credential.helper store` или SSH-ключ): `update.sh` делает `git fetch` при каждом обновлении.
Все дальнейшие команды выполняются из `/opt/pipu-music/deploy`.
---
## 5. Подготовка сервера
```bash
sudo ./install.sh
```
Скрипт:
- создаёт каталоги данных в `/srv/pipu` (БД, кэши, загрузки, сертификаты);
- делает `/srv/pipu/library` точкой монтирования с shared propagation, чтобы NFS-хранилища, которые потом
подключит админ, были видны всем контейнерам, и ставит systemd-юнит, повторяющий это после перезагрузки;
- создаёт `.env` со случайными паролями БД, ключом поиска и межсервисным токеном.
Заполните в `/opt/pipu-music/deploy/.env`:
```ini
COMPOSE_FILE=compose.yaml:compose.tls.yaml
DOMAIN=music.example.com
ACME_EMAIL=you@example.com
```
`ACME_EMAIL` — адрес, на который Let's Encrypt пишет, если с сертификатом что-то не так.
Остальные значения менять не нужно; `PIPU_VERSION` проставляет `update.sh`.
`.env` не хранится в git (он в `.gitignore`), поэтому обновления репозитория его не трогают.
---
## 6. Сборка и запуск
```bash
./update.sh
```
Скрипт переключает репозиторий на последний релизный тег (например, `v0.1.1`), собирает образы, запускает
все сервисы и ждёт готовности API (до 2 минут). Первая сборка занимает 5–15 минут, следующие — быстрее
за счёт кэша Docker. Конкретная версия: `./update.sh v0.1.1`.
> Локальная сборка поддерживается начиная с `v0.1.1`: в теге `v0.1.0` `compose.yaml` ещё ссылается на реестр образов.
> Пока `v0.1.1` не вышел, запускайте `./update.sh main`.
При первом запуске Caddy сам получит сертификат для `DOMAIN`, обычно за 10–30 секунд.
Проверка с любого компьютера:
```bash
curl -s https://music.example.com/api/v1/server-info
```
Ожидаемый ответ: `{"name":"pipu-music","version":"0.1.0",...}`. Проверка всех зависимостей:
```bash
curl -s https://music.example.com/readyz
```
Должно вернуться `"status":"ok"` и `ok` для `postgres` и `redis`.
---
## 7. Веб-клиент
Откройте `https://music.example.com` в браузере.
- **v0.1.0:** карточка «Сервер 0.1.0 на связи» и переключатель языка RU / EN.
- **С версии 0.2 (M1):** экран входа. Первый вход — логин `admin` и пароль, который сервер один раз
выводит в лог при первом запуске:
```bash
docker compose logs api | grep -i bootstrap
```
Сразу после входа приложение попросит сменить логин и пароль администратора
([auth-and-roles.md](auth-and-roles.md#первый-запуск-bootstrap)). Чтобы задать пароль заранее, впишите
`BOOTSTRAP_ADMIN_PASSWORD` в `.env` **до** первого запуска.
- **С версии 0.3 (M2):** Админка → Хранилища → «Добавить хранилище» → адрес NFS-сервера и путь экспорта
→ «Проверить» → шаблоны путей → «Сохранить и пересканировать» ([library-and-storage.md](library-and-storage.md)).
---
## 8. Android-приложение
1. На телефоне откройте страницу релизов
`https://gitea.hsrv.site/mrleo1nid/pipu-music/releases` и скачайте `pipu-music-<версия>.apk`.
2. Откройте файл. Android попросит разрешить установку из этого источника (браузера или файлового менеджера) —
разрешите и вернитесь к установке.
3. Запустите **Pipu Music**, на экране подключения введите адрес сервера: `music.example.com`
(`https://` подставится сам) → «Проверить».
4. Ожидаемый результат — «Сервер 0.1.0 на связи». Если версия приложения слишком старая для сервера,
приложение попросит обновиться.
Дальше приложение обновляется само: с версии M5 оно проверяет новые релизы через ваш сервер
и предлагает обновиться ([mobile.md](mobile.md#самообновление)). Для этого в `.env` сервера нужно заполнить
`GITEA_URL`, `GITEA_REPO` и `GITEA_TOKEN` (токен только на чтение).
> Приложению нужен настоящий сертификат. С самоподписанным сертификатом и голым IP-адресом подключиться не получится.
> Поэтому нужен домен с Let's Encrypt, как в этой инструкции.
---
## 9. Вариант: свой reverse proxy вместо Caddy
Если на сервере уже есть Traefik, Nginx Proxy Manager или свой nginx с сертификатами:
1. В `.env` закомментируйте `COMPOSE_FILE` и задайте порт, на котором Pipu будет слушать только локально:
```ini
#COMPOSE_FILE=compose.yaml:compose.tls.yaml
HTTP_PORT=127.0.0.1:8081
```
2. Направьте прокси для `music.example.com` на `http://127.0.0.1:8081` с такими настройками:
- передавать заголовки `Host`, `X-Forwarded-For`, `X-Forwarded-Proto`;
- лимит тела запроса не меньше **16 МБ** (загрузка треков идёт чанками по 8 МБ);
- **без буферизации ответов** (стриминг аудио), длинные таймауты чтения (≥ 1 ч).
Пример для nginx:
```nginx
server {
listen 443 ssl http2;
server_name music.example.com;
# ssl_certificate / ssl_certificate_key — как у остальных ваших сайтов
client_max_body_size 16m;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 1h;
}
}
```
3. Запуск и проверки — как в шагах 6–8.
Если прокси работает на другой машине, укажите в `HTTP_PORT` внутренний адрес сервера (`192.168.1.10:8081`) и
закройте этот порт от интернета файрволом.
---
## 10. Обновление, бэкапы, удаление
Обновление до последнего релиза:
```bash
cd /opt/pipu-music/deploy && ./update.sh
```
Конкретная версия или откат: `./update.sh v0.2.0`. Скрипт делает `git fetch`, переключается на тег,
пересобирает образы и перезапускает сервисы; старые образы удаляются. Миграции БД применяются
автоматически при старте API.
Бэкап базы (всё остальное восстанавливается само — см. [deployment.md](deployment.md#бэкапы)):
```bash
docker compose exec -T postgres pg_dump -U pipu -Fc pipu > /srv/pipu/backups/pipu-$(date +%F).dump
```
Остановить сервис, сохранив данные:
```bash
cd /opt/pipu-music/deploy && docker compose down
```
---
## 11. Если что-то не работает
| Симптом | Что проверить |
|---|---|
| Сертификат не выдаётся, браузер ругается на HTTPS | `dig +short music.example.com` указывает на сервер; порт 80 открыт снаружи; `docker compose logs caddy` |
| `502 Bad Gateway` | API ещё запускается или упал: `docker compose ps`, `docker compose logs api` |
| `/readyz` отвечает `degraded` | Какая зависимость не `ok` — её логи: `docker compose logs postgres` / `redis` |
| `update.sh`: ошибка `git fetch` | Нет доступа к репозиторию: проверьте SSH-ключ или сохранённый токен (`git fetch` вручную) |
| Сборка падает на скачивании зависимостей | Нет интернета или DNS на сервере; повторите `./update.sh` — уже собранные слои берутся из кэша |
| Сборка падает: «no space left on device» | Очистите кэш сборки: `docker builder prune` |
| `permission denied … docker.sock` | Пользователь не в группе `docker` (шаг 3) или вы не перелогинились |
| `storage-agent` не стартует, ошибка про propagation / shared mount | Ещё раз `sudo ./install.sh`; на хосте `findmnt -o TARGET,PROPAGATION /srv/pipu/library` должно показать `shared` |
| Android: «Сервер не отвечает» | Тот же адрес открывается в браузере телефона; в адресе нет опечатки; телефон в сети с доступом к серверу |
| Android: «Эта версия приложения слишком старая» | Установите APK из последнего релиза |
Логи всех сервисов сразу:
```bash
cd /opt/pipu-music/deploy && docker compose logs -f --tail=100
```