From 047a3a7e15b8739830a97a3996adde341aebec71 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 19 Jun 2026 20:12:56 +0300 Subject: [PATCH] =?UTF-8?q?deploy:=20compose.truenas.yml=20+=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=20SCALE/=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D1=83-?= =?UTF-8?q?=D0=BD=D0=B0-NAS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - compose.truenas.yml: готовый host-path compose (build на NAS / или image), env с JWT_SECRET/CLIENT_ORIGIN, healthcheck start_period 40s (запас на первичные миграции). - DEPLOY-TRUENAS.md: переписан под реальный кейс (TrueNAS SCALE, сборка образа на NAS, без Docker на ПК): датасет → код по SMB → docker build → правка JWT/пути → docker compose up / Custom App → проверка → домен/HTTPS/бэкапы. PC-сборка и CORE — в конце. Co-Authored-By: Claude Opus 4.8 (1M context) --- DEPLOY-TRUENAS.md | 179 ++++++++++++++++++++++---------------------- compose.truenas.yml | 41 ++++++++++ 2 files changed, 130 insertions(+), 90 deletions(-) create mode 100644 compose.truenas.yml diff --git a/DEPLOY-TRUENAS.md b/DEPLOY-TRUENAS.md index 43695d1..fbaaa68 100644 --- a/DEPLOY-TRUENAS.md +++ b/DEPLOY-TRUENAS.md @@ -1,118 +1,117 @@ -# Запуск LearnSpace на TrueNAS +# Установка LearnSpace на TrueNAS SCALE -Коротко: **TrueNAS SCALE** (Linux) запускает приложение как **Docker-контейнер** — в репозитории уже -есть `Dockerfile` и `docker-compose.yml`. **TrueNAS CORE** (FreeBSD) Docker не умеет — там нужен Linux-VM -или jail с Node (см. конец). Ниже — путь для **SCALE** (рекомендуемый). +Пошаговая инструкция под **твой случай: TrueNAS SCALE, сборка образа прямо на NAS** +(на рабочем ПК Docker не нужен). В репозитории уже есть `Dockerfile`, `docker-entrypoint.sh` +и готовый `compose.truenas.yml`. -Контейнер **самоинициализируется**: при старте применяет миграции БД и при первом запуске засевает права -(`docker-entrypoint.sh`). Данные (SQLite-БД, загрузки, бэкапы) хранятся в томах, переживают пересоздание. +Контейнер **самоинициализируется**: при старте применяет миграции БД, при первом запуске засевает права. +Данные (SQLite-БД, загрузки, бэкапы) лежат на датасете и переживают пересоздание/обновление контейнера. + +> CORE (FreeBSD) Docker не умеет — см. раздел в конце. Если у тебя CORE — скажи, распишу отдельно. --- -## 1. Подготовка хранилища (dataset) +## Шаг 1. Датасет под данные -В TrueNAS создайте датасет под приложение и в нём подпапки для постоянных данных: +TrueNAS → **Datasets → Add Dataset**: создай `tank/apps/learnspace` (вместо `tank` — имя своего пула). +Затем создай в нём три папки (через **System → Shell** или по SMB): +```sh +mkdir -p /mnt/tank/apps/learnspace/{data,uploads,backups} +``` +Здесь будут жить БД, загрузки и бэкапы. Их защищают снапшоты/репликация TrueNAS. -``` -Datasets → Add Dataset: tank/apps/learnspace -``` -Внутри (через Shell или по SMB) создайте три папки — БД, загрузки, бэкапы: -``` -/mnt/tank/apps/learnspace/data -/mnt/tank/apps/learnspace/uploads -/mnt/tank/apps/learnspace/backups -``` -Они монтируются в контейнер; их защищают снапшоты/репликация TrueNAS. +## Шаг 2. Положить код проекта на NAS -## 2. Доставить образ на NAS (3 варианта) +Самое простое — по SMB скопировать папку репозитория в датасет, например в +`/mnt/tank/apps/learnspace/src` (нужны `Dockerfile`, `compose.truenas.yml`, `backend/`, `frontend/`, `js/`, +`docker-entrypoint.sh`). Папку `node_modules` копировать НЕ нужно — образ ставит зависимости сам. -**A. Собрать на машине с Docker и перенести** (без реестра): -```bash -# на ПК с Docker, в корне репозитория: +Либо, если у NAS есть доступ к git-серверу: +```sh +cd /mnt/tank/apps/learnspace +git clone <адрес-репозитория> src +``` + +## Шаг 3. Включить SSH и собрать образ + +System → **Services → SSH** → включить. Зайти в шелл NAS (ssh root@IP_NAS) и собрать: +```sh +cd /mnt/tank/apps/learnspace/src docker build -t learnspace:latest . -docker save learnspace:latest | gzip > learnspace.tar.gz -# скопировать learnspace.tar.gz на NAS (SMB/scp), затем в Shell TrueNAS: -docker load < /mnt/tank/apps/learnspace/learnspace.tar.gz +``` +(Сборка скачает зависимости и займёт пару минут.) + +## Шаг 4. Настроить параметры запуска + +Открой `compose.truenas.yml` (в папке src) и поправь **три вещи**: +1. Пути `/mnt/tank/apps/learnspace/...` — под свой пул, если он не `tank`. +2. `JWT_SECRET` — длинная случайная строка. Сгенерируй прямо на NAS: + ```sh + openssl rand -hex 32 + ``` + и вставь значение. +3. `CLIENT_ORIGIN` — адрес, по которому будешь открывать сайт (пока `http://IP_NAS:3000`, позже домен с https). + +## Шаг 5. Запустить + +**Способ 1 — командой (просто):** +```sh +cd /mnt/tank/apps/learnspace/src +docker compose -f compose.truenas.yml up -d ``` -**B. Через реестр** (Docker Hub / GHCR / локальный): `docker push /learnspace:latest`, -а в compose указать этот `image:`. +**Способ 2 — через UI TrueNAS (интегрированно, app виден в Apps):** +Apps → **Discover Apps → Custom App → Install via YAML** → вставь содержимое `compose.truenas.yml` +(этот способ — для SCALE 24.10 «ElectricEel» и новее; на 24.04 «Dragonfish» и старше используй Способ 1). -**C. Собрать прямо на NAS** (SCALE ElectricEel 24.10+ имеет Docker CLI): склонировать репозиторий в -датасет и `docker compose build` (см. ниже). +Первый старт сам накатит миграции и засеет права — отдельных команд не нужно. -## 3. Развернуть как Custom App (Compose YAML) +## Шаг 6. Проверить -TrueNAS SCALE → **Apps → Discover Apps → Custom App → Install via YAML** (на ElectricEel+ это нативный -docker-compose). Вставьте (правьте пути пула, порт и **обязательно JWT_SECRET**): - -```yaml -services: - app: - image: learnspace:latest # из шага 2 (или build: . если собираете на NAS) - container_name: learnspace - restart: unless-stopped - ports: - - "3000:3000" # снаружи:внутри. Поменяйте левую часть при конфликте - volumes: - - /mnt/tank/apps/learnspace/data:/app/backend/data - - /mnt/tank/apps/learnspace/uploads:/app/backend/uploads - - /mnt/tank/apps/learnspace/backups:/app/backups - environment: - - NODE_ENV=production - - PORT=3000 - - JWT_SECRET=ЗАМЕНИТЕ_на_длинную_случайную_строку # обязательно! напр. `openssl rand -hex 32` - - JWT_EXPIRES_IN=7d - - DB_PATH=/app/backend/data/learnspace.db - - UPLOADS_DIR=/app/backend/uploads - - CLIENT_ORIGIN=https://learn.example.com # публичный адрес (для CORS); или http://:3000 - # необязательно — TURN для видеосвязи за NAT (классы/доска): - # - TURN_URL=turn:turn.example.com:3478 - # - TURN_USER=... - # - TURN_PASS=... - # необязательно — LLM «Спроси» (Groq и т.п.): - # - ASSISTANT_LLM_URL=https://api.groq.com/openai/v1/chat/completions - # - ASSISTANT_LLM_KEY=... - # - ASSISTANT_LLM_MODEL=llama-3.3-70b-versatile +```sh +docker logs -f learnspace ``` +Ожидаемо: `applying migrations...` → (на первом старте) `seeding permissions...` → `starting server...` +→ `Server running on port 3000`. Через ~30–40 с healthcheck станет «healthy». -Сгенерировать секрет: в Shell TrueNAS `openssl rand -hex 32`. +Открой в браузере **http://IP_NAS:3000** и зарегистрируй первого пользователя. +(Нужен сразу админ — скажи, добавлю команду/скрипт создания админа.) -## 4. Первый запуск +## Шаг 7. Домен и HTTPS (рекомендуется) -При старте entrypoint сам накатит миграции и засеет права — отдельных команд не нужно. Проверьте: -- **Apps → learnspace → Logs**: должно быть `applying migrations...` → `starting server...` → `Server running on port 3000`. -- Healthcheck (`/api/health`) станет «healthy» через ~10–30 с. +Порт 3000 — внутренний. Для домена с HTTPS поставь reverse-proxy (TrueNAS app **Nginx Proxy Manager** +или **Traefik**, либо внешний nginx) → проксировать на `IP_NAS:3000`. В `CLIENT_ORIGIN` укажи итоговый +публичный URL (иначе CORS заблокирует фронт). Для видеосвязи в классах за CGNAT/симметричным NAT нужен +TURN-сервер (coturn) — пропиши `TURN_URL/TURN_USER/TURN_PASS`. -Откройте `http://:3000`. Создайте первого пользователя через регистрацию (или заведите админа — -скажите, добавлю команду/скрипт сидинга админа). +## Обновление версии -## 5. HTTPS и домен (рекомендуется) +1. Обнови код в `src` (git pull или перекопируй). +2. Пересобери и перезапусти: + ```sh + cd /mnt/tank/apps/learnspace/src + docker compose -f compose.truenas.yml up -d --build + ``` + Данные сохранятся (тома на датасете), новые миграции применятся автоматически. -Порт 3000 — внутренний. Для доступа по домену с HTTPS поставьте reverse-proxy: -- TrueNAS app **Traefik**/**Nginx Proxy Manager**, либо внешний nginx → проксировать на `:3000`. -- В `CLIENT_ORIGIN` укажите итоговый публичный URL (иначе CORS заблокирует фронт). -- Для классов/доски (WebRTC) за CGNAT/симметричным NAT нужен TURN-сервер (coturn) — иначе p2p может не подняться. +## Бэкапы -## 6. Обновление версии - -1. Пересоберите образ новой версии (шаг 2) с тем же тегом или новым. -2. Apps → learnspace → **Edit** (обновите `image:` если тег сменился) → **Update**, либо пересоздайте app. -3. Данные сохранятся (тома на датасете). Миграции применятся автоматически при старте. - -## 7. Бэкапы - -Данные в `/mnt/tank/apps/learnspace/{data,uploads}`. Настройте **снапшоты датасета** + при желании -**репликацию**. SQLite-файл — `data/learnspace.db` (консистентный бэкап лучше делать при остановленном app -или через снапшот ZFS). +Данные — в `/mnt/tank/apps/learnspace/{data,uploads}`. Настрой **снапшоты датасета** (+ репликацию при желании). +SQLite-файл — `data/learnspace.db`; консистентный бэкап лучше делать снапшотом ZFS или при остановленном app. --- +## Альтернатива: собрать на ПК (если поставишь Docker Desktop) + +```bash +docker build -t learnspace:latest . +docker save learnspace:latest | gzip > learnspace.tar.gz # перенести файл на NAS +``` +На NAS: `docker load < learnspace.tar.gz`, затем в `compose.truenas.yml` заменить `build: .` на +`image: learnspace:latest` и выполнить Шаги 4–6. + ## TrueNAS CORE (FreeBSD) — без Docker -CORE не запускает Docker-контейнеры. Варианты: -- **Linux-VM** в TrueNAS CORE (если включён bhyve) → внутри VM поставить Docker и следовать инструкции SCALE выше. -- **Jail** с Node 22+: установить node, `npm ci --omit=dev` в `backend/`, прописать env, `npm run migrate && - npm run seed:permissions`, запускать `node src/server.js` под supervisor (pm2/rc.d). node:sqlite требует Node ≥ 22. - -> Какой у вас TrueNAS (SCALE или CORE) и версия — подскажу точные шаги под конкретный вариант. +CORE контейнеры не запускает. Варианты: **Linux-VM** (bhyve) с Docker внутри → следовать инструкции выше; +либо **jail** с Node ≥ 22: `npm ci --omit=dev` в `backend/`, прописать env, `npm run migrate && +npm run seed:permissions`, запускать `node src/server.js` под supervisor (pm2/rc.d). diff --git a/compose.truenas.yml b/compose.truenas.yml new file mode 100644 index 0000000..ba31247 --- /dev/null +++ b/compose.truenas.yml @@ -0,0 +1,41 @@ +# LearnSpace — деплой на TrueNAS SCALE (host-path тома на датасет). +# Запуск из папки с кодом на NAS: docker compose -f compose.truenas.yml up -d +# ⚠️ Перед запуском поправь: +# 1) пути /mnt/tank/apps/learnspace/... под свой пул/датасет; +# 2) JWT_SECRET — длинная случайная строка (на NAS: openssl rand -hex 32); +# 3) CLIENT_ORIGIN — публичный адрес, по которому открываешь сайт. +services: + app: + build: . # собрать образ из этого репозитория прямо на NAS + # image: learnspace:latest # ← вместо build: ., если образ уже собран/загружен (docker load) + container_name: learnspace + restart: unless-stopped + ports: + - "3000:3000" # внешний:внутренний — поменяй левую часть при конфликте порта + volumes: + - /mnt/tank/apps/learnspace/data:/app/backend/data # SQLite-БД (learnspace.db) + - /mnt/tank/apps/learnspace/uploads:/app/backend/uploads # загрузки (картинки и т.п.) + - /mnt/tank/apps/learnspace/backups:/app/backups # бэкапы + environment: + - NODE_ENV=production + - PORT=3000 + - JWT_SECRET=ЗАМЕНИ_НА_СЛУЧАЙНУЮ_СТРОКУ # openssl rand -hex 32 — обязательно! + - JWT_EXPIRES_IN=7d + - DB_PATH=/app/backend/data/learnspace.db + - UPLOADS_DIR=/app/backend/uploads + - CLIENT_ORIGIN=http://NAS_IP:3000 # публичный URL (для CORS); потом — домен с https + # --- необязательно --- + # TURN для видеосвязи (классы/доска) за NAT: + # - TURN_URL=turn:turn.example.com:3478 + # - TURN_USER=user + # - TURN_PASS=pass + # LLM «Спроси» (Groq и т.п.): + # - ASSISTANT_LLM_URL=https://api.groq.com/openai/v1/chat/completions + # - ASSISTANT_LLM_KEY= + # - ASSISTANT_LLM_MODEL=llama-3.3-70b-versatile + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"] + interval: 30s + timeout: 5s + start_period: 40s # запас на первичные миграции при первом старте + retries: 3