Files
orchestrator/deploy/bundled/docker-compose.yml
claude-bot f0cd19d748 feat(replication): ORCH-10b Bundled-тираж — bundle-compose всего стека + bootstrap-скрипт
Закрывает Type B эпика ORCH-10 (по ADR-001 ORCH-103, D1–D11):

- deploy/bundled/docker-compose.yml — самодостаточный compose всего стека
  (орк + watchdog + Gitea 1.22.6 + зеркало upstream Plane CE v0.23.1,
  ~14 контейнеров); project name orchestrator-bundle (узнаваемый префикс),
  container_name не пиннится, staging-контура нет; одна bridge-сеть,
  машинный трафик — сервис-DNS, наружу только человеческие порты;
  GITEA__webhook__ALLOWED_HOST_LIST=orchestrator; все образы пиннованы
  неподвижными тегами. Корневой compose/Dockerfile/src/** — байт-в-байт.
- deploy/bundled/.env.example — конфиг-канон bundle (плейсхолдеры, ни одного
  дефолтного пароля; key-set-sync интерполяций держит тест).
- scripts/bootstrap_bundle.py — python stdlib-only, режимы plan/apply/verify,
  step-движок check→ensure, exit 0/2/1: preflight (fail-fast до мутаций) →
  секреты (gen_secrets.py + stdlib secrets, без перетирания) → up+готовность →
  init Gitea автоматом → init Plane (manual-step с API-верификацией) →
  онбординг строго onboard_project.py apply+verify → token-remote клон →
  сборка .env/.env.watchdog (единственный писатель, права 600) → health.
  Delete-операций нет вообще (D9), секреты не печатаются (NFR-3).
- CHANGELOG.md, CLAUDE.md (абзац Type B), .gitignore (deploy/bundled/repos/).

Док BUNDLED_SETUP.md, REPLICATION §1, arch README, adr-0038 и три структурных
тест-модуля (TC-01…TC-11) — в предыдущих коммитах ветки; полный регресс
1844 passed, ruff по файлам задачи чистый.

Refs: ORCH-103

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 02:16:32 +03:00

339 lines
13 KiB
YAML
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.
# ORCH-103 (Type B Bundled, ADR-001 D1D4): самодостаточный compose ВСЕГО стека
# для тиража «под ключ» на хост заказчика: orchestrator + orchestrator-watchdog +
# Gitea + Plane CE (зеркало официального selfhost-référence makeplane/plane
# v0.23.1: имена сервисов и env-контракт — upstream, анти-дрейф к их докам; наши
# отличия от référence: пиннинг неподвижными тегами литералом вместо ${APP_RELEASE}
# (NFR-6, держится tests/test_bundle_compose.py), убраны replicas/platform/SENTRY,
# секреты БЕЗ дефолтных значений — их генерирует scripts/bootstrap_bundle.py).
#
# Этот файл НЕ исполняется в нашем прод-контуре (корневой docker-compose.yml —
# байт-в-байт, заморожен анти-дрейфом ORCH-102); активация — только явный запуск
# оператором на целевом хосте (паттерн ORCH-009, kill-switch не нужен).
#
# Конфиг-слои (D2): интерполяции ${VAR} читаются compose'ом из deploy/bundled/.env
# (авто-чтение из project dir — без --env-file-футгана); канон ключей —
# deploy/bundled/.env.example (key-set-sync держит тест). Runtime-конфиг орка и
# watchdog — КОРНЕВЫЕ .env / .env.watchdog (канон Lite 1:1, REPLICATION §2);
# их единственный писатель — bootstrap_bundle.py.
#
# Сеть (D4): одна bridge-сеть проекта; машинный трафик — строго сервис-DNS
# (Plane→орк http://orchestrator:8500/webhook/plane, Gitea→орк .../webhook/gitea,
# орк→Plane http://proxy, орк→Gitea http://gitea:3000); network_mode: host НЕ
# используется (ssh-деплой-контур нашего хоста в bundle структурно спит —
# ORCH_DEPLOY_SSH_HOST пуст). Наружу публикуются ТОЛЬКО человеческие порты
# (орк/Plane proxy/Gitea web); postgres/redis/mq/minio не публикуются.
#
# Project name = узнаваемый префикс томов/контейнеров orchestrator-bundle_* (D1);
# container_name сознательно НЕ пиннится ни у кого — bundle и Lite/корневой
# compose не сталкиваются по именам на одном хосте.
name: orchestrator-bundle
networks:
default:
name: orchestrator-bundle
driver: bridge
# Env-контракт Plane CE — upstream-имена (référence v0.23.1). Значения секретов
# (POSTGRES_PASSWORD/SECRET_KEY/RABBITMQ_DEFAULT_PASS/MINIO_ROOT_PASSWORD) живут
# ТОЛЬКО в deploy/bundled/.env (генерирует bootstrap); дефолтных паролей нет.
x-plane-env: &plane-env
environment:
- WEB_URL=http://${BUNDLE_PUBLIC_HOST:-localhost}:${BUNDLE_PLANE_PORT:-8080}
- DEBUG=0
- CORS_ALLOWED_ORIGINS=http://${BUNDLE_PUBLIC_HOST:-localhost}:${BUNDLE_PLANE_PORT:-8080}
- GUNICORN_WORKERS=1
# db (upstream-имена; host/port — фиксированные сервис-DNS этого файла)
- PGHOST=plane-db
- PGDATABASE=${POSTGRES_DB:-plane}
- POSTGRES_USER=${POSTGRES_USER:-plane}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB:-plane}
- POSTGRES_PORT=5432
- PGDATA=/var/lib/postgresql/data
- DATABASE_URL=postgresql://${POSTGRES_USER:-plane}:${POSTGRES_PASSWORD}@plane-db:5432/${POSTGRES_DB:-plane}
# redis
- REDIS_HOST=plane-redis
- REDIS_PORT=6379
- REDIS_URL=redis://plane-redis:6379/
# rabbitmq
- RABBITMQ_HOST=plane-mq
- RABBITMQ_PORT=5672
- RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-plane}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
- RABBITMQ_DEFAULT_VHOST=${RABBITMQ_DEFAULT_VHOST:-plane}
- RABBITMQ_VHOST=${RABBITMQ_DEFAULT_VHOST:-plane}
- AMQP_URL=amqp://${RABBITMQ_DEFAULT_USER:-plane}:${RABBITMQ_DEFAULT_PASS}@plane-mq:5672/${RABBITMQ_DEFAULT_VHOST:-plane}
# application secret (генерирует bootstrap; дефолта сознательно НЕТ)
- SECRET_KEY=${SECRET_KEY}
# datastore (minio)
- USE_MINIO=1
- AWS_REGION=
- AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER:-plane-minio-admin}
- AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
- AWS_S3_ENDPOINT_URL=http://plane-minio:9000
- AWS_S3_BUCKET_NAME=uploads
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-plane-minio-admin}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
- BUCKET_NAME=uploads
- FILE_SIZE_LIMIT=5242880
# live server
- API_BASE_URL=http://api:8000
# proxy
- NGINX_PORT=80
services:
# ── Платформа: орк + sidecar-watchdog (образы собираются из этого же чекаута;
# корневой Dockerfile / watchdog/Dockerfile — без правок, NFR-1) ──────────
orchestrator:
build:
context: ../..
# ORCH-101 (D5): uid/gid/home двигаются ОДНОЙ группой с user: и таргетами
# маунтов ниже (инвариант ORCH-040). Дефолты bundle нейтральны (D2).
args:
APP_UID: ${ORCH_RUN_UID:-1000}
APP_GID: ${ORCH_RUN_GID:-1000}
APP_HOME: ${ORCH_AGENT_HOME_DIR:-/home/orchestrator}
restart: unless-stopped
user: "${ORCH_RUN_UID:-1000}:${ORCH_RUN_GID:-1000}"
init: true
command: ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8500"]
ports:
# человеческая точка: операторский smoke `curl /health` (D4)
- "${BUNDLE_ORCH_PORT:-8500}:8500"
volumes:
# данные/репозитории — bind ВНУТРИ project dir (uid-причины ORCH-040;
# покрыты .gitignore: неякорный data/ + deploy/bundled/repos/)
- ./data:/app/data
- ./repos:/repos
- /var/run/docker.sock:/var/run/docker.sock
# LLM-предусловие хоста заказчика (bundle его НЕ поставляет, BRD §1.3)
- ${ORCH_HOST_CLAUDE_CODE_DIR:-/usr/lib/node_modules/@anthropic-ai/claude-code}:/opt/claude-code:ro
- ${ORCH_HOST_NODE_BIN:-/usr/bin/node}:/usr/bin/node:ro
- ${ORCH_HOST_CLAUDE_DIR:-~/.claude}:${ORCH_AGENT_HOME_DIR:-/home/orchestrator}/.claude
- ${ORCH_HOST_CLAUDE_JSON:-~/.claude.json}:${ORCH_AGENT_HOME_DIR:-/home/orchestrator}/.claude.json:ro
# ssh-контур в bundle сознательно НЕ вводится (ADR D8): git-доступ агентов
# — HTTP token-remote, деплой-хуки нашего хоста структурно спят.
# runtime-конфиг орка собирает bootstrap (шаг 8); required:false — первый
# `up -d` поднимает стек ДО сборки конфига (AC-1), орк жив без него.
env_file:
- path: ../../.env
required: false
environment:
- ORCH_REPOS_DIR=/repos
group_add:
- "${ORCH_DOCKER_GID:-999}"
orchestrator-watchdog:
build:
context: ../..
dockerfile: watchdog/Dockerfile
restart: unless-stopped
init: true
mem_limit: 128m
mem_reservation: 32m
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./repos:/repos:ro
- ./data:/app/data:ro
env_file:
- path: ../../.env.watchdog
required: false
environment:
# bundle-сеть ≠ host-network Lite: метрики — по сервис-DNS; имя контейнера
# орка детерминировано project name (container_name не пиннится, D1).
# environment перекрывает env_file → когерентность механическая (TR-8).
- WATCHDOG_METRICS_URL=http://orchestrator:8500/metrics
- WATCHDOG_CONTAINERS=orchestrator-bundle-orchestrator-1
group_add:
- "${ORCH_DOCKER_GID:-999}"
# ── Gitea (D6): официальный образ, НЕ rootless; init полностью автоматом —
# bootstrap создаёт админа/токен через `gitea admin ...` CLI в контейнере.
# Branch protection на main НЕ настраивается (норматив D10 ORCH-009/INV-4).
gitea:
image: gitea/gitea:1.22.6
restart: unless-stopped
ports:
- "${BUNDLE_GITEA_HTTP_PORT:-3000}:3000"
environment:
- GITEA__database__DB_TYPE=sqlite3
- GITEA__security__INSTALL_LOCK=true
- GITEA__server__DOMAIN=${BUNDLE_PUBLIC_HOST:-localhost}
- GITEA__server__ROOT_URL=http://${BUNDLE_PUBLIC_HOST:-localhost}:${BUNDLE_GITEA_HTTP_PORT:-3000}/
# ssh-контур не вводится (D8): порт не публикуется, ssh выключен.
- GITEA__server__DISABLE_SSH=true
- GITEA__service__DISABLE_REGISTRATION=true
# МИНА TR-4 (D4): Gitea по умолчанию режет webhook'и в приватные адреса —
# без этой строки «задача не появилась» гарантирован.
- GITEA__webhook__ALLOWED_HOST_LIST=orchestrator
volumes:
- gitea-data:/data
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/healthz"]
interval: 10s
timeout: 5s
retries: 12
# ── Plane CE — зеркало upstream selfhost-référence v0.23.1 (D3) ────────────
web:
<<: *plane-env
image: makeplane/plane-frontend:v0.23.1
restart: unless-stopped
command: node web/server.js web
depends_on:
- api
- worker
space:
<<: *plane-env
image: makeplane/plane-space:v0.23.1
restart: unless-stopped
command: node space/server.js space
depends_on:
- api
- worker
- web
admin:
<<: *plane-env
image: makeplane/plane-admin:v0.23.1
restart: unless-stopped
command: node admin/server.js admin
depends_on:
- api
- web
live:
<<: *plane-env
image: makeplane/plane-live:v0.23.1
restart: unless-stopped
command: node live/dist/server.js live
depends_on:
- api
- web
api:
<<: *plane-env
image: makeplane/plane-backend:v0.23.1
restart: unless-stopped
command: ./bin/docker-entrypoint-api.sh
volumes:
- logs_api:/code/plane/logs
depends_on:
- plane-db
- plane-redis
- plane-mq
worker:
<<: *plane-env
image: makeplane/plane-backend:v0.23.1
restart: unless-stopped
command: ./bin/docker-entrypoint-worker.sh
volumes:
- logs_worker:/code/plane/logs
depends_on:
- api
- plane-db
- plane-redis
- plane-mq
beat-worker:
<<: *plane-env
image: makeplane/plane-backend:v0.23.1
restart: unless-stopped
command: ./bin/docker-entrypoint-beat.sh
volumes:
- logs_beat-worker:/code/plane/logs
depends_on:
- api
- plane-db
- plane-redis
- plane-mq
migrator:
<<: *plane-env
image: makeplane/plane-backend:v0.23.1
restart: "no"
command: ./bin/docker-entrypoint-migrator.sh
volumes:
- logs_migrator:/code/plane/logs
depends_on:
- plane-db
- plane-redis
plane-db:
<<: *plane-env
image: postgres:15.7-alpine
restart: unless-stopped
command: postgres -c 'max_connections=1000'
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 12
plane-redis:
<<: *plane-env
image: valkey/valkey:7.2.5-alpine
restart: unless-stopped
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
plane-mq:
<<: *plane-env
image: rabbitmq:3.13.6-management-alpine
restart: always
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 15s
timeout: 10s
retries: 12
plane-minio:
<<: *plane-env
# upstream-référence держит latest — bundle пиннит неподвижный тег (NFR-6)
image: minio/minio:RELEASE.2024-05-28T17-19-04Z
restart: unless-stopped
command: server /export --console-address ":9090"
volumes:
- uploads:/export
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 12
proxy:
<<: *plane-env
image: makeplane/plane-proxy:v0.23.1
restart: unless-stopped
ports:
# человеческая точка: UI Plane в браузере оператора (D4)
- "${BUNDLE_PLANE_PORT:-8080}:80"
depends_on:
- web
- api
- space
# Состояние Plane/Gitea — именованные тома проекта (префикс orchestrator-bundle_,
# D1/D2); preflight bootstrap детектирует «грязный хост» по этому префиксу.
volumes:
pgdata:
redisdata:
uploads:
logs_api:
logs_worker:
logs_beat-worker:
logs_migrator:
rabbitmq_data:
gitea-data: