diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index 4ff8a33..7d23dd7 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -92,6 +92,38 @@ jobs: Сборка: ✅ Публикация: ⏭️ + - name: Deploy to Production Server + if: steps.version_check.outputs.changed == 'true' + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + password: ${{ secrets.DEPLOY_PASSWORD }} + script: | + set -e + REGISTRY="dungeonsiege.synology.me/poignatov/play-life" + DEPLOY_PATH="/volume1/docker/play-life" + + echo "🚀 Начинаю деплой на production сервер..." + + # Переходим в директорию проекта + cd $DEPLOY_PATH + + # Логинимся в registry + echo "${{ secrets.GIT_TOKEN }}" | docker login dungeonsiege.synology.me -u ${{ secrets.GIT_USERNAME }} --password-stdin + + # Обновляем образ + echo "📥 Обновляю образ из registry..." + docker pull $REGISTRY:latest + + # Перезапускаем контейнеры + echo "🔄 Перезапускаю контейнеры..." + docker-compose -f docker-compose.prod.yml up -d --force-recreate + + # Проверяем статус + echo "✅ Деплой завершен успешно" + docker-compose -f docker-compose.prod.yml ps + - name: Send Telegram notification (publish success) if: success() && steps.version_check.outputs.changed == 'true' uses: appleboy/telegram-action@master @@ -105,6 +137,22 @@ jobs: Сборка: ✅ Публикация: ✅ + Деплой: ✅ + + - name: Send Telegram notification (deploy failure) + if: failure() && steps.version_check.outputs.changed == 'true' + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_TO }} + token: ${{ secrets.TELEGRAM_TOKEN }} + format: markdown + message: | + *play-life* + _${{ steps.version_check.outputs.commit_message }}_ + + Сборка: ✅ + Публикация: ✅ + Деплой: ❌ - name: Send Telegram notification (failure) if: failure() diff --git a/DEPLOY_SETUP.md b/DEPLOY_SETUP.md new file mode 100644 index 0000000..421f671 --- /dev/null +++ b/DEPLOY_SETUP.md @@ -0,0 +1,474 @@ +# Инструкция по настройке автоматического деплоя на Synology + +## Что было сделано + +✅ Создан `docker-compose.prod.yml` - production конфигурация для Synology +✅ Обновлен `.gitea/workflows/build-and-push.yml` - добавлен автоматический деплой +✅ Настроена автоматическая раскатка после успешного пуша образа в registry + +## Как это работает + +1. При пуше в `main` ветку запускается workflow +2. Если версия в `VERSION` изменилась: + - Собирается Docker образ + - Образ пушится в registry `dungeonsiege.synology.me/poignatov/play-life:latest` + - Автоматически выполняется деплой на Synology сервер +3. Деплой включает: + - Подключение к серверу по SSH + - Логин в registry + - Обновление образа (`docker pull`) + - Перезапуск контейнера через docker-compose + +--- + +## Шаг 1: Настройка Secrets в Gitea + +Перейдите в настройки вашего репозитория в Gitea: **Settings → Secrets** + +Добавьте следующие 3 secrets: + +| Secret | Значение | Описание | +|--------|----------|----------| +| `DEPLOY_HOST` | `192.168.50.55` | IP адрес вашего Synology (или `dungeonsiege.synology.me`) | +| `DEPLOY_USER` | `poignatov` | Ваш SSH пользователь на Synology (замените на свой) | +| `DEPLOY_PASSWORD` | `ваш_пароль` | Пароль от этого пользователя | + +**Как узнать вашего пользователя:** +- Подключитесь к Synology по SSH: `ssh poignatov@192.168.50.55` +- Или посмотрите в DSM: **Control Panel → User & Group** + +**Важно:** Пользователь должен быть в группе `administrators` и иметь доступ к Docker. + +--- + +## Шаг 2: Включение SSH на Synology + +1. Откройте **DSM** (веб-интерфейс Synology) +2. Перейдите в **Control Panel → Terminal & SNMP** +3. Включите **Enable SSH service** +4. Порт по умолчанию: `22` (можно оставить как есть) +5. Нажмите **Apply** + +**Проверка:** +```bash +ssh poignatov@192.168.50.55 +# Должно запросить пароль и подключиться +``` + +--- + +## Шаг 3: Подготовка сервера + +### 3.1. Определить правильный путь и создать директорию + +**Способ 1: Через File Station (определить путь):** + +1. Откройте **File Station** в DSM +2. Создайте папку `play-life` в удобном месте (например, в `docker` или в корне `homes`) +3. **Правый клик** на папке → **Properties** → вкладка **Location** +4. Скопируйте полный путь (например: `/volume1/docker/play-life` или `/volume1/homes/poignatov/docker/play-life`) + +**Способ 2: Через SSH (определить путь):** + +```bash +ssh poignatov@192.168.50.55 + +# Посмотреть доступные volumes +ls -la /volume* + +# Или найти где находятся ваши Docker контейнеры +docker inspect dungeonsiege.synology.me-poignatov-play-life1 | grep -i source +# Это покажет путь к volume (например: /volume1/docker/play-life/uploads) + +# Создать директорию (замените путь на правильный!) +mkdir -p /volume1/docker/play-life +cd /volume1/docker/play-life + +# Или если путь другой, например: +# mkdir -p /volume1/homes/poignatov/docker/play-life +# cd /volume1/homes/poignatov/docker/play-life +``` + +**Проверка пути через существующий контейнер:** + +Если у вас уже запущен контейнер `dungeonsiege.synology.me-poignatov-play-life1`, можно проверить путь через него: + +```bash +ssh poignatov@192.168.50.55 +docker inspect dungeonsiege.synology.me-poignatov-play-life1 | grep -A 5 "Mounts" +``` + +Это покажет путь к volume (например: `/volume1/docker/play-life/uploads`), значит базовый путь будет `/volume1/docker/play-life`. + +**Подтвержденный путь:** `/volume1/docker/play-life` ✅ + +### 3.2. Скопировать docker-compose.prod.yml + +Скопируйте файл `docker-compose.prod.yml` из репозитория на сервер: + +**Вариант 1: Через веб-интерфейс File Station (рекомендуется):** +1. Откройте **File Station** в DSM +2. Перейдите в папку `docker` → `play-life` (путь: `/volume1/docker/play-life/`) +3. Если папки нет, создайте её: **Create** → **Create Folder** → `play-life` +4. Нажмите **Upload** → **Upload Files** +5. Выберите файл `docker-compose.prod.yml` с вашего компьютера +6. Дождитесь завершения загрузки + +**Проверка:** Файл должен быть по пути `/volume1/docker/play-life/docker-compose.prod.yml` + +**Вариант 2: Через SSH с cat (если SCP не работает):** +```bash +# На сервере через SSH - создать файл +ssh poignatov@192.168.50.55 +cd /volume1/docker/play-life +cat > docker-compose.prod.yml << 'EOF' +# Вставьте сюда содержимое файла, затем нажмите Enter и введите EOF +EOF +``` + +**Вариант 3: Вручную через SSH с nano:** +```bash +# На сервере +ssh poignatov@192.168.50.55 +cd /volume1/docker/play-life +nano docker-compose.prod.yml +# Вставьте содержимое файла (Ctrl+Shift+V или правый клик) +# Сохраните: Ctrl+O, Enter, Ctrl+X +``` + +**Вариант 4: Через SCP (если настроен):** +```bash +# Если SCP не работает, используйте один из вариантов выше +scp docker-compose.prod.yml poignatov@192.168.50.55:/volume1/docker/play-life/ +``` + +**Содержимое файла `docker-compose.prod.yml`:** +```yaml +version: '3.8' + +# Production конфигурация для Synology +# Использует образ из registry вместо локальной сборки +# База данных postgres запущена отдельно (не в этом compose) + +services: + play-life: + image: dungeonsiege.synology.me/poignatov/play-life:latest + container_name: play-life-prod + ports: + - "3080:80" + volumes: + - /volume1/docker/play-life/uploads:/app/uploads:rw + restart: always + env_file: + - .env + # Подключаемся к общей сети playlife-net + # Перед первым запуском нужно создать сеть и подключить postgres: + # docker network create playlife-net + # docker network connect playlife-net postgres1 + networks: + - playlife-net + +networks: + playlife-net: + external: true +``` + +**Важно:** Оба контейнера (play-life и postgres1) подключены к сети `playlife-net`, что позволяет им находить друг друга по имени. В `.env` файле используйте `DB_HOST=postgres1` (имя контейнера). Это стабильно и не меняется при перезапуске. + +### 3.3. Создать общую сеть и подключить postgres + +Создадим отдельную docker network для связи контейнеров. Это нужно сделать **один раз**: + +```bash +ssh poignatov@192.168.50.55 + +# 1. Создать сеть playlife-net +docker network create playlife-net + +# 2. Подключить существующий postgres контейнер к этой сети +docker network connect playlife-net postgres1 + +# 3. Проверить, что postgres подключен к сети +docker network inspect playlife-net +``` + +**Важно:** Postgres остается также подключен к своей старой сети (bridge), так что другие контейнеры, которые его используют, продолжат работать. + +### 3.4. Создать .env файл + +На сервере создайте файл `.env` с переменными окружения: + +```bash +cd /volume1/docker/play-life +nano .env +``` + +Вставьте следующие переменные (замените значения на свои): + +```env +DB_NAME=n8n_db +DB_HOST=postgres1 +DB_PORT=5432 +DB_USER=n8n_user +DB_PASSWORD=y9CroXzrQdA^RP +WEBHOOK_BASE_URL=https://pl-dungeonsiege.duckdns.org +TIMEZONE=Europe/Moscow +TELEGRAM_BOT_TOKEN=8473129470:AAFeB1CKmhbyH26g_sFz5CgpAit3QFM-wME +TODOIST_CLIENT_ID=bf61a1770f004838935816bee3b10ec4 +TODOIST_CLIENT_SECRET=25942ae6e4d64054b76aee3aecd259bb +JWT_SECRET=BYa757tsSlG2CuGW5PUPpTRgF5iILniEXVXaBDlKfB4= +``` + +**Важно:** +- `DB_HOST=postgres1` - имя контейнера postgres (стабильно, не меняется) +- `DB_PORT=5432` - порт внутри контейнера postgres (не путать с внешним портом 5433) +- Благодаря сети `playlife-net`, контейнеры могут находить друг друга по имени +- Убедитесь, что все остальные значения соответствуют вашему текущему контейнеру + +Сохраните файл (Ctrl+O, Enter, Ctrl+X). + +### 3.4. Проверить права доступа + +Убедитесь, что пользователь может выполнять docker команды: + +```bash +docker ps +docker-compose --version +``` + +Если команды не работают, добавьте пользователя в группу `docker`: + +```bash +sudo synogroup --add docker poignatov +# Или через DSM: Control Panel → User & Group → Edit → Groups → docker +``` + +--- + +## Шаг 4: Остановка старого контейнера + +### Вариант 1: Через DSM (веб-интерфейс) + +1. Откройте **Container Manager** (или **Docker** в старых версиях DSM) +2. Найдите контейнер `dungeonsiege.synology.me-poignatov-play-life1` +3. Остановите его (Stop) +4. Опционально: удалите контейнер (Remove) + +### Вариант 2: Через SSH + +```bash +# Остановить контейнер +docker stop dungeonsiege.synology.me-poignatov-play-life1 + +# Удалить контейнер (опционально) +docker rm dungeonsiege.synology.me-poignatov-play-life1 +``` + +--- + +## Шаг 5: Первый запуск через docker-compose + +На сервере выполните: + +```bash +cd /volume1/docker/play-life + +# Проверить конфигурацию +docker-compose -f docker-compose.prod.yml config + +# Запустить контейнер +docker-compose -f docker-compose.prod.yml up -d + +# Проверить статус +docker-compose -f docker-compose.prod.yml ps + +# Посмотреть логи +docker-compose -f docker-compose.prod.yml logs -f +``` + +**Ожидаемый результат:** +- Контейнер должен запуститься +- Приложение должно быть доступно на `http://192.168.50.55:3080` + +--- + +## Шаг 6: Проверка автоматического деплоя + +1. Поднимите версию в файле `VERSION` (например, с `4.8.2` на `4.8.3`) +2. Закоммитьте и запушьте изменения в `main` ветку +3. Workflow автоматически: + - Соберет образ + - Запушит в registry + - Выполнит деплой на сервер +4. Проверьте уведомление в Telegram (если настроено) +5. Проверьте, что контейнер обновился: + +```bash +ssh poignatov@192.168.50.55 +cd /volume1/docker/play-life +docker-compose -f docker-compose.prod.yml ps +docker images | grep play-life +``` + +--- + +## Устранение проблем + +### Проблема: SCP не работает (subsystem request failed) + +**Ошибка:** +``` +subsystem request failed on channel 0 +scp: Connection closed +``` + +**Решение:** +Используйте альтернативные способы копирования файлов: +1. **Через File Station** (веб-интерфейс) - самый простой способ +2. **Через SSH с cat/heredoc** - создать файл напрямую на сервере +3. **Через nano** - вручную скопировать содержимое + +Подробности в разделе "Шаг 3.2" выше. + +### Проблема: SSH подключение не работает + +**Решение:** +- Проверьте, что SSH включен в DSM +- Проверьте правильность IP адреса и пользователя +- Проверьте пароль в secrets Gitea + +### Проблема: Docker команды не выполняются + +**Решение:** +```bash +# Добавить пользователя в группу docker +sudo synogroup --add docker poignatov + +# Или через DSM: Control Panel → User & Group → Edit → Groups → docker +``` + +### Проблема: Ошибка при логине в registry + +**Решение:** +- Проверьте, что `GIT_TOKEN` и `GIT_USERNAME` secrets настроены в Gitea +- Проверьте, что registry доступен с сервера: `docker login dungeonsiege.synology.me` + +### Проблема: "lookup postgres1 on ...: no such host" + +**Ошибка:** +``` +Failed to connect to database:dial tcp: lookup postgres1 on 192.168.50.1:53: no such host +``` + +**Решение:** +Это происходит если контейнеры не в одной сети. Проверьте: + +```bash +# 1. Проверить, что оба контейнера в сети bridge +docker network inspect bridge | grep -A 5 "Containers" + +# 2. Убедиться, что в docker-compose.prod.yml используется networks, а не network_mode +cat /volume1/docker/play-life/docker-compose.prod.yml + +# 3. Пересоздать контейнер с правильной сетью +cd /volume1/docker/play-life +docker-compose -f docker-compose.prod.yml down +docker-compose -f docker-compose.prod.yml up -d + +# 4. Проверить, что контейнеры могут общаться +docker exec play-life-prod ping -c 2 postgres1 +``` + +### Проблема: Контейнер не запускается + +**Решение:** +```bash +# Проверить логи +docker-compose -f docker-compose.prod.yml logs + +# Проверить конфигурацию +docker-compose -f docker-compose.prod.yml config + +# Проверить, что postgres контейнер запущен +docker ps | grep postgres + +# Проверить IP адрес postgres +docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres1 + +# Проверить, что контейнер может достучаться до postgres по IP +docker exec play-life-prod ping -c 2 +``` + +### Проблема: Ошибка "network-scoped alias is supported only for containers in user defined networks" + +**Ошибка:** +``` +ERROR: network-scoped alias is supported only for containers in user defined networks +``` + +**Решение:** +Это происходит при использовании `external_links` с bridge network. Убедитесь, что в `docker-compose.prod.yml` используется `network_mode: bridge` без `external_links` и `networks`. + +Проверьте содержимое файла - он должен быть как в разделе 3.2 выше. + +### Проблема: Образ не обновляется + +**Решение:** +```bash +# Вручную обновить образ +cd /volume1/docker/play-life +docker pull dungeonsiege.synology.me/poignatov/play-life:latest +docker-compose -f docker-compose.prod.yml up -d --force-recreate +``` + +--- + +## Полезные команды + +### Просмотр логов +```bash +cd /volume1/docker/play-life +docker-compose -f docker-compose.prod.yml logs -f +``` + +### Перезапуск контейнера +```bash +cd /volume1/docker/play-life +docker-compose -f docker-compose.prod.yml restart +``` + +### Остановка контейнера +```bash +cd /volume1/docker/play-life +docker-compose -f docker-compose.prod.yml down +``` + +### Обновление образа вручную +```bash +cd /volume1/docker/play-life +docker pull dungeonsiege.synology.me/poignatov/play-life:latest +docker-compose -f docker-compose.prod.yml up -d --force-recreate +``` + +--- + +## Безопасность + +- ✅ Пароль хранится в secrets Gitea, не в коде +- ✅ `.env` файл с секретами хранится только на сервере +- ✅ Используется существующий `GIT_TOKEN` для логина в registry +- ⚠️ Рекомендуется использовать SSH ключи вместо пароля (можно настроить позже) + +--- + +## Что дальше? + +После настройки автоматический деплой будет работать при каждом изменении версии. Просто: + +1. Поднимите версию в `VERSION` +2. Закоммитьте и запушьте изменения +3. Деплой выполнится автоматически! 🚀 + +--- + +**Вопросы?** Проверьте логи workflow в Gitea: **Actions → Build and Push Docker Image** diff --git a/VERSION b/VERSION index 326ec63..f99c658 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.8.2 +4.8.3 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..4ab1ca1 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,27 @@ +version: '3.8' + +# Production конфигурация для Synology +# Использует образ из registry вместо локальной сборки +# База данных postgres запущена отдельно (не в этом compose) + +services: + play-life: + image: dungeonsiege.synology.me/poignatov/play-life:latest + container_name: play-life-prod + ports: + - "3080:80" + volumes: + - /volume1/docker/play-life/uploads:/app/uploads:rw + restart: always + env_file: + - .env + # Подключаемся к общей сети playlife-net + # Перед первым запуском нужно создать сеть и подключить postgres: + # docker network create playlife-net + # docker network connect playlife-net postgres1 + networks: + - playlife-net + +networks: + playlife-net: + external: true diff --git a/play-life-web/package.json b/play-life-web/package.json index 1181807..852db8c 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "4.8.2", + "version": "4.8.3", "type": "module", "scripts": { "dev": "vite",