Развёртывание
Деплой через Makefile
# Первый раз: сгенерировать пароли
make generate-passwords
# Деплой всего стека (QMServer, QMAdmin, QMWeb, QMDocs)
make deploy
Синхронизация каталогов проекта на сервер в $(DEPLOY_PATH) (по умолчанию /opt/qmserver) выполняется через rsync с --delete: файлы и папки, которые вы удалили в репозитории, удаляются и на хосте (внутри зеркалируемого дерева). Исключение — копирование конфигов в /etc/nginx/: там --delete не используется, чтобы не трогать сторонние файлы в conf.d и sites-available.
QMServer в Docker: образ ghcr.io/<GHCR_OWNER>/qmserver, подставляется из .env.cloud / .env (GHCR_OWNER — владелец пакетов на GitHub). При таргете make deploy на сервер зеркалируются исходники QMServer/ (и др.) для отладки и Swagger, а контейнер qmserver поднимается с уже собранного образа (make build-all-cloud делает pull для QMServer и build для QMAdmin/QMWeb/QMDocs). Платформа образа — Linux x86_64 (amd64) (platform: linux/amd64).
Локальный стек в Docker: docker-compose.yaml → по умолчанию ghcr.io/<GHCR_OWNER>/qmserver:latest.
Без Docker: один исполняемый qmserver и systemd — см. QMServer — Запуск и deploy/systemd/ в репозитории QMServer. Сценарий install.sh (нативно или Docker Compose): QMServer.
Репозиторий mindevis/QMServer — см. BUILD.md. Образы GHCR: ghcr.io/<owner>/qmserver:latest (и теги по SHA в CI).
Только QMAdmin без монорепозитория: образ ghcr.io/<owner>/qmadmin, релизы со статикой qmadmin-*-dist.tar.gz, сценарий install.sh (QMAdmin) — репозиторий mindevis/QMAdmin.
Только QMWeb без монорепозитория: образ ghcr.io/<owner>/qmweb, релизы qmweb-*-dist.tar.gz, install.sh (QMWeb) — mindevis/QMWeb.
Отдельные цели deploy-qmadmin, deploy-qmweb, deploy-qmserver, deploy-qmdocs, deploy-images используют тот же принцип для своих деревьев под DEPLOY_PATH.
QMDocs — статический сайт: в каталоге QMDocs выполняется npm ci (или npm install) и npm run build; артефакты каталога build/ отдаются через Nginx на хосте (порт 8243 в таблице ниже). Документация открыта без входа; /login и /licensing используют QMSERVER_API_URL на этапе сборки (customFields.qmserverApiUrl / переменная окружения при docusaurus build). Для Kubernetes используйте образ со статикой, например ghcr.io/<owner>/qmdocs:<tag>, собранный из Dockerfile в репозитории QMDocs — в Helm-чарте QMDeploy задайте образ и хосты через values.yaml (см. репозиторий QMDeploy).
Pro без «вечного» env: опционально QMSERVER_LICENSE_PUBLIC_KEY (hex публичного ключа) и активация токена в QMAdmin; выпуск токена — QMSERVER_LICENSE_ISSUER_SEED только на площадке продавца — см. Лицензия Pro.
Фронтенды QMAdmin и QMWeb (Vite): базовый URL API задаётся переменной QMSERVER_API_URL на этапе npm run build / docker build (вшивается в статику). В docker-compose.cloud.yaml в оба образа передаётся build-arg QMSERVER_API_URL=${QMSERVER_PUBLIC_URL}/api/v1 (см. env.cloud.example). Локально — файлы .env в каталогах QMAdmin и QMWeb (образцы: .env.example). В vite.config у обоих приложений указано envPrefix: ['VITE_', 'QMSERVER_'], поэтому QMSERVER_* и прежние VITE_* (например ссылки QMClient, лаунчер в QMWeb) доступны в import.meta.env.
Стек согласован так: QMAdmin, QMWeb и (при облачных настройках) QMLauncher обращаются к QMServer по одному базовому URL API. После развёртывания в QMAdmin обычно включают нужные модули и интеграции (QMLauncher, QMWeb). Иначе часть API для клиентов будет недоступна (см. QMServer). Синхронизация инвентаря (POST /api/v1/inventory, QXSync, coming soon) на QMServer действует при модуле Minecraft и интеграции QMWeb — см. Синхронизация инвентаря. В env.cloud.example кратко описаны флаги по умолчанию.
Компоненты на сервере
| Сервис | Порт | Домен |
|---|---|---|
| QMServer | 8240 | api.qx-dev.ru |
| QMAdmin | 8241 | admin.qx-dev.ru |
| QMWeb | 8242 | web.qx-dev.ru |
| QMDocs | 8243 | qm.qx-dev.ru |
| Nginx (на хосте) | 80, 443 | Reverse proxy → 127.0.0.1:8240–8243 |
Nginx (на хосте)
Nginx устанавливается на хост, не в Docker. При деплое конфиги из nginx/sites-available/ копируются в /etc/nginx/sites-available/, в /etc/nginx/sites-enabled/ создаются симлинки на них.
api.qx-dev.ru.conf→ 127.0.0.1:8240admin.qx-dev.ru.conf→ 127.0.0.1:8241web.qx-dev.ru.conf→ 127.0.0.1:8242qm.qx-dev.ru.conf→ 127.0.0.1:8243
Kubernetes (K3s)
Развёртывание в кластере (K3s или любой Kubernetes) — репозиторий QMDeploy: чарт qm-project, единая утилита scripts/k8s-manage.py (подкоманды bootstrap / secrets / addons). От root: README.md. Bootstrap ставит K3s, Helm, опционально секреты и Argo CD + Application qm (GitOps, values-argocd.yaml). Прямой Helm: bootstrap --direct-helm. В рабочей копии qm-project каталог QMDeploy/ — отдельный клон репозитория QMDeploy (рядом с корнем монорепо). Пути kubectl apply / render.py / migrate-from-kubectl не поддерживаются.
Кратко:
- Клонируйте QMDeploy (или откройте
QMDeploy/в монорепо). - От root: файл
/root/.ghcr-credentials(одна строка — PAT для ghcr.io, пользователь по умолчанию mindevis), затемpython3 scripts/k8s-manage.pyсоздаётqm-mysql/qm-app,ghcr-credentialsи при необходимости Argo — см.QMDeploy/README.md. Ручные секреты /--dry-run:k8s-manage.py secrets. - Домены и образы —
values-argocd.yaml. Прямой Helm:--direct-helm(см. README QMDeploy).
GRAFANA_ADMIN_PASSWORD и др. — qm-app / values.
Подробности — QMDeploy/README.md.
Массовая выгрузка модов (QMAdmin → API)
QMAdmin шлёт массовую загрузку несколькими POST: в каждом запросе суммарный размер файлов в пачке не превышает ~50 МиБ (один файл больше 50 МиБ уходит отдельным запросом). Так проще обойти лимиты Cloudflare (~100 МБ на один запрос на обычных тарифах) и не раздувать client_max_body_size у прокси до сотен мегабайт.
Конфиг nginx/sites-available/api.qx-dev.ru.conf в монорепозитории — для Nginx на отдельной ВМ перед локальным QMServer (127.0.0.1:8240). В K3s этот файл не применяется автоматически: наружу смотрит Ingress (часто Traefik). Обычно дополнительный Traefik Middleware под гигантские POST не нужен; если перед кластером всё же стоит nginx с дефолтом 1m, выставьте client_max_body_size хотя бы 64m и адекватные таймауты чтения тела.
Cloudflare на api-имени: каждый POST остаётся ниже типичного лимита ~100 МБ на запрос; отдельный очень большой .jar в одном POST может снова упереться в лимит CF — тогда DNS-only для API или обход CF.
Если перед кластером свой Nginx (DNS → Nginx → NodePort/Ingress): используйте вариант api.qx-dev.ru.conf из репозитория (подставьте proxy_pass на вход в K3s), nginx -t, systemctl reload nginx.
Синхронизация с GitHub без полного клона (sync-from-github.py)
Скрипт QMDeploy/scripts/sync-from-github.py скачивает в кэш (по умолчанию /opt/qm у root, иначе ~/.cache/qm) чарт qm-project, файл VERSION и скрипты из QMDeploy. Переменные: QM_HELM_BASE_URL, QM_DEPLOY_BASE_URL, QM_HELM_CACHE, QM_DEPLOY_ROOT. Затем на сервере от root обычно python3 …/k8s-manage.py (GitOps) или helm upgrade --install с путём к чарту в кэше (bootstrap --direct-helm).
Argo CD и MinIO S3 (Helm)
Argo CD ставится bootstrap-ом k8s-manage.py (внутри — установщик Argo + Application). MinIO: QMDeploy/scripts/k8s-manage.py addons --s3 или --argocd --s3. Нужны Helm и kubectl. Домен UI Argo: k3s.qx-dev.ru (--argocd-host). Значения: QMDeploy/helm/argocd/values-k3s.yaml.
Опционально: мониторинг (Grafana и Prometheus)
В чарте qm-project поддерживается стек Grafana + Prometheus. В пути GitOps (values-argocd.yaml) по умолчанию monitoring.enabled: false — поды мониторинга не создаются. Чтобы включить: monitoring.enabled: true в Git (коммит в QMDeploy) или параметры Application в Argo, затем Sync; в Secret qm-app — GRAFANA_ADMIN_PASSWORD. Ingress Grafana по умолчанию — monit.qx-dev.ru. Локально без k8s: docker-compose.monitoring.yml в QMDeploy.
Версионирование
В QMDeploy: VERSION (semver бандла), helm/qm-project/Chart.yaml (version, appVersion) — согласуются при релизе.
Обновление образов (например QMWeb)
# По умолчанию GitOps: imageTag=latest, images.* пустые (см. values-argocd.yaml).
helm upgrade qm ./helm/qm-project -n qm --reuse-values --set imageTag=latest
# Пин одного сервиса (редко):
helm upgrade qm ./helm/qm-project -n qm --reuse-values --set images.qmweb=ghcr.io/<GHCR_OWNER>/qmweb:<тег>
Образы должны быть доступны в реестре (для приватного GHCR — pull-secret на ноде).
Релизы QMWeb и текст из CHANGELOG на GitHub
В репозитории QMWeb workflow CI подставляет в описание GitHub Release содержимое секции ## [<версия>] из CHANGELOG.md (номер совпадает с package.json) при релизе по git-тегу vX.Y.Z. Релиз со статикой не создаётся от push в main (только образ в GHCR). Автогенерация заметок только по коммитам для этого шага отключена. Подробнее — QMWeb/CHANGELOG.md и .github/workflows/build-qmweb-image.yml.
DNS
Настроить A-записи для доменов на IP сервера:
- api.qx-dev.ru
- admin.qx-dev.ru
- web.qx-dev.ru
- qm.qx-dev.ru
- monit.qx-dev.ru (опционально Grafana, если
monitoring.enabledв Helm QMDeploy) - k3s.qx-dev.ru (Argo CD при стандартном
k8s-manage.py(bootstrap); только MinIO — см.k8s-manage.py addons)
Swagger / ReDoc
Документация API доступна по адресам:
- Swagger UI: https://api.qx-dev.ru/docs (редирект на /swagger) или https://api.qx-dev.ru/swagger/index.html
- ReDoc: https://api.qx-dev.ru/redoc
SSL и CloudFlare
Nginx настроен для работы с CloudFlare:
- Редирект HTTP → HTTPS
- Real IP из заголовка CF-Connecting-IP
- Gzip сжатие
- Кэширование статики (JS, CSS, изображения)
Сертификаты Let's Encrypt в /etc/letsencrypt/live/DOMAIN/:
fullchain.pem,privkey.pem
Выпустите сертификаты заранее (certbot) для каждого домена. В каждом конфиге указан путь по имени домена.
Ely.by OAuth (привязка аккаунта Ely.by)
Для использования скинов Ely.by пользователям нужно привязать аккаунт Ely.by. Настройка:
- Зарегистрировать приложение на account.ely.by/dev/applications/new (тип: Website).
- Указать Redirect URI:
https://api.qx-dev.ru/api/v1/auth/ely/callback(или ваш домен API). - Добавить в
.env.cloud(передаётся в QMServer при deploy):ELY_CLIENT_ID— Client ID из приложенияELY_CLIENT_SECRET— Client SecretELY_REDIRECT_URI— тот же URL, что в п.2 (напримерhttps://api.qx-dev.ru/api/v1/auth/ely/callback)ELY_FRONTEND_REDIRECT—https://web.qx-dev.ru(куда редиректить после OAuth)