☁️ Облачный стенд для хакатона — архитектура и план
О чём это
Проект эфемерного публичного стенда на Oracle Cloud под хакатон «Dice Chess AI» (23–24.07.2026): играть/смотреть партии против AI-ботов + витрина статистики. Облако = одноразовый клон демо-поверхности, независимый от homelab и домашнего интернета во время выступления. Настоящий прод остаётся на aurora (см. dicechess-analytics-na-aurora-deploy-i-sizing); сюда мы кладём снапшот данных, а не второй источник истины.
Статус — черновик/обсуждение. Решено 07-16: IaC = Terraform, код в отдельном репо
dicechess-infra(см. раздел ниже). Механика облака уже проверена на KCP-прогоне — см. Oracle-Cloud-zhurnal-nastroyki-CLI-avtorizatsiya-set.
Зачем облако, а не homelab
- Публичность — чистый URL, который открывают жюри, без проброса домашней сети.
- Независимость — во время демо не зависим от домашнего аплинка/питания.
- Изоляция — не рискуем настоящим продом на aurora.
- Одноразовость — поднял → показал →
terminate. Стоит центы (см. бюджет ниже).
Охват (scope B): play + боты + аналитика
Полный нарратив темы «Dice Chess AI»: посетитель играет против oracle-ботов и тут же видит почему модель сильная (openings explorer, winrate-by-position).
Ключевой плюс
Бо́льшая часть уже существует как
docker-compose(у play-api свой compose, у analytics — db+api+ui). Стенд — это редеплой того же compose на публичную VM + обёртка наружу (прокси/TLS/снапшот), а не стройка с нуля.
Архитектура: одна A1.Flex VM
flowchart TD U["Игрок / жюри"] -->|HTTPS| C["Caddy · авто-TLS"] C -->|"play.домен"| PF["play-frontend · статика"] C -->|"/api + WS"| PA["play-api · JVM"] C -->|"stats.домен"| AU["analytics-ui · статика"] C -->|"/api"| AA["analytics-api · JVM"] PA --> PDB[("Postgres · play")] AA --> ADB[("Postgres · analytics · снапшот")] B["oracle-боты 1/2/3 + KCP · JVM+ONNX"] -->|"вызовы"| PA subgraph VM["Одна A1.Flex VM · docker-compose · aarch64"] C PF PA AU AA PDB ADB B end
Всё — JVM / статика / Postgres, то есть arch-нейтрально; ONNX-боты уже работают на arm64 (rpi4), так что Ampere A1 (aarch64) подходит без эмуляции.
| Компонент | Что это | Арх / формат | Наружу |
|---|---|---|---|
| Caddy | Обратный прокси + авто-TLS (Let’s Encrypt) | любой | :443 |
| play-frontend | SvelteKit adapter-static | статика | через Caddy |
| play-api | Игровой сервер + WebSocket | JVM (arm64 образ) | через Caddy |
| Postgres [play] | Партии/сессии play | arm64 | внутр. |
| analytics-ui | Статистика/openings explorer | статика (или arm64 образ) | через Caddy |
| analytics-api | REST над БД игр | JVM (arm64 образ) | через Caddy |
| Postgres [analytics] | Снапшот игр | arm64 | внутр. |
| oracle-боты 1/2/3 + KCP | ONNX-value-боты | arm64 (как на rpi4) | внутр. → play-api |
Почему это влезает в €250
| Ресурс | Конфиг | €/мес (24/7) |
|---|---|---|
| A1.Flex | 4 OCPU / 24 GB | ~€50 |
| A1.Flex | 8 OCPU / 48 GB | ~€100 |
| Block storage (БД+тома) | ~50 GB | ~€1 |
| Egress | 10 ТБ/мес бесплатно | €0 |
Реально за окно хакатона (пара недель тестов + событие) — ~€25–50 из €250. Подробнее про биллинг: Oracle-Cloud-rannbuk.
IaC: Terraform в репо dicechess-infra
Решено 2026-07-16
Инфраструктуру стенда описываем Terraform, код — в отдельном репо
dicechess-infra(не смешиваем с приложениями). Мотив: воспроизводимость + «поднял → показал → снёс одной командой». Terraform выбран в т.ч. как повод освоить инструмент (раньше не работали с ним).
🎓 Terraform в двух словах (для первого раза)
Terraform — декларативный IaC: описываешь желаемое состояние (какие ресурсы должны существовать), а не шаги. Весь жизненный цикл стенда закрывают три команды:
terraform plan— сухой прогон: показывает, что создаст / изменит / удалит; ничего не трогает;terraform apply— приводит облако к конфигу (создаёт VM, публичный IP и т.д.);terraform destroy— удаляет ровно то, что создал → это и есть «погасить, когда не нужно», без ручного клика в консоли.
Отличие от bootstrap-скрипта (который был на KCP-прогоне): Terraform хранит state — файл terraform.tfstate, карту «мой конфиг ↔ реальные ID ресурсов». Поэтому он знает, что именно снести, и повторный apply не плодит дубли (идемпотентность).
⚠️ State может содержать секреты/ID → в git не коммитим (.gitignore). Для соло-стенда локального state достаточно; удалённый (OCI Object Storage) — оверкилл. Аутентификация OCI-провайдера — та же, что у CLI (профиль DEFAULT / security-token), отдельного сетапа не нужно.
Разделение слоёв: Terraform vs cloud-init
- Terraform = инфраструктура: VM (
oci_core_instance), переиспользование готовых VCN/subnet (черезdata-источники), публичный IP, правило security-list на 80/443. - cloud-init (передаётся в
metadata.user_data) = приложение: ставит Docker, логинится в GHCR, кладётdocker-compose.hackathon.yaml+Caddyfile, делаетdocker compose up -d.
Terraform поднимает «железо», внутри него cloud-init разворачивает тот же compose-стек, что на схеме выше.
Раскладка репо
dicechess-infra/
README.md # что это + как запускать (apply/destroy)
hackathon-stand/
versions.tf # пины: terraform >= 1.x, provider oracle/oci
variables.tf # входы: tenancy/compartment OCID, region,
# ssh_public_key, subnet_ocid, shape,
# ocpus, memory_gb, ghcr_token (sensitive)
main.tf # data-источники (образ Ubuntu arm64, subnet) +
# oci_core_instance с user_data
cloud-init.yaml # docker + GHCR login + compose up
outputs.tf # public_ip, ssh_command, готовые URL
terraform.tfvars.example # шаблон (коммитим); реальный tfvars — gitignore
.gitignore # *.tfstate*, terraform.tfvars, .terraform/Иллюстративно (не финальный код) — инстанс с cloud-init:
resource "oci_core_instance" "stand" {
compartment_id = var.compartment_ocid
availability_domain = var.availability_domain
shape = "VM.Standard.A1.Flex" # Ampere aarch64, бесплатная квота
shape_config { ocpus = var.ocpus, memory_in_gbs = var.memory_gb }
create_vnic_details { subnet_id = var.subnet_ocid, assign_public_ip = true }
source_details {
source_type = "image"
source_id = data.oci_core_images.ubuntu_arm.images[0].id
}
metadata = {
ssh_authorized_keys = var.ssh_public_key
user_data = base64encode(templatefile("cloud-init.yaml", { ghcr_token = var.ghcr_token }))
}
}Секреты — вне git
ghcr_token, пароль Postgres, токены ботов → Terraform-переменные с sensitive = true; значения даём через gitignored terraform.tfvars или TF_VAR_*-переменные окружения. В репо — только terraform.tfvars.example с плейсхолдерами. ⚠️ Образ ghcr.io/rabestro/dicechess-house-bots приватный → на VM нужен docker login ghcr.io с read:packages-токеном (делает cloud-init, токен приходит переменной). Публичным образам (play-api/play/analytics) логин не нужен.
Рабочий цикл
cd dicechess-infra/hackathon-stand
terraform init # один раз: качает провайдер oci
terraform apply # VM+IP, cloud-init разворачивает стек (~пара минут)
# terraform output public_ip → прописать в DNS-поддомен → Caddy берёт TLS сам
# ... демо ...
terraform destroy # сносит ровно созданное; биллинг останавливаетсяСеть (VCN/subnet) переиспользуется через data-источники → destroy её не трогает (в простое бесплатна).
Что нетривиально (разовые задачи)
- ✅ arm64-образы — УЖЕ ГОТОВЫ (проверено 07-16, снят с критического пути). Все CD-воркфлоу (play-api, analytics-api, analytics-ui) уже собирают и пушат мультиарх через QEMU+Buildx (
platforms: linux/amd64,linux/arm64); Dockerfile’ы — build-стадия--platform=$BUILDPLATFORM+ мультиарх-рантайм-базы. Проверка опубликованных манифестов:dicechess-play-api(sha-776e25e),dicechess-analytics-api(v0.5.6),dicechess-analytics-ui(v0.6.8) — во всехlinux/amd64, linux/arm64. Боты (house-bots) — arm64 доказан работой на rpi4; play-фронт — статика (образа нет). Менять файлы сборки НЕ нужно. - Снапшот данных:
pg_dumpигр с aurora → restore в облако. Решить: вся история (~140k партий) или курированный срез. ← теперь фактический критический путь вместе с Terraform-скелетом. - DNS + TLS: поддомен
jc.id.lv→ public IP инстанса → Caddy сам берёт сертификат. - Секреты / вход: redirect-URI Google-OAuth под новый домен — либо запустить analytics-ui в анонимном read-only режиме, чтобы не возиться с логином на публичном демо.
План по фазам
Фаза 0 — подготовка (заранее, вне критического пути)
- ✅ arm64-образы — уже готовы (все образы мультиарх, проверено 07-16; см. «Что нетривиально» п.1).
- ✅ Репо
dicechess-infra(private) + Terraform-скелетhackathon-stand/— сделано 07-16: self-contained VCN + A1.Flex VM + cloud-init +docker-compose.hackathon.yaml(Caddy + play-api in-memory + oracle-3) +Caddyfile.terraform validate✓ (провайдер oci v8.23.0). Осталось: заполнитьterraform.tfvars→plan→ репетиция.- Скрипт
pg_dump(aurora) → restore (облако).- Поддомен
jc.id.lv+ подготовка DNS.
Фаза 1 — генеральная репетиция (~за неделю)
launch VM → bootstrap →
compose up→ restore данных → DNS+TLS → сквозной smoke (сыграть партию против бота, открыть статистику) →terminate. Проверяет весь стенд за ~час.
Фаза 2 — событие
Поднять из готовых артефактов за ~10 мин (или держать с репетиции). Мониторить логи/ресурсы.
Фаза 3 — после
terminateинстанса (сеть VCN остаётся, в простое бесплатна).
Открытые решения (обсудить в отдельной сессии)
- Объём данных: полная история vs курированный срез (свежие/высокорейтинговые) — чтобы дамп был лёгкий, а openings explorer выглядел богато. Склоняюсь к срезу.
- Вход в аналитику: анонимный read-only (проще, ничего не светим) vs полноценный OAuth с новым доменом.
- Размер VM: 4 OCPU/24 GB для демо с запасом; 8 — только при многих одновременных партиях.
- ✅ IaC — решено (07-16): Terraform в отдельном репо
dicechess-infra(см. раздел «IaC: Terraform» выше).
Следующий шаг
Прогресс 07-16:
- ✅ репо
dicechess-infra(private) + Terraform-скелетhackathon-stand/(validate ✓, запушен); - ✅ arm64-образы сняты с критического пути (все мультиарх);
- ✅ репетиция ядра пройдена (Фаза 1, core-scope):
plan→apply(A1.Flex VM + сеть) → cloud-init развернул Caddy+play-api+oracle-3 → сквозной smoke (/health→play-api 200, oracle-3 постит seek, приватный образ+bot-token работают) →destroy(6 ресурсов снесены, биллинг 0). Весь циклapply→smoke→destroyвживую. Репетиция выловила 2 бага Caddyfile (пустойSTAND_HOSTNAME-адрес → крэш;respondперехватывал/healthвместо proxy) — оба исправлены в репо (commitff3c111), cloud-init теперь воспроизводит рабочую конфигурацию.
Дальше (scope-B, чтобы стенд стал полным демо):
- фронт play-статика — собрать SPA под URL стенда и отдавать через Caddy (сейчас
/= заглушка); - скрипт снапшота данных (
pg_dumpaurora → restore) + персистентность play (PLAY_DB_URL+ postgres); - analytics-трио (api+ui+snapshot db);
- на событие — DNS-поддомен +
stand_hostname(Caddy авто-TLS) + генеральный прогон полного scope.
⚠️ Гоча Terraform (поймана в репетиции): terraform.tfvars перебивает TF_VAR_* из окружения — не класть плейсхолдеры в tfvars, если реальные значения приходят из env (иначе плейсхолдер выигрывает; на этом cloud-init сделал docker login пустышкой → unauthorized).
Связанные заметки
- Oracle-Cloud-zhurnal-nastroyki-CLI-avtorizatsiya-set — как поднимали сеть/инстанс (механика проверена).
- Oracle-Cloud-rannbuk — сжатый operational how-to + биллинг.
- dicechess-analytics-na-aurora-deploy-i-sizing — текущий прод, откуда снимаем снапшот.