Files
crank/docs/deployment.md
T
2026-04-11 12:40:00 +03:00

267 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](/home/a.tolmachev/code/rust/mcpaas/docs/deploy-and-staging-smoke.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;
- `docker-compose.yml`;
- `.env.example`;
- `.github/workflows/ci.yml`;
- `.github/workflows/deploy.yml`;
- примеры reverse proxy конфигураций.
Рекомендуемое стартовое распределение портов:
- `ui` -> `3000`
- `admin-api` -> `3001`
- `mcp-server` -> `3002`
## 4. Production-like runtime состав
Минимальный набор сервисов:
- `ui`
- `admin-api`
- `mcp-server`
- `postgres`
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`
```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`
```yaml
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`
`docker-compose.yml` должен:
- поднимать `postgres`, `admin-api`, `mcp-server`, `ui`;
- использовать env vars вместо hardcoded config;
- иметь persistent storage для БД и artifacts;
- публиковать `ui`, `admin-api` и `mcp-server` на loopback по умолчанию;
- не публиковать внутренние сервисы наружу без необходимости;
- содержать healthchecks.
## 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`
Для текущего проекта базовая рекомендуемая схема:
- `GHCR` как registry;
- versioned tags по `git sha`;
- `docker/buildx` cache в GitHub Actions;
- на сервере только `pull + restart`, без `docker compose up --build`.
Для private registry на сервере нужны отдельные deployment secrets:
- `DEPLOY_REGISTRY_USER`
- `DEPLOY_REGISTRY_TOKEN`
Они используются только для `docker login` на целевом Linux host перед `docker compose pull`.
Монолитный `DEPLOY_ENV_FILE` больше не используется.
Workflow собирает `.env` на сервере из отдельных GitHub secrets. Для runtime-переменных нужно
создавать secrets с теми же именами, что и у приложений:
- `POSTGRES_HOST`
- `POSTGRES_PORT`
- `POSTGRES_DB`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
- `CRANK_STORAGE_ROOT`
- `CRANK_ADMIN_BIND`
- `CRANK_MCP_BIND`
- `CRANK_MCP_REFRESH_MS`
- `CRANK_LOG_LEVEL`
- `CRANK_MASTER_KEY`
- `CRANK_BASE_URL`
- `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`
Практически это означает:
- одна runtime-переменная = один GitHub secret;
- ротация одного секрета не требует переписывать весь набор env;
- `.env` на сервере каждый deploy собирается заново из актуальных secrets.
## 12. Что нельзя забыть
- TLS и renewal сертификатов;
- backup для `postgres` и artifact storage;
- секреты вне репозитория;
- app-level auth для `admin-api` и bootstrap admin credentials в env;
- rollback на предыдущий image tag или предыдущую compose revision.