Files
crank/docs/deployment.md
T
github-ops 7ab04aab2d
CI / Rust Checks (push) Has been cancelled
CI / UI Checks (push) Has been cancelled
CI / Frontend E2E (push) Has been cancelled
CI / Deployment Manifests (push) Has been cancelled
Deploy / build-images (apps/admin-api/Dockerfile, git.itexp.me/bsodfather/crank-community-admin-api, admin-api) (push) Has been cancelled
Deploy / build-images (apps/mcp-server/Dockerfile, git.itexp.me/bsodfather/crank-community-mcp-server, mcp-server) (push) Has been cancelled
Deploy / build-images (apps/ui/Dockerfile, git.itexp.me/bsodfather/crank-community-ui, ui) (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
ci: switch openbao loading to approle
2026-06-16 17:50:18 +00:00

12 KiB
Raw Blame History

Deployment

1. Назначение документа

Этот документ фиксирует platform-agnostic модель деплоя проекта.

Он отвечает на вопросы:

  • какие deployment artifacts должны быть в репозитории;
  • как проект должен запускаться в production-like окружении;
  • как routing должен работать через reverse proxy;
  • что должен делать CD pipeline.

Документ не привязан к конкретному hypervisor, cloud provider, типу VM или домашней инфраструктуре.

Практический post-deploy smoke checklist вынесен отдельно в deploy-and-staging-smoke.md.

Отдельный release checklist именно для открытой редакции вынесен в community-release-checklist.md.

2. Базовая модель деплоя

Для MVP принимается контейнерная модель:

  • admin-api, mcp-server и ui упаковываются в контейнеры;
  • PostgreSQL запускается как отдельный контейнер;
  • сервисы запускаются через docker-compose;
  • перед приложением находится reverse proxy;
  • CI проверяет качество, CD доставляет и перезапускает сервисы.

3. Что должно быть в репозитории

В проекте должны существовать:

  • Dockerfile для apps/admin-api;
  • Dockerfile для apps/mcp-server;
  • Dockerfile для apps/ui или отдельный production build flow для UI;
  • deploy/community/docker-compose.yml как canonical Community deployment manifest;
  • deploy/community/.env.example как canonical Community runtime env template;
  • docker-compose.yml и root .env.example только как local development convenience layers, а не как future private delivery boundary;
  • .gitea/workflows/ci.yml;
  • .gitea/workflows/deploy.yml;
  • примеры reverse proxy конфигураций.

Рекомендуемое стартовое распределение портов:

  • ui -> 3000
  • admin-api -> 3001
  • mcp-server -> 3002

4. Production-like runtime состав

Минимальный набор сервисов:

  • ui
  • admin-api
  • mcp-server
  • postgres
  • optional valkey через compose profile cache

Persistent data:

  • volume для postgres;
  • volume или bind mount для artifact storage.

5. Рекомендуемая routing model

Под одним доменом:

  • / -> ui
  • /api/admin/ -> admin-api
  • /mcp/ -> mcp-server

Это позволяет:

  • централизовать TLS;
  • не плодить поддомены без необходимости;
  • держать demo URL простым;
  • использовать один reverse proxy для UI, API и MCP.

6. Требования к reverse proxy

Reverse proxy должен обеспечивать:

  • TLS termination;
  • редирект HTTP -> HTTPS;
  • X-Forwarded-* headers;
  • отключение buffering для MCP endpoint;
  • увеличенные таймауты для долгих runtime-запросов;
  • корректную маршрутизацию UI, admin API и MCP.

Особенно для mcp-server важно:

  • proxy_buffering off;
  • proxy_request_buffering off;
  • длинный proxy_read_timeout.

7. Пример nginx

server {
    listen 80;
    server_name example.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    client_max_body_size 25m;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;

    location / {
        proxy_pass http://ui:3000;
    }

    location /api/admin/ {
        proxy_pass http://admin-api:3001/;
    }

    location /mcp/ {
        proxy_pass http://mcp-server:3002/;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

8. Пример Traefik

services:
  ui:
    labels:
      - traefik.enable=true
      - traefik.http.routers.ui.rule=Host(`example.com`) && PathPrefix(`/`)
      - traefik.http.services.ui.loadbalancer.server.port=3000

  admin-api:
    labels:
      - traefik.enable=true
      - traefik.http.routers.admin.rule=Host(`example.com`) && PathPrefix(`/api/admin`)
      - traefik.http.services.admin.loadbalancer.server.port=3001

  mcp-server:
    labels:
      - traefik.enable=true
      - traefik.http.routers.mcp.rule=Host(`example.com`) && PathPrefix(`/mcp`)
      - traefik.http.services.mcp.loadbalancer.server.port=3002

Для production нужно отдельно проверить timeout behavior на MCP маршруте.

9. Требования к docker-compose

deploy/community/docker-compose.yml должен:

  • поднимать postgres, admin-api, mcp-server, ui;
  • использовать env vars вместо hardcoded config;
  • иметь persistent storage для БД и artifacts;
  • публиковать ui, admin-api и mcp-server на loopback по умолчанию;
  • позволять переопределить publish bind одним env для случаев, когда reverse proxy вынесен на отдельный хост;
  • не публиковать внутренние сервисы наружу без необходимости;
  • содержать healthchecks;
  • поддерживать optional cache profile без превращения Valkey в обязательную runtime dependency.

Для Community optional cache layer включается отдельно:

  • profile: cache
  • service: valkey
  • базовый запуск без profile должен оставаться полностью рабочим
  • CD включает profile cache автоматически, если в .env стоит CRANK_CACHE_BACKEND=valkey или CRANK_CACHE_BACKEND=redis

Пример:

docker compose \
  -f deploy/community/docker-compose.yml \
  --env-file deploy/community/.env.example \
  --profile cache up -d

Для встроенного compose-сервиса valkey рекомендуемые значения в OpenBao:

CRANK_CACHE_BACKEND=valkey
CRANK_CACHE_URL=redis://valkey:6379/0
CRANK_CACHE_DEFAULT_TTL_MS=60000

10. Healthchecks

Нужны как минимум:

  • GET /health для admin-api
  • GET /health для mcp-server

CD не должен считать deployment успешным, пока эти endpoints не начали возвращать 200 OK.

11. CI и CD

11.1. CI

CI должен выполнять:

  • cargo fmt --all --check
  • cargo check --workspace
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace --all-targets

11.2. CD

CD для MVP должен:

  • запускаться только после успешного CI на main или вручную;
  • собирать versioned container images;
  • пушить их в container registry с cache;
  • доставлять deployment files на целевой Linux host;
  • выполнять controlled restart через pull, без сборки на сервере;
  • проверять health endpoints после запуска.

Минимальный flow:

  1. push в main
  2. успешный CI
  3. build and push images в registry
  4. upload runtime env и deployment files
  5. docker login на registry на целевом хосте
  6. docker compose config -q
  7. docker compose pull
  8. docker compose up -d
  9. healthcheck verification для ui, admin-api, mcp-server

Для текущего проекта базовая рекомендуемая схема:

  • registry на том же Gitea instance;
  • versioned tags по git sha;
  • self-hosted Gitea Actions runner;
  • на сервере только pull + restart, без docker compose up --build.

В Gitea secrets хранится только bootstrap-доступ к OpenBao:

  • BAO_ADDR
  • BAO_ROLE_ID
  • BAO_SECRET_ID

Workflow получает short-lived token через OpenBao AppRole и читает deploy, registry и runtime значения через scripts/load-openbao-env.sh. Это оставляет Gitea runner без прямого списка прикладных секретов.

OpenBao mount:

ci/

Workflow читает KV v2 секреты:

shared/registry
shared/deploy-ssh
projects/crank/deploy
projects/crank/runtime

В OpenBao должны лежать:

  • REGISTRY_USERNAME
  • REGISTRY_PASSWORD
  • DEPLOY_SSH_KEY
  • DEPLOY_HOST
  • DEPLOY_PORT
  • DEPLOY_USER
  • DEPLOY_PATH
  • DEPLOY_KNOWN_HOSTS, optional

scripts/load-openbao-env.sh маппит registry credentials для существующего workflow:

REGISTRY_USERNAME -> DEPLOY_REGISTRY_USER
REGISTRY_PASSWORD -> DEPLOY_REGISTRY_TOKEN

Workflow доставляет именно deploy/community/docker-compose.yml как public Community deployment manifest и собирает .env на сервере из значений OpenBao. Для runtime-переменных нужно создавать ключи с теми же именами, что и у приложений:

  • POSTGRES_HOST
  • POSTGRES_PORT
  • POSTGRES_DB
  • POSTGRES_USER
  • POSTGRES_PASSWORD
  • CRANK_STORAGE_ROOT
  • CRANK_PUBLISH_BIND
  • CRANK_ADMIN_BIND
  • CRANK_MCP_BIND
  • CRANK_MCP_REFRESH_MS
  • CRANK_LOG_LEVEL
  • CRANK_MASTER_KEY
  • CRANK_BASE_URL
  • CRANK_CACHE_BACKEND
  • CRANK_CACHE_URL
  • CRANK_CACHE_DEFAULT_TTL_MS
  • CRANK_SESSION_SECRET
  • CRANK_PASSWORD_PEPPER
  • CRANK_SESSION_TTL_HOURS
  • CRANK_BOOTSTRAP_ADMIN_EMAIL
  • CRANK_BOOTSTRAP_ADMIN_PASSWORD
  • CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME
  • CRANK_DEMO_SEED

Практически это означает:

  • Gitea знает только OpenBao bootstrap credentials;
  • ротация runtime/deploy значений делается в OpenBao;
  • .env на сервере каждый deploy собирается заново из актуального OpenBao секрета.

11. Граница Community против future commercial delivery

Community delivery path должен оставаться отдельным и воспроизводимым сам по себе.

Это означает:

  • deploy/community/* — public source of truth для открытой поставки;
  • future Enterprise и Cloud не должны появляться как еще один .env на том же compose-файле;
  • commercial manifests и pipelines должны жить отдельно после создания private repositories.

12. Что нельзя забыть

  • TLS и renewal сертификатов;
  • backup для postgres и artifact storage;
  • секреты вне репозитория;
  • app-level auth для admin-api и bootstrap admin credentials в env;
  • rollback на предыдущий image tag или предыдущую compose revision.