475 lines
18 KiB
Markdown
475 lines
18 KiB
Markdown
|
|
# Инструкция по настройке автоматического деплоя на 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 <IP_адрес_postgres>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Проблема: Ошибка "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**
|