Развёртывание

Базовый способ развёртывания проекта — docker compose. Структура и правила повторяют подход, который уже используется в crewnaut: отдельный слой для портов, отдельный слой для app/deploy-настроек, compose-файлы в deploy/docker/, запуск через Makefile, единая named network для межсервисной коммуникации.

Идентификаторы среды

ПараметрЗначение
Project nametracium
Display nameTracium
Production base domaintracium.ru
Local testing base domaintracium.dev
Compose project nametracium

Структура deploy-контура

/
├── .env.example                    # централизованные host ports и compose project name
├── Makefile                        # команды запуска и диагностики compose-стека
└── deploy/
    ├── .env.example                # app settings, DSN, domains, credentials for local setup
    ├── auth/.env.example           # auth-specific env для shared/auth
    └── docker/
        ├── docker-compose.yml      # базовый infra stack
        ├── docker-compose.dev.yml  # локальный override с публикацией портов
        ├── docker-compose.web.yml  # локальный HTTPS ingress + docs/api/auth/web
        └── resources/
            ├── clickhouse/init/
            ├── certs/
            ├── docs/
            ├── go-dev/
            ├── nginx-local/
            └── otel-collector/

Принципы развёртывания

  • Один compose-stack на среду. На старте не используем Helm/Kubernetes.
  • Порты отделены от app-конфига. Host ports лежат в /.env, домены/DSN/секреты в deploy/.env.
  • Auth-конфиг отделён от общего deploy-конфига. deploy/auth/.env хранит только параметры shared/auth.
  • Compose-файлы хранятся в deploy/docker/. Это единая точка входа для локальной среды и production-like stack.
  • Внутренние вызовы идут по compose network. Сервисы общаются по DNS-именам контейнеров, а не по host ports.
  • Все stateful сервисы имеют named volumes.
  • Сервисы перезапускаются через restart: unless-stopped.
  • На stateful компонентах обязательны healthcheck’и.
  • Секреты в локальной среде идут через deploy/.env; prod backend для master key может быть env, KMS или Vault Transit.

Базовый compose-stack

deploy/docker/docker-compose.yml поднимает общую инфраструктуру проекта:

  • PostgreSQL — primary store и event store.
  • Redis — rate limiter, locks, cache.
  • Kafka — event bus.
  • Kafka UI — локальная диагностика топиков.
  • MinIO — S3-совместимое хранилище raw payloads и media.
  • ClickHouse — история цен/остатков и аналитика.
  • Elasticsearch — полнотекстовый и faceted search.
  • OTel Collector — приём OTLP telemetry.
  • tracium network — общая внутренняя сеть для infra и будущих прикладных сервисов.

deploy/docker/docker-compose.dev.yml поверх базового файла публикует host ports для локальной разработки и тестирования.

Коммуникация в рантайме

  • Внутренние DSN и URLs всегда указывают на service DNS внутри compose: postgres:5432, redis:6379, kafka:9092, catalog-core:8080.
  • Host ports (25432, 29092, 19200, …) существуют только для человека, IDE, браузера и локальных CLI.
  • Новые сервисы обязаны подключаться к общей сети tracium и не должны зависеть от localhost внутри контейнера.
  • Исключение для Kafka: single-node KRaft broker и controller живут в одном контейнере, поэтому внутренний KAFKA_CONTROLLER_QUORUM_VOTERS указывает на localhost:9093. Это не является межсервисным endpoint; все клиенты и сервисы продолжают использовать compose DNS kafka:9092.

Конфигурация

1. Центральные порты

/.env содержит только host ports и COMPOSE_PROJECT_NAME.

Примеры переменных:

  • POSTGRES_PORT
  • REDIS_PORT
  • KAFKA_PORT
  • MINIO_API_PORT
  • CLICKHOUSE_HTTP_PORT
  • ELASTICSEARCH_HTTP_PORT

2. App и deploy settings

deploy/.env содержит:

  • имя проекта и display name;
  • production и local base domains;
  • DSN и параметры infra-сервисов;
  • credentials локального окружения;
  • bucket names и внутренние service endpoints.

deploy/auth/.env содержит:

  • APP_AUTH_HOST, APP_COOKIE_DOMAIN, APP_AUTH_COOKIE_DOMAIN;
  • APP_MONGO_DSN, APP_AMQP_DSN;
  • APP_JWT_SECRET;
  • CORS и anonymous JWT настройки shared/auth.

Команды

make git-init
cp .env.example .env
cp deploy/.env.example deploy/.env
cp deploy/auth/.env.example deploy/auth/.env
 
make infra-up
make verify-compose
make infra-status
make infra-logs
make infra-down

Production и локальный нейминг

  • tracium.ru — production base domain.
  • docs.tracium.ru — production docs domain.
  • api.tracium.ru — production API domain.
  • tracium.dev — локальный base domain для тестирования и ingress hosts.
  • На локальном ingress уже закреплены tracium.dev, docs.tracium.dev, api.tracium.dev, id.tracium.dev.
  • API ingress проксирует запросы на Go API без переписывания путей. Публичные клиентские endpoints (/v1/*) и внутренние compatibility endpoints (/api/v1/*) регистрируются в Go-коде явно.

Production hosting

Текущее фактическое состояние production-хоста, доменов, runtime-state и systemd units вынесено в отдельный runbook:

Production-контур теперь живёт за системным nginx на хосте, а не за container ingress на :8444. Внешний nginx держит 80/443, TLS и reverse-proxy rules, а tracium публикует только loopback host ports для blue/green slot’ов.

Почему production ingress вынесен на хост

  • сертификаты и домены централизованы в одном системном nginx;
  • server1c.1-fb.ru остаётся обычным plain-HTTP proxy на 192.168.1.190 и не зависит от deploy цикла tracium;
  • reload host nginx делается без restart и без обрыва активных соединений;
  • blue/green переключает только upstream snippet, а не весь ingress-контур.

Состав production stack

deploy/docker/docker-compose.prod-bluegreen.yml поднимает один color-slot tracium:

  • web — статический web на loopback port 18180 или 18280;
  • docs — Quartz docs на loopback port 18181 или 18281;
  • api — Go API на loopback port 18182 или 18282.

shared/auth живёт отдельным стеком и публикуется на loopback port 18183 или 18283.

Системный nginx использует snippet tracium-active-upstreams.conf, который указывает на активные loopback ports для:

  • tracium.ru
  • docs.tracium.ru
  • api.tracium.ru
  • id.tracium.ru

Production env

Для production используются две env-группы:

  • корневой env: COMPOSE_PROJECT_NAME, TRACIUM_*_HOST_PORT;
  • deploy/.env: APP_PROD_BASE_DOMAIN, APP_PROD_DOCS_DOMAIN, APP_PROD_API_DOMAIN, APP_PROD_ADMIN_DOMAIN.

Сгенерировать production env можно скриптом:

APP_PROD_BASE_DOMAIN=tracium.ru \
APP_PROD_DOCS_DOMAIN=docs.tracium.ru \
APP_PROD_API_DOMAIN=api.tracium.ru \
sh ./scripts/render_production_env.sh /tmp/tracium-root.env /tmp/tracium-deploy.env

Проверка конфига:

sh ./scripts/verify_compose.sh prod

verify_compose.sh — единая точка валидации compose-конфигов, используется и CI (compose-validate job), и make verify-prod-compose. Режимы: dev, prod, all.

Production deploy — пошаговый flow

Весь production deploy запускается одним скриптом deploy/production-runtime/bin/deploy-full.sh, который CI-job deploy-production вызывает из ci-deploy runner’а, работающего на production-хосте. Скрипт оркестрирует 9 шагов:

sequenceDiagram
    participant CI as CI runner
    participant DF as deploy-full.sh
    participant Git as repo.git (bare)
    participant FS as filesystem<br/>($DEPLOY_ROOT)
    participant NG as host nginx
    participant App as tracium-{color}

    CI->>DF: sh deploy-full.sh
    DF->>FS: resolve-color.sh → CURRENT_COLOR / NEXT_COLOR
    DF->>Git: checkout-release.sh (git fetch + worktree add)
    Git-->>FS: releases/<sha> (worktree)
    DF->>FS: render_production_env.sh → .deploy/*.env
    DF->>FS: cp *.env → shared/
    DF->>App: deploy-tracium-color.sh NEXT_COLOR (compose up)
    DF->>NG: render-nginx-upstreams.sh → snippet
    DF->>NG: install snippet → /etc/nginx/snippets/
    DF->>NG: nsenter + nginx -t + systemctl reload nginx
    DF->>App: verify-public-endpoints.sh (HTTP smoke)
    alt smoke OK
        DF->>FS: echo NEXT_COLOR > tracium.active
        DF->>FS: ln -sfn releases/<sha> current
        opt CURRENT != NEXT
            DF->>App: retire-tracium-color.sh CURRENT_COLOR
        end
        DF->>FS: prune-releases.sh (keep 5)
    else smoke fails
        DF->>NG: render snippet for CURRENT_COLOR + reload
        DF->>CI: exit 1 (rollback)
    end

Привязка шагов к скриптам

ШагСкриптОтветственность
1. Resolve colorresolve-color.shЧитает runtime-state/tracium.active, вычисляет NEXT_COLOR
2. Checkout releasecheckout-release.shgit fetch в repo.git + git worktree add --detach releases/<sha>
3. Render envscripts/render_production_env.shГенерирует tracium-root.env и tracium-deploy.env
4. Stage envcp в deploy-full.shКопирует env в shared/ для compose
5. Up new colordeploy-tracium-color.shdocker compose up -d --build нового color-slot’а
6. Render nginx snippetrender-nginx-upstreams.shСобирает tracium-active-upstreams.conf для NEXT_COLOR
7. Install + reload nginxnsenternginx -t + systemctl reload nginxПрименяет snippet в host-side nginx
8. Smoke checkverify-public-endpoints.shHTTP-проверка 5 публичных endpoints
9a. Promote (при успехе)записывает tracium.active, симлинк current, retire-tracium-color.sh, prune-releases.shФиксирует переключение
9b. Rollback (при падении smoke)повторный render+reload для CURRENT_COLORВозвращает active upstream

restore-active-upstreams.sh — отдельный host-side скрипт, который пересобирает snippet и reload’ит nginx после reboot (не часть deploy flow, но использует те же runtime-state файлы).

Blue/green runtime state

  • активный color tracium хранится в /home/belkanov/apps/runtime-state/tracium.active;
  • активный color shared/auth хранится в /home/belkanov/apps/runtime-state/shared-auth.active;
  • renderer deploy/production-runtime/bin/render-nginx-upstreams.sh умеет собирать mixed-конфигурацию, где tracium и id могут быть в разных color-slot’ах;
  • host-side script deploy/production-runtime/bin/restore-active-upstreams.sh пересобирает snippet и делает systemctl reload nginx после reboot.

Release layout on host

/home/belkanov/apps/
├── runtime-state/
│   ├── tracium.active
│   └── shared-auth.active
├── production-runtime/
├── tracium/
│   ├── repo.git/                 # персистентный bare git-репо (object store)
│   ├── current -> releases/<sha>
│   ├── releases/<sha>             # git worktree, детачнутая на <sha>
│   └── shared/
│       ├── tracium-root.env
│       └── tracium-deploy.env
└── shared-auth/
    ├── current -> releases/<sha>
    ├── releases/<sha>
    └── shared/.env

Release материализуется не rsync’ом, а через git worktree из repo.git:

  • при первом деплое repo.git создаётся git clone --bare;
  • каждый следующий deploy делает git fetch + git worktree add --detach releases/<sha> <sha>;
  • устаревшие релизы чистятся prune-releases.sh через git worktree remove, а не rm -rf.

Плюс подхода: нет дубликатов исходников между runner’ом и хостом, релиз ровно соответствует git SHA, объектный store переиспользуется между релизами (incremental fetch).

Локальный web ingress (HTTPS :4444)

Локальный web-контур поднимается отдельным docker-compose.web.yml. Он публикует проект через TLS на порту 4444 и использует один ingress nginx:

ДоменЧтоUpstream
https://tracium.dev:4444placeholder landingweb (nginx static)
https://docs.tracium.dev:4444документация через Quartz v4 (livereload + граф связей)docs (Node.js 22 + Quartz)
https://api.tracium.dev:4444минимальный dev APIapi (Go stdlib + hot reload)
https://id.tracium.dev:4444Tracium ID на базе shared/authauth (/Users/belkanov/projects/shared/auth)

Предусловия

  • /etc/hosts:

    127.0.0.1 tracium.dev docs.tracium.dev api.tracium.dev id.tracium.dev
    
  • openssl доступен локально. make ssl-init генерирует self-signed сертификаты по шаблону из garmony.

  • SHARED_WORKSPACE в deploy/.env указывает на каталог общего репозитория: /Users/belkanov/projects/shared.

  • Для проверки без изменения /etc/hosts можно использовать ./scripts/verify_local_web.sh — он применяет curl --resolve.

Запуск

make init-env
make ssl-init
make web-up

Проверка:

make verify-local-web

Если hosts уже настроен, документация открывается в браузере по https://docs.tracium.dev:4444/.

Остановка и очистка

make web-down
make web-clean
make web-status
make web-logs SERVICE=nginx

Auth flow (anonymous JWT)

  • Для tracium.dev, docs.tracium.dev, api.tracium.dev nginx выполняет auth_requesthttp://auth:8080/auth/nginx.
  • shared/auth либо валидирует пришедший JWT, либо выпускает anonymous JWT.
  • Nginx выставляет Set-Cookie: jwt=...; Domain=.tracium.dev клиенту и прокидывает jwt / jwt_refresh headers в upstream.
  • id.tracium.dev — без auth_request, чтобы избежать loop.

Это — фундамент для будущего typed-claim flow (роли, реальные customer’ы). Миграция на типизированные claims не требует переделки proxy-слоя.

Docs (Quartz v4)

  • Движок: Quartz v4 — SSG на Preact/TypeScript поверх канонического markdown.
  • Почему: граф связей (Obsidian-style), backlinks, дерево explorer, тёмная/светлая тема, KaTeX, Mermaid, поиск, RSS — всё из коробки.
  • Content directory: bind-mount docs/docs/ → /quartz/content (read-only).
  • Homepage: docs/docs/index.md.
  • Конфиг: deploy/docker/resources/docs-quartz/quartz.config.ts + quartz.layout.ts.
  • Static build для CI/Pages: ./scripts/build_quartz_site.sh public.

Hot reload

  • docs: Quartz build --serve → incremental rebuild при изменении markdown в docs/docs/ + livereload WebSocket → nginx proxy_http_version 1.1; Upgrade.
  • Изменения в quartz.config.ts, quartz.layout.ts и кастомных стилях не попадают в running container автоматически: после них нужен make web-up или docker compose ... up -d --build docs.
  • api: CompileDaemon над ./backend/....
  • auth: CompileDaemon над bind-mounted shared/auth; сборка идёт в vendor-режиме самого auth-сервиса.
  • nginx: watcher на templates/, includes/, certs/render-configs.sh + nginx -s reload.
  • web: статические файлы — bind mount (без пересборки).

Общий Elasticsearch

shared/auth и Tracium используют один Elasticsearch (сервис elasticsearch из базового docker-compose.yml). Разделение — по префиксам индексов: auth_* у shared/auth, tracium_* у Tracium.

RabbitMQ overhead

shared/auth требует RabbitMQ для нотификаций — добавлен как отдельный сервис rabbit в web-стэк. В будущем планируется миграция на Kafka (уже используется ядром Tracium), пока принят operational overhead.

Примечания по безопасности (локальная разработка)

  • deploy/auth/.env никогда не коммитится (gitignored).
  • JWT secret в deploy/auth/.env.example — placeholder, обязателен override в deploy/auth/.env.
  • Сертификаты в deploy/docker/resources/certs/local/ — gitignored.
  • В prod — отдельный ADR-0018 master key + KMS/Vault Transit.

См. ADR-0018, ADR-0010.

Best practices, перенесённые из crewnaut

  • Не смешивать compose-логику, app-конфиг и host ports в одном файле.
  • Не хранить рабочие .env в репозитории, только .env.example.
  • Все стандартные действия должны быть доступны через Makefile, а не через длинные docker compose ... команды.
  • Локальная разработка должна поднимать HTTPS ingress отдельным compose-слоем, не ломая базовый infra stack.
  • Production ingress должен использовать выделенный host port и не конфликтовать с другими проектами на том же сервере.
  • Сначала поднимается инфраструктура, затем к ней подключаются прикладные сервисы catalog-core, ingestion, search, pricing, public-api, admin-api.
  • GitLab CI валидирует compose-конфиг через docker compose config до merge.

Связано