Перейти к содержанию

Промышленное развёртывание

Статья описывает, как поставить Taimen на выделенный сервер: раскладку каталогов, подготовку .env и секретов, выбор профилей, первый запуск, bootstrap и вынос автономных исполнителей на отдельный runner-хост. Для локального знакомства с платформой достаточно быстрого старта; здесь — то, что отличает промышленную установку.

Целевая топология

Узел Что работает Откуда код
Хост платформы Один compose-проект на корневом compose.yml: ядро (core), периметр (edge), по необходимости панель (platform) и прочие профили Клон суперпроекта с сабмодулями; релиз = коммит суперпроекта
Runner-хост (необязательно) Демон control-plane-agent с кодовым агентом, bare-зеркала репозиториев, рабочие копии Тот же суперпроект: образ deploy/runner/docker или systemd-юниты deploy/runner/systemd
Рабочие места операторов MCP-плагин / CLI control-plane, Human Harness Пакет control-plane из суперпроекта

Хост платформы и runner-хост связаны только через публичный адрес платформы: runner обменивает свой PAT на access token в IAM и ходит в Control Plane API через Caddy. Прямого сетевого доступа к базам ему не нужно.

Почему runner отдельно

Кодовому агенту разрешено выполнять произвольные команды (режим bypassPermissions), иначе он не может работать. Периметр вокруг него — отдельная машина или контейнер без доступа к секретам платформы, а не режим разрешений. Держать агента на хосте с базами и ключом подписи IAM нельзя.

Требования к хосту

Что Минимум Комментарий
ОС Linux x86_64 с systemd Проверено на Ubuntu 24.04 LTS
Docker Engine 24+ С плагином Compose v2
Docker Compose v2.24+ env_file с required: false используется в compose.yml
git любой современный Клон с сабмодулями
python3 + PyYAML и jsonschema (или uv) 3.10+ На хосте запускаются deploy/bootstrap.py и tools/smoke.py. Установке каталога из пакетов (шаг 5b bootstrap) нужны PyYAML и jsonschema: make bootstrap при установленном uv подключает их сам; при прямом вызове скрипта (на хосте без make) — либо uv run --no-project --with pyyaml --with jsonschema python3 deploy/bootstrap.py …, либо системный python3 с ними (на Ubuntu пакеты python3-yaml, python3-jsonschema)
openssl, make — make secrets генерирует ключи подписи RSA 3072
DNS A/AAAA-запись публичного имени на хост До первого запуска Caddy, см. Периметр и TLS
Порты 80 и 443 снаружи Остальные порты сервисов привязаны к 127.0.0.1

Ресурсы (CPU, RAM, диск) — в статье Ресурсы и масштабирование.

Раскладка каталогов

Рекомендуемая раскладка на хосте платформы:

/opt/taimen/
├── src/                         клон суперпроекта с сабмодулями (релиз = коммит)
│   ├── compose.yml              единое описание всех сервисов
│   ├── .env                     окружение установки, 0600
│   ├── secrets/                 ключи подписи, PAT, env-файлы service accounts, 0600
│   │   ├── iam-signing.pem      приватный ключ подписи IAM (владелец uid 10001)
│   │   ├── harness-pat          PAT оператора, выпускает bootstrap
│   │   └── control-plane-iam.env  service account ядра к памяти (bootstrap)
│   └── deploy/state/<env>.json  идентификаторы, записанные bootstrap (не секрет)
├── Caddyfile                    конфигурация периметра этой установки
└── backups/                     дампы БД (см. «Резервное копирование»)

.env, secrets/ и deploy/state/ перечислены в .gitignore суперпроекта: git pull их не трогает.

Caddyfile держите вне клона

Переменная CADDYFILE задаёт путь к файлу, который монтируется в контейнер caddy. Файл установки с вашим доменом удобнее держать вне рабочего дерева git (например, /opt/taimen/Caddyfile), взяв за образец deploy/staging/Caddyfile или deploy/caddy/Caddyfile.local. Так обновление суперпроекта не конфликтует с локальными правками.

Профили compose

Профиль Сервисы Когда нужен
core iam-db, iam-service, control-plane-db, control-plane-api, control-plane-worker, context-adapter, memory-db, memory-service, minio, minio-bootstrap Всегда (MinIO — хранилище содержимого артефактов, см. Хранилище объектов)
edge caddy Всегда: единственный вход снаружи
platform platform-db, platform-redis, realm-render, keycloak, minio, minio-bootstrap, platform-migrations, platform-api, platform-web Нужен вход людей в веб-консоль через Keycloak
policy policy-db, openfga-migrate, openfga, policy-service, policy-worker Экспериментальный PDP, см. Policy Service
entitlement entitlement-db, entitlement-service Экспериментальная проверка лицензий
demo support-bot Демонстрационный бот, в поставку не входит

Замороженный периметр

