ADR-011: статика игры вынесена из куба на nginx (LXC 152 game-web), версии каталогами
- корень smart-updown.binom.pw -> nginx на Proxmox (LXC 152, 192.168.76.152) - /api,/ws -> куб (router PathPrefix на внешнем Traefik VDS), куб не менялся - версионирование: /opt/game/releases/<N>/, активная = симлинк /opt/game/current - tools/proxmox-nginx: unpack-release.sh, publish-release.sh, game.nginx.conf, smart-updown.traefik.yaml, README - приёмка живьём: корень 200 nginx/1.24.0 (Ubuntu), /27/ /28/ 200, /api/state 200 JSON, /api/ws цена, autoclose-check 9/9 - питфоллы: OCI-manifest в skopeo dir-дампе, слэш в конце версии, /solana-rpc мимо PathPrefix /api
This commit is contained in:
@@ -80,8 +80,10 @@ binance-market — это **не часть игры**, а **общий инфр
|
|||||||
| **k3s-кластер** | VM `192.168.76.120`, ns `game` | — |
|
| **k3s-кластер** | VM `192.168.76.120`, ns `game` | — |
|
||||||
| **Игровой бэк (боевой)** | TS-шлюз `updown-relayer`, сервис `testfront-gateway-smart-updown-relayer:8895` | оба домена: `/api`, `/ws` |
|
| **Игровой бэк (боевой)** | TS-шлюз `updown-relayer`, сервис `testfront-gateway-smart-updown-relayer:8895` | оба домена: `/api`, `/ws` |
|
||||||
| **Домены** | `smart-updown.binom.pw` (Unity-игра), `testfront.binom.pw` (тех-дашборд) | один и тот же бэк |
|
| **Домены** | `smart-updown.binom.pw` (Unity-игра), `testfront.binom.pw` (тех-дашборд) | один и тот же бэк |
|
||||||
| **Фронт игры** | deployment `fun-game-front2` (nginx + Unity WebGL) | образ `images.binom.pw/fun/game-front2:<тег>` |
|
| **Статика игры (корень `smart-updown.binom.pw`)** | **nginx на Proxmox, LXC 152 `game-web` (192.168.76.152)** | `/opt/game/releases/<N>/`, активная — `/opt/game/current` (ADR-011) |
|
||||||
| **Ingress-пути** | **`smart-updown-api`** — `/api`,`/ws` (ВНЕ helm); `fun-game-front2-…` — только `/` | ADR-010 |
|
| **Фронт игры (сборка)** | репозиторий `fun-game-front2` → образ `images.binom.pw/fun/game-front2:<тег>` | из образа извлекается статика и кладётся на nginx (`tools/proxmox-nginx/`) |
|
||||||
|
| **Ingress-пути (куб)** | **`smart-updown-api`** — `/api`,`/ws` (ВНЕ helm); `fun-game-front2-…` — только `/` (уже не обслуживает домен) | ADR-010 |
|
||||||
|
| **Маршрутизация корня** | внешний Traefik VDS: `/api`,`/ws` → куб `192.168.76.120`; всё остальное → nginx `192.168.76.152` | ADR-011 |
|
||||||
| **Solana localnet** | LXC 151 на Proxmox `192.168.76.66` | RPC `192.168.76.181:8899` |
|
| **Solana localnet** | LXC 151 на Proxmox `192.168.76.66` | RPC `192.168.76.181:8899` |
|
||||||
| **Контракт (в цепи)** | `9ALs…S1fCpf` (фантики, 4 инструкции) | прод-игра работает тут |
|
| **Контракт (в цепи)** | `9ALs…S1fCpf` (фантики, 4 инструкции) | прод-игра работает тут |
|
||||||
| **Реестр образов** | `images.binom.pw` | — |
|
| **Реестр образов** | `images.binom.pw` | — |
|
||||||
|
|||||||
@@ -156,3 +156,40 @@ helm upgrade fun-game-front2 binom/fun-game-front2 -n game \
|
|||||||
## Ссылки (дополнено 2026-09-13)
|
## Ссылки (дополнено 2026-09-13)
|
||||||
- Цена: `docs/PRICE-NATS.md`, диаграмма `docs/diagrams/price-flow.puml`
|
- Цена: `docs/PRICE-NATS.md`, диаграмма `docs/diagrams/price-flow.puml`
|
||||||
- **Безопасность: `docs/SECURITY.md` — чек-лист `SEC-1…SEC-15` (аудит живого стенда 2026-09-13)**
|
- **Безопасность: `docs/SECURITY.md` — чек-лист `SEC-1…SEC-15` (аудит живого стенда 2026-09-13)**
|
||||||
|
|
||||||
|
## ⭐ 2026-09-15 (2) — статика игры ушла из куба на nginx (LXC 152), версии каталогами
|
||||||
|
|
||||||
|
**Где мы:** корень `smart-updown.binom.pw` раздаёт **nginx на Proxmox (LXC 152 `game-web`,
|
||||||
|
192.168.76.152)** со версионированием по каталогам; `/api` и `/ws` по-прежнему идут в куб
|
||||||
|
на TS-шлюз отдельным router'ом внешнего Traefik. Решение и разбор — **ADR-011** в `docs/DECISIONS.md`.
|
||||||
|
|
||||||
|
**Как выпустить новую сборку игры (памятка — вместо helm upgrade фронта):**
|
||||||
|
```bash
|
||||||
|
# 1. CI репозитория fun-game-front2 собирает Unity WebGL и пушит образ
|
||||||
|
# images.binom.pw/fun/game-front2:<N> (тег = номер релиза, как раньше)
|
||||||
|
# 2. раскладываем на nginx и включаем как активную:
|
||||||
|
ssh root@192.168.76.66 "pct exec 152 -- /opt/game/tools/publish-release.sh <N> --activate"
|
||||||
|
# 3. проверяем ДОМЕН (не helm):
|
||||||
|
curl -sI https://smart-updown.binom.pw/ | grep -iE 'server|last-modified' # nginx/1.24.0 (Ubuntu)
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' https://smart-updown.binom.pw/<N>/Build/WebGL.framework.js
|
||||||
|
```
|
||||||
|
Без `--activate` сборка просто ложится в `/opt/game/releases/<N>/` и доступна по `/<N>/` —
|
||||||
|
корень не меняется. Откат версии = `publish-release.sh <старый N> --activate` (каталоги не удаляются).
|
||||||
|
|
||||||
|
**Инструменты:** `tools/proxmox-nginx/` — `unpack-release.sh` (распаковать dir-дамп образа),
|
||||||
|
`publish-release.sh` (тянуть образ + разложить + переключить), `game.nginx.conf`,
|
||||||
|
`smart-updown.traefik.yaml` (боевой конфиг внешнего Traefik). Скопированы в LXC 152
|
||||||
|
в `/opt/game/tools/` и `/etc/nginx/sites-available/game`.
|
||||||
|
|
||||||
|
**Проверено живьём:** `/` → 200 `nginx/1.24.0 (Ubuntu)`; `/27/`,`/28/` → 200, ассеты версий
|
||||||
|
различаются; `/api/state` → 200 JSON; `/api/ws` → цена идёт; сквозной `autoclose-check.cjs` — **9/9**.
|
||||||
|
|
||||||
|
**Задачи на будущее по этой схеме:**
|
||||||
|
- [ ] **TODO** `[pump.meta]` Ссылки на версии — **только со слэшем** (`/28/`): голый `/28` даёт 301.
|
||||||
|
Если понадобится бесслэшевый вид — добавить в nginx `return 301 /$build/`.
|
||||||
|
- [ ] **TODO** `[pump.meta]` Чистка старых сборок на 152: каталоги `releases/<N>` не удаляются сами
|
||||||
|
(~68 МБ на сборку). Политика ретеншена (сколько версий держим) — за владельцем.
|
||||||
|
- [ ] **TODO** `[pump.meta]` Автоматизировать шаг публикации в CI `fun-game-front2`: после push образа
|
||||||
|
дёргать `publish-release.sh` на 152 (сейчас — ручной вызов одной командой).
|
||||||
|
- [ ] **TODO** `[pump.meta]` Уточнить, нужен ли под `fun-game-front2` в кубе вообще (домен он больше
|
||||||
|
не обслуживает) — либо оставить как «предыдущая схема на откат», либо выключить.
|
||||||
|
|||||||
@@ -203,3 +203,58 @@
|
|||||||
(отдать фронтендеру, см. `TASKS.md`).
|
(отдать фронтендеру, см. `TASKS.md`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ADR-011 — Статика игры вынесена из куба на nginx (LXC 152), версии — каталогами (2026-09-15)
|
||||||
|
|
||||||
|
**Контекст.** До этого и корень, и `/api`,`/ws` жили в кубе: внешний Traefik (VDS) вёл весь домен
|
||||||
|
`smart-updown.binom.pw` на k3s `192.168.76.120`, статику отдавал под `fun-game-front2`
|
||||||
|
(nginx внутри образа), API — отдельный ingress `smart-updown-api` → TS-шлюз `:8895`.
|
||||||
|
Владелец решил **разделить плоскости**: бэк честно деплоится в куб, а сама игра (статика)
|
||||||
|
раздаётся с nginx, развёрнутого на Proxmox — со **версионированием по каталогам** (`/<номер>/`).
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
1. **nginx на Proxmox** — новый LXC **152** (`game-web`, 192.168.76.152, Ubuntu 24.04, 2 ядра,
|
||||||
|
1 ГБ RAM, диск 8 GB на `local-lvm`, `onboot=1`, unprivileged). Отдельный LXC потому, что
|
||||||
|
статика игры — «10 строк конфига и каталог», держать её в кубе рядом с бэком смысла нет.
|
||||||
|
2. **Версионирование каталогами:**
|
||||||
|
- `/opt/game/releases/<N>/` — файлы сборки N (`Build/`, `TemplateData/`, `index.html`);
|
||||||
|
- `/opt/game/current` — симлинк на активную сборку;
|
||||||
|
- `https://smart-updown.binom.pw/` → активная сборка, `/28/…` → конкретная сборка.
|
||||||
|
3. **Маршрутизация на внешнем Traefik (VDS)** — два router'а на один host, вместо одного:
|
||||||
|
- `smart-updown-api-router`: `Host(...) && (PathPrefix('/api') || PathPrefix('/ws'))` → `192.168.76.120` (куб);
|
||||||
|
- `smart-updown-router`: `Host(...)` → `192.168.76.152` (nginx на проксмоксе).
|
||||||
|
PathPrefix-роутер специфичнее Host-роутера, поэтому `/api`,`/ws` **не перехватываются статикой**.
|
||||||
|
IP LXC 152 достижим с VDS через wg-туннель (192.168.76.0/22 dev wg0).
|
||||||
|
4. **В кубе ничего не менялось**: `ingress smart-updown-api` (`/api`,`/ws` → шлюз) как был, вне helm.
|
||||||
|
Под `fun-game-front2` остался жив, но домен он больше не обслуживает (статика ушла на nginx) —
|
||||||
|
в куб его релизить больше не обязательно; релизы — только доставка статики на nginx.
|
||||||
|
5. **Инструменты** (`tools/proxmox-nginx/`): `unpack-release.sh` (распаковка dir-дампа образа в
|
||||||
|
`releases/<N>`), `publish-release.sh` (тянет образ из реестра, раскладывает, по `--activate`
|
||||||
|
переключает `current`), `game.nginx.conf`, `smart-updown.traefik.yaml`.
|
||||||
|
|
||||||
|
**Питфоллы, пойманные живьём:**
|
||||||
|
- `skopeo copy docker://… dir:` кладёт слои по **sha256-digest**, а `manifest.json` — это **OCI-манифест**
|
||||||
|
(словарь `layers[].digest`, не список строк как в docker-archiv). Парсить `json['layers'][i]['digest']`,
|
||||||
|
а не печатать элемент целиком (иначе `tar: Cannot connect to {'mediaType': ...}`).
|
||||||
|
- Голый шаблон `/28` (без слэша) отдаёт **301** — это `try_files ... /$build$subpath/`, т.е. редирект
|
||||||
|
на `/28/`. Для относительных путей Unity (`src="Build/WebGL.loader.js"`) важно, чтобы **браузерный
|
||||||
|
URL заканчивался слэшем**; иначе `Build/...` уедет в `/Build/...` и вернётся 404. Ссылаться на
|
||||||
|
версии только со слэшем (`/28/`), либо ставить редирект в nginx (`return 301 /$build/`).
|
||||||
|
- `/solana-rpc/` **не** перехватывается PathPrefix `/api` — это отдельный path, и на VDS он уходит
|
||||||
|
в корневой router (nginx). Поэтому страховочный `location /solana-rpc/` в nginx нужен: старые сборки
|
||||||
|
игры стучатся именно туда (проверено: `getHealth` → `ok` через домен).
|
||||||
|
- Имена ассетов Unity внутри сборки не меняются (`WebGL.data`/`.wasm`/`.framework.js`), но теперь
|
||||||
|
URL содержит номер сборки → `immutable`-кэш больше не «прилипает» между релизами (закрыт остаточный
|
||||||
|
риск из ADR-010). Внутри одной версии ассеты неизменяемы, т.к. каталог версии не перезаписывается.
|
||||||
|
|
||||||
|
**Приёмка (живой домен, 2026-09-15):** корень `/` → 200 от `nginx/1.24.0 (Ubuntu)` (LXC 152);
|
||||||
|
`/27/`,`/28/` → 200, ассеты версий различаются (md5 `WebGL.data`: `76b6f3…` vs `47453d…`);
|
||||||
|
`/api/state` → 200 JSON (куб), `/api/ws` → `[open]` + `{"type":"price"}`; сквозной
|
||||||
|
`tools/autoclose-check.cjs` на `BASE=https://smart-updown.binom.pw/api` — **9/9**; `testfront` не задет.
|
||||||
|
Откат: вернуть `/opt/traefik/static/smart-updown.yaml` из бэкапа VDS
|
||||||
|
(`/root/bak-smart-updown-nginx-2026-09-15/`) — домен снова целиком уйдёт в куб.
|
||||||
|
|
||||||
|
**Альтернативы:** (а) оставить статику в кубе и версионировать каталогами внутри образа — тянет
|
||||||
|
пересборку образа на каждый билд игры и не решает вопрос кэша; (б) поднять в LXC не nginx, а
|
||||||
|
podman-контейнер с nginx — лишний слой на 10 строках конфига; (в) отдавать статику прямо с k3s-Traefik
|
||||||
|
по PVC — не то, о чём просил владелец (он хотел nginx на проксмоксе).
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# nginx на Proxmox — раздача статики игры (LXC 152)
|
||||||
|
|
||||||
|
Боевой веб-слой игры «Бинарная ракета»: **корень `smart-updown.binom.pw` раздаётся nginx'ом
|
||||||
|
на LXC 152 с версионированием по каталогам**. Бэк (API/WS) остаётся в кубе — см. ADR-011.
|
||||||
|
|
||||||
|
## Что где живёт
|
||||||
|
|
||||||
|
| Что | Где |
|
||||||
|
|---|---|
|
||||||
|
| LXC | **152 `game-web`**, 192.168.76.152 (Server3, `pct exec 152` через `root@192.168.76.66`) |
|
||||||
|
| nginx-конфиг | `/etc/nginx/sites-available/game` (симлинк в `sites-enabled/`) |
|
||||||
|
| Сборки | `/opt/game/releases/<N>/` — `Build/`, `TemplateData/`, `index.html`, `ServiceWorker.js` |
|
||||||
|
| Активная сборка | `/opt/game/current` → симлинк на `releases/<N>` |
|
||||||
|
| Инструменты | `/opt/game/tools/` (`unpack-release.sh`, `publish-release.sh`) |
|
||||||
|
| Логи | `/var/log/nginx/game-access.log`, `game-error.log` |
|
||||||
|
|
||||||
|
## Адреса
|
||||||
|
|
||||||
|
- `https://smart-updown.binom.pw/` — активная сборка;
|
||||||
|
- `https://smart-updown.binom.pw/<N>/` — конкретная сборка (**обязательно со слэшем**);
|
||||||
|
- `https://smart-updown.binom.pw/<N>/Build/WebGL.data` — ассет конкретной сборки;
|
||||||
|
- `/api`, `/ws` — **не nginx**: их забирает внешний Traefik (VDS) → куб (ingress `smart-updown-api`).
|
||||||
|
|
||||||
|
## Публикация сборки
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CI fun-game-front2 пушит образ images.binom.pw/fun/game-front2:<N>
|
||||||
|
# затем на хосте Proxmox:
|
||||||
|
ssh root@192.168.76.66 "pct exec 152 -- /opt/game/tools/publish-release.sh <N> --activate"
|
||||||
|
```
|
||||||
|
|
||||||
|
Без `--activate` сборка ляжет в `/opt/game/releases/<N>/` и будет доступна по `/<N>/`,
|
||||||
|
но корень останется прежним. Откат = `publish-release.sh <старый N> --activate`.
|
||||||
|
|
||||||
|
## Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sI https://smart-updown.binom.pw/ | grep -iE 'server|last-modified' # nginx/1.24.0 (Ubuntu)
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' https://smart-updown.binom.pw/<N>/
|
||||||
|
curl -s https://smart-updown.binom.pw/api/state | head -c 80 # JSON от шлюза в кубе
|
||||||
|
# сквозная живая приёмка (ставит реальную ставку):
|
||||||
|
cd ~/WORK/games/pump-game-ops && BASE=https://smart-updown.binom.pw/api \
|
||||||
|
WS_URL=wss://smart-updown.binom.pw/api/ws node tools/autoclose-check.cjs # ждём 9/9
|
||||||
|
```
|
||||||
|
|
||||||
|
## Питфоллы
|
||||||
|
|
||||||
|
- **Слэш в конце обязателен** для версии: `/28` → 301 (редирект на `/28/`). Unity грузит ассеты
|
||||||
|
относительным путём (`Build/WebGL.loader.js`), поэтому без слэша `Build/...` уедет в корень → 404.
|
||||||
|
- **`skopeo ... dir:` пишет OCI-манифест**, где `layers` — словари. `unpack-release.sh` берёт
|
||||||
|
`digest` каждого слоя и распаковывает по имени файла `<hex>` (не `sha256:...`).
|
||||||
|
- **Кэш больше не прилипает между релизами**: URL содержит номер сборки → `immutable` корректен.
|
||||||
|
Внутри версии каталог не перезаписывается, поэтому ассеты действительно неизменяемы.
|
||||||
|
- **Сборки копятся** (~68 МБ на версию) — ретеншен пока ручной, см. `TASKS.md`.
|
||||||
|
- **Откат схемы целиком**: вернуть `/opt/traefik/static/smart-updown.yaml` из
|
||||||
|
`/root/bak-smart-updown-nginx-2026-09-15/` на VDS — домен снова пойдёт целиком в куб.
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# nginx для раздачи Unity-сборок «Бинарной ракеты» — LXC 152 (game-web, 192.168.76.152)
|
||||||
|
# Файл: /etc/nginx/sites-available/game
|
||||||
|
#
|
||||||
|
# Модель версионирования:
|
||||||
|
# /opt/game/releases/<N>/ — файлы сборки N (Build/, TemplateData/, index.html)
|
||||||
|
# /opt/game/current -> releases/<N> — активная сборка (симлинк, переключает publish-release.sh)
|
||||||
|
#
|
||||||
|
# https://smart-updown.binom.pw/ -> активная сборка (current)
|
||||||
|
# https://smart-updown.binom.pw/28/ -> конкретная сборка 28
|
||||||
|
# https://smart-updown.binom.pw/28/Build/WebGL.data -> ассет сборки 28
|
||||||
|
#
|
||||||
|
# API и WebSocket nginx НЕ обслуживает: их забирает Traefik на VDS
|
||||||
|
# (router smart-updown-api: PathPrefix /api,/ws -> куб 192.168.76.120).
|
||||||
|
# Локальные location /api, /ws, /solana-rpc — страховка для запросов,
|
||||||
|
# которые всё же дойдут до nginx (прямой заход по IP, старые сборки).
|
||||||
|
|
||||||
|
# Ассеты Unity внутри каталога версии неизменяемы -> immutable.
|
||||||
|
# index.html и всё остальное -> no-store.
|
||||||
|
map $uri $game_cache_control {
|
||||||
|
default "no-store";
|
||||||
|
~^/[0-9]+/.+\.(js|wasm|data|mem|symbols|css|png|jpg|jpeg|gif|svg|woff2?)$ "public, max-age=604800, immutable";
|
||||||
|
~^/.+\.(js|wasm|data|mem|symbols|css|png|jpg|jpeg|gif|svg|woff2?)$ "public, max-age=604800, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /opt/game/current;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/game-access.log;
|
||||||
|
error_log /var/log/nginx/game-error.log warn;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 512;
|
||||||
|
gzip_types application/javascript application/wasm application/json text/css application/octet-stream;
|
||||||
|
|
||||||
|
add_header Cache-Control $game_cache_control;
|
||||||
|
|
||||||
|
# --- версионированные сборки: /<число>/... -------------------------------
|
||||||
|
# /28/index.html -> /opt/game/releases/28/index.html
|
||||||
|
location ~ ^/(?<build>[0-9]+)(?<subpath>/.*)?$ {
|
||||||
|
root /opt/game/releases;
|
||||||
|
try_files /$build$subpath /$build$subpath/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- ассеты активной сборки ----------------------------------------------
|
||||||
|
location ~* \.(js|wasm|data|mem|symbols|json|css|png|jpg|jpeg|gif|svg|woff2?)$ {
|
||||||
|
root /opt/game/current;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- страховка: API -------------------------------------------------------
|
||||||
|
# Нормальный /api идёт мимо nginx (Traefik VDS -> куб). Если запрос всё же
|
||||||
|
# пришёл сюда — уходим в куб на k3s-Traefik с правильным Host, дальше
|
||||||
|
# ingress smart-updown-api ведёт в шлюз :8895.
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://192.168.76.120;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host smart-updown.binom.pw;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_connect_timeout 10s;
|
||||||
|
proxy_read_timeout 90s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# страховка: WebSocket шлюза
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://192.168.76.120;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host smart-updown.binom.pw;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# страховка: тонкий CORS-прокси до Solana RPC (старые сборки игры)
|
||||||
|
location /solana-rpc/ {
|
||||||
|
proxy_pass http://192.168.76.181:8899/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host 192.168.76.181:8899;
|
||||||
|
proxy_connect_timeout 5s;
|
||||||
|
proxy_read_timeout 30s;
|
||||||
|
add_header Access-Control-Allow-Origin "*" always;
|
||||||
|
add_header Access-Control-Allow-Headers "content-type" always;
|
||||||
|
add_header Access-Control-Allow-Methods "POST, OPTIONS" always;
|
||||||
|
if ($request_method = OPTIONS) { return 204; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- корень = активная сборка --------------------------------------------
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Публикация сборки игры на nginx (LXC 152, game-web).
|
||||||
|
#
|
||||||
|
# publish-release.sh <тег> [--activate]
|
||||||
|
#
|
||||||
|
# Что делает:
|
||||||
|
# 1. тянет образ images.binom.pw/fun/game-front2:<тег> из реестра (skopeo copy в dir);
|
||||||
|
# 2. распаковывает статику в /opt/game/releases/<тег>/;
|
||||||
|
# 3. без --activate оставляет активной текущую сборку (корень не меняется);
|
||||||
|
# с --activate переключает симлинк /opt/game/current на новую версию;
|
||||||
|
# 4. печатает проверку.
|
||||||
|
#
|
||||||
|
# Итог: https://smart-updown.binom.pw/ -> активная сборка
|
||||||
|
# https://smart-updown.binom.pw/<тег>/ -> конкретная сборка
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="${1:?использование: publish-release.sh <тег> [--activate]}"
|
||||||
|
ACTIVATE="${2:-}"
|
||||||
|
REG="images.binom.pw/fun/game-front2"
|
||||||
|
CREDS="subochev:Drovosek319"
|
||||||
|
IMG="/tmp/gameimg-${TAG}"
|
||||||
|
|
||||||
|
echo "=== 1/4 тяну образ ${REG}:${TAG} ==="
|
||||||
|
rm -rf "$IMG"
|
||||||
|
skopeo copy --src-creds "$CREDS" "docker://${REG}:${TAG}" "dir:${IMG}"
|
||||||
|
# креды не нужны при распаковке
|
||||||
|
chmod -R a+rX "$IMG"
|
||||||
|
|
||||||
|
echo "=== 2/4 распаковываю статику в /opt/game/releases/${TAG} ==="
|
||||||
|
/opt/game/tools/unpack-release.sh "$TAG" "$IMG"
|
||||||
|
|
||||||
|
if [ "$ACTIVATE" = "--activate" ]; then
|
||||||
|
echo "=== 3/4 переключаю активную сборку на ${TAG} ==="
|
||||||
|
PREV="$(readlink -f /opt/game/current || echo none)"
|
||||||
|
rm -f /opt/game/current
|
||||||
|
ln -s "/opt/game/releases/${TAG}" /opt/game/current
|
||||||
|
echo "было: $PREV"
|
||||||
|
echo "стало: $(readlink -f /opt/game/current)"
|
||||||
|
else
|
||||||
|
echo "=== 3/4 активную сборку НЕ трогаю (нет --activate) ==="
|
||||||
|
echo "активна: $(readlink -f /opt/game/current)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== 4/4 проверка ==="
|
||||||
|
systemctl reload nginx
|
||||||
|
for u in "/" "/${TAG}/" "/${TAG}/index.html" "/${TAG}/Build/WebGL.framework.js"; do
|
||||||
|
printf ' %-42s %s\n' "$u" "$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1${u}")"
|
||||||
|
done
|
||||||
|
echo "готово: сборка ${TAG}"
|
||||||
|
rm -rf "$IMG"
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
http:
|
||||||
|
# ── middlewares ─────────────────────────────────────────────────────────
|
||||||
|
middlewares:
|
||||||
|
# Подмена Host на входе в куб — k3s-Traefik роутит именно по Host,
|
||||||
|
# а ingress `smart-updown-api` объявлен на smart-updown.binom.pw.
|
||||||
|
smart-updown-host:
|
||||||
|
headers:
|
||||||
|
customRequestHeaders:
|
||||||
|
Host: "smart-updown.binom.pw"
|
||||||
|
smart-updown-redirect-https:
|
||||||
|
redirectScheme:
|
||||||
|
scheme: https
|
||||||
|
permanent: true
|
||||||
|
|
||||||
|
# ── routers ─────────────────────────────────────────────────────────────
|
||||||
|
routers:
|
||||||
|
# API и WebSocket игры: уходят в k3s (192.168.76.120) на ingress
|
||||||
|
# `smart-updown-api` (ns game), дальше в TS-шлюз :8895.
|
||||||
|
# Отдельный router потому, что PathPrefix специфичнее Host:
|
||||||
|
# так /api и /ws не перехватываются статикой nginx.
|
||||||
|
smart-updown-api-router:
|
||||||
|
rule: "Host(`smart-updown.binom.pw`) && (PathPrefix(`/api`) || PathPrefix(`/ws`))"
|
||||||
|
service: smart-updown-api
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
certResolver: mycloudflare
|
||||||
|
|
||||||
|
# Корень: статика игры. Раздаёт nginx на LXC 152 (game-web, 192.168.76.152)
|
||||||
|
# в домашней сети через wg-туннель. Версии — каталогами (/28/, /29/, ...),
|
||||||
|
# корень — активная сборка (симлинк /opt/game/current).
|
||||||
|
smart-updown-router:
|
||||||
|
rule: Host(`smart-updown.binom.pw`)
|
||||||
|
service: smart-updown
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
certResolver: mycloudflare
|
||||||
|
|
||||||
|
# http -> https
|
||||||
|
smart-updown-http-redirect:
|
||||||
|
rule: Host(`smart-updown.binom.pw`)
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
service: noop
|
||||||
|
middlewares:
|
||||||
|
- smart-updown-redirect-https
|
||||||
|
|
||||||
|
# ── services ────────────────────────────────────────────────────────────
|
||||||
|
services:
|
||||||
|
smart-updown-api:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://192.168.76.120"
|
||||||
|
smart-updown:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://192.168.76.152"
|
||||||
|
noop:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://127.0.0.1:9"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Распаковка образа игры из dir-дампа skopeo в версионированный каталог nginx.
|
||||||
|
# Использование: unpack-release.sh <тег> <dir-дамп образа>
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="$1"
|
||||||
|
IMG="$2"
|
||||||
|
DEST="/opt/game/releases/${TAG}"
|
||||||
|
|
||||||
|
[ -d "$IMG" ] || { echo "нет дампа $IMG"; exit 1; }
|
||||||
|
|
||||||
|
rm -rf /tmp/rootfs-${TAG}
|
||||||
|
mkdir -p /tmp/rootfs-${TAG}
|
||||||
|
|
||||||
|
cd "$IMG"
|
||||||
|
# слои из manifest.json в правильном порядке
|
||||||
|
/usr/bin/python3 - <<'PY' > /tmp/layers-${TAG}.txt
|
||||||
|
import json
|
||||||
|
m = json.load(open('manifest.json'))
|
||||||
|
for l in m['layers']:
|
||||||
|
print(l['digest'].split(':', 1)[1])
|
||||||
|
PY
|
||||||
|
|
||||||
|
while read -r layer; do
|
||||||
|
[ -z "$layer" ] && continue
|
||||||
|
case "$layer" in
|
||||||
|
*tar.gz|*tgz) tar -xzf "$layer" -C /tmp/rootfs-${TAG} ;;
|
||||||
|
*tar.zst) tar --zstd -xf "$layer" -C /tmp/rootfs-${TAG} ;;
|
||||||
|
*) tar -xf "$layer" -C /tmp/rootfs-${TAG} ;;
|
||||||
|
esac
|
||||||
|
done < /tmp/layers-${TAG}.txt
|
||||||
|
|
||||||
|
SRC="/tmp/rootfs-${TAG}/usr/share/nginx/html"
|
||||||
|
[ -d "$SRC" ] || { echo "нет $SRC в образе"; ls /tmp/rootfs-${TAG}/usr/share/nginx 2>/dev/null; exit 1; }
|
||||||
|
|
||||||
|
mkdir -p "$DEST"
|
||||||
|
cp -a "$SRC/." "$DEST/"
|
||||||
|
chmod -R a+rX /opt/game/releases
|
||||||
|
rm -rf /tmp/rootfs-${TAG} /tmp/layers-${TAG}.txt
|
||||||
|
|
||||||
|
echo "=== релиз ${TAG} разложен в ${DEST} ==="
|
||||||
|
ls -la "$DEST"
|
||||||
|
du -sh "$DEST"
|
||||||
Reference in New Issue
Block a user