Профили platform, policy, entitlement работают и исправляются при поломке, но новых функций не получают. make up без аргументов поднимает только core edge. Если людям нужен вход в веб-консоль, профиль platform включается явно — на нём держится вход через Keycloak.

Процедура первого развёртывания

1. Клон суперпроекта

sudo mkdir -p /opt/taimen && sudo chown "$USER" /opt/taimen
git clone --recursive <url-суперпроекта> /opt/taimen/src
cd /opt/taimen/src
git submodule status        # все сабмодули без «-» в начале строки

Если клон сделан без --recursive, выполните make submodules (git submodule update --init --recursive). Сборка без сабмодулей падает: контекст сборки control-plane, memory-service и других — корень суперпроекта, а platform-auth-sdk подключён path-зависимостью соседней папкой.

2. .env и ключи подписи

make secrets

Цель make secrets:

  1. копирует .env.example в .env с правами 0600 (если .env ещё нет);
  2. заполняет пустые секреты случайными значениями (tools/fill_secrets.py трогает только пустые значения — повторный запуск ничего не перезапишет);
  3. генерирует secrets/iam-signing.pem и secrets/entitlement-signing.pem (RSA 3072) и ставит им 0600.

Затем отредактируйте .env под установку:

TAIMEN_PUBLIC_URL=https://platform.example.com
TAIMEN_PUBLIC_HOST=platform.example.com
COMPOSE_PROJECT_NAME=taimen
TAIMEN_NETWORK=taimen_default
CADDYFILE=/opt/taimen/Caddyfile
LOG_RENDERER=json
KEYCLOAK_HOSTNAME_STRICT=true
IAM_SIGNING_KEY_ID=prod-2026-01        # осмысленный kid, меняется при ротации ключа
CP_LEGACY_API_KEYS_ENABLED=false

Параметры провайдеров памяти (MEMORY_EMBEDDING_PROVIDER, MEMORY_LLM_PROVIDER, LLM_BASE_URL, LLM_MODEL, ключ провайдера) описаны в конфигурации памяти; полный список переменных — в справочнике.

TAIMEN_PUBLIC_URL выбирается один раз

Из него строится issuer IAM (${TAIMEN_PUBLIC_URL}/iam), а Control Plane ищет binding identity по паре (issuer, iam_principal_id). Смена публичного адреса после bootstrap — отдельная процедура, см. Аварийные процедуры.

3. Права секретов для контейнеров

Большинство сервисов платформы (iam-service, control-plane, policy-service, entitlement-service) работают в контейнере под uid 10001. Файлы из секции secrets: compose монтируются bind-mount'ом с правами хоста, поэтому на Linux:

sudo chown 10001:10001 secrets/iam-signing.pem secrets/entitlement-signing.pem
sudo chmod 600 secrets/*.pem

Права не ослабляйте до 644: это приватные ключи. Если оставить владельцем root при режиме 600, IAM не прочитает ключ и ответит 500 на выдаче токенов. Env-файлы (secrets/*.env) читает сам docker compose на хосте, для них chown не нужен.

4. Caddyfile

Возьмите за основу deploy/staging/Caddyfile, замените адрес сайта на своё имя и уберите маршруты профилей, которые не поднимаете. Подробно — в статье Периметр и TLS. Перед первым запуском убедитесь, что имя уже резолвится на хост:

dig +short platform.example.com

5. Проверка конфигурации и сборка

make config PROFILES="core platform edge"   # docker compose ... config --quiet
make build  PROFILES="core platform edge"

Сборка идёт на хосте из исходников, отдельный registry не нужен. Если хотите держать предыдущие образы для быстрого отката, задайте в .env IMAGE_TAG (например, короткий хэш коммита суперпроекта) — см. Обновление и миграции.

6. Запуск

make up PROFILES="core platform edge"       # docker compose --profile ... up -d --build
docker compose --profile "*" ps

Порядок старта задан depends_on с условиями service_healthy: базы → IAM и память → control-plane-api (выполняет alembic upgrade head, затем становится healthy, когда ревизия БД совпала с head) → control-plane-worker и context-adapter. Первый старт с пустыми томами занимает 1–3 минуты (Keycloak — до минуты сам по себе).

7. Bootstrap

python3 deploy/bootstrap.py --env .env --name prod --operator "Platform Operator"

deploy/bootstrap.py идемпотентен и делает за один проход:

Шаг Что создаётся Куда пишется результат
1 Ожидание /health/ready Control Plane и /healthz IAM на 127.0.0.1 —
2 IAM tenant, audiences с потолками scope, human principal оператора deploy/state/<env>.json
2a Service account ядра (память, entitlement, policy) secrets/control-plane-iam.env
3 POST /api/v1/bootstrap Control Plane: tenant, admin principal и первый IAM binding state
4 Authentication context и PAT оператора (read/write/admin) secrets/harness-pat
5 Шаблон проекта, project и workspace state
5b Каталог из пакетов (--packages, по умолчанию deploy/packages.yaml) state
— Отзыв legacy api-key, выданного bootstrap Control Plane state
7 Если задан POL_BOOTSTRAP_TOKEN и policy-service отвечает на /healthz — каталоги и роли PDP. Иначе шаг пропускается с сообщением; с --policy скрипт ждёт сервис до 30 с и падает не дождался …/healthz secrets/memory-service-iam.env, secrets/policy-service-cp.env

После первого прогона выполните то, что скрипт печатает с пометкой !!:

# 1. IAM tenant нужен platform-api и исполнителям пакетов
sed -i "s/^IAM_TENANT_ID=.*/IAM_TENANT_ID=<tenant-id>/" .env

# 2. Ядро должно подхватить env-файл service account (CP_CONTEXT_AUTH=auto)
docker compose up -d control-plane-api control-plane-worker context-adapter

Проверка, что ядро перешло с MEMORY_API_KEY на service account:

docker compose exec context-adapter env | grep -c CP_IAM_CLIENT_ID   # 1
docker compose logs --since 5m context-adapter | grep -E ' 40[13] ' || echo "нет 401/403"

8. Проверка

make smoke
curl -fsS https://platform.example.com/health/ready
curl -fsS https://platform.example.com/iam/.well-known/jwks.json | head -c 200

make smoke (скрипт tools/smoke.py) опрашивает health каждого запущенного сервиса через порты на 127.0.0.1; не поднятые профили помечаются «не запущен» и ошибкой не считаются.

9. Credential оператора

PAT оператора лежит в secrets/harness-pat. На рабочем месте оператора он кладётся в ~/.config/iam/credentials.json (режим строго 0600, иначе клиент откажется читать файл) под ключом <issuer>|<tenant-id>|<principal-id> — bootstrap печатает точный ключ последней строкой. Подробнее — в MCP-плагине и Credentials и PAT.

Перенос файла с сервера

Копируйте PAT по защищённому каналу (scp) и удаляйте промежуточные копии. Не вставляйте токен в чаты, тикеты и командную строку с историей.

10. Панель платформы (профиль platform)

Для входа людей через Keycloak после bootstrap нужно: засидировать tenant platform-core с тем же UUID, что у IAM (bootstrap печатает SEED_TENANT_ID=<tenant-id>), завести пользователей в realm platform с атрибутом tenant_id и привязать их к IAM principal, включить feature flag консоли. Процедура — в статье Вход людей — Keycloak.

Runner-хост

Автономный исполнитель ставится отдельно от хоста платформы. В суперпроекте лежат два варианта:

Образ собирается из дерева суперпроекта, внутри — демон control-plane-agent, Claude Code и тестовые базы (db-test, memory-db-test) в tmpfs. Секреты — файлы в каталоге с правами 0700, каждый 0600: PAT агента, токен подписки кодового агента, токен forge. Docker-сокет в контейнер не пробрасывается.

docker compose -f deploy/runner/docker/compose.yml up -d --build
docker compose -f deploy/runner/docker/compose.yml logs -f runner

Демон ставится uv-инструментом под непривилегированным пользователем runner; юнит ограничивает ресурсы (CPUQuota, MemoryHigh, MemoryMax, IOWeight) и файловую систему (ProtectSystem=strict, ProtectHome=read-only, NoNewPrivileges). Конфигурация — env-файл 0600, PAT — ~/.config/iam/credentials.json пользователя runner.

Правила, общие для обоих вариантов:

  • Один исполнитель — один principal. У каждого свой IAM principal вида agent, свой PAT и binding без admin и approvals.decide (bootstrap отказывается выдавать агенту эти права).
  • CONTROL_PLANE_AGENT_ONLY_ASSIGNED=1 и явный CONTROL_PLANE_AGENT_WORKSPACE: без них агент берёт первую доступную задачу по приоритету из общей очереди.
  • IAM_PRINCIPAL обязателен, если на машине лежат credentials нескольких исполнителей одного tenant.
  • Сумма лимитов памяти всех исполнителей не должна превышать физическую память хоста — см. Ресурсы и масштабирование.

Установка и переменные подробно — в статьях Установка runner и Конфигурация runner.

Чек-лист готовности к эксплуатации

  • .env и все файлы secrets/ — 0600; .env не лежит в git.
  • Ключ подписи IAM — владелец uid 10001, режим 600.
  • CP_LEGACY_API_KEYS_ENABLED=false, legacy api-key отозван bootstrap.
  • Caddy выпустил сертификат, http:// редиректит на https://.
  • /metrics закрыт от внешнего доступа (см. Периметр и TLS).
  • Порты 127.0.0.1:18000, 18001, 18010 и прочие не видны снаружи (ss -ltnp на хосте показывает их на 127.0.0.1).
  • Настроены бэкапы всех томов БД и каталога secrets/.
  • Записаны даты истечения всех PAT (deploy/state/<env>.json хранит operatorPatExpiresAt).
  • Runner-хост отделён от хоста платформы.

См. также