diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8600b6e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,206 @@ +# Chrysopedia — Docker Compose +# XPLTD convention: xpltd_chrysopedia project, bind mounts, dedicated bridge +# Deployed to: /vmPool/r/compose/xpltd_chrysopedia/ (symlinked) +name: xpltd_chrysopedia + +services: + # ── PostgreSQL 16 ── + chrysopedia-db: + image: postgres:16-alpine + container_name: chrysopedia-db + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-chrysopedia} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} + POSTGRES_DB: ${POSTGRES_DB:-chrysopedia} + volumes: + - /vmPool/r/services/chrysopedia_db:/var/lib/postgresql/data + ports: + - "127.0.0.1:5433:5432" + networks: + - chrysopedia + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-chrysopedia}"] + interval: 10s + timeout: 5s + retries: 5 + stop_grace_period: 30s + + # ── Redis (Celery broker + runtime config) ── + chrysopedia-redis: + image: redis:7-alpine + container_name: chrysopedia-redis + restart: unless-stopped + command: redis-server --save 60 1 --loglevel warning + volumes: + - /vmPool/r/services/chrysopedia_redis:/data + networks: + - chrysopedia + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + stop_grace_period: 15s + + # ── Qdrant vector database ── + chrysopedia-qdrant: + image: qdrant/qdrant:v1.13.2 + container_name: chrysopedia-qdrant + restart: unless-stopped + volumes: + - /vmPool/r/services/chrysopedia_qdrant:/qdrant/storage + networks: + - chrysopedia + healthcheck: + test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/6333'"] + interval: 15s + timeout: 5s + retries: 5 + start_period: 10s + stop_grace_period: 30s + + # ── Ollama (embedding model server) ── + chrysopedia-ollama: + image: ollama/ollama:latest + container_name: chrysopedia-ollama + restart: unless-stopped + volumes: + - /vmPool/r/services/chrysopedia_ollama:/root/.ollama + networks: + - chrysopedia + healthcheck: + test: ["CMD", "ollama", "list"] + interval: 15s + timeout: 5s + retries: 5 + start_period: 30s + stop_grace_period: 15s + + # ── FastAPI application ── + chrysopedia-api: + build: + context: . + dockerfile: docker/Dockerfile.api + container_name: chrysopedia-api + restart: unless-stopped + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD:-changeme}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} + REDIS_URL: redis://chrysopedia-redis:6379/0 + QDRANT_URL: http://chrysopedia-qdrant:6333 + EMBEDDING_API_URL: http://chrysopedia-ollama:11434/v1 + PROMPTS_PATH: /prompts + volumes: + - /vmPool/r/services/chrysopedia_data:/data + - ./config:/config:ro + depends_on: + chrysopedia-db: + condition: service_healthy + chrysopedia-redis: + condition: service_healthy + chrysopedia-qdrant: + condition: service_healthy + chrysopedia-ollama: + condition: service_healthy + networks: + - chrysopedia + stop_grace_period: 15s + + # ── Celery worker (pipeline stages 2-6) ── + chrysopedia-worker: + build: + context: . + dockerfile: docker/Dockerfile.api + container_name: chrysopedia-worker + restart: unless-stopped + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD:-changeme}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} + REDIS_URL: redis://chrysopedia-redis:6379/0 + QDRANT_URL: http://chrysopedia-qdrant:6333 + EMBEDDING_API_URL: http://chrysopedia-ollama:11434/v1 + PROMPTS_PATH: /prompts + command: ["celery", "-A", "worker", "worker", "--loglevel=info", "--concurrency=1"] + healthcheck: + test: ["CMD-SHELL", "celery -A worker inspect ping --timeout=5 2>/dev/null | grep -q pong || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + volumes: + - /vmPool/r/services/chrysopedia_data:/data + - ./prompts:/prompts:ro + - ./config:/config:ro + depends_on: + chrysopedia-db: + condition: service_healthy + chrysopedia-redis: + condition: service_healthy + chrysopedia-qdrant: + condition: service_healthy + chrysopedia-ollama: + condition: service_healthy + networks: + - chrysopedia + stop_grace_period: 30s + + # ── Transcript folder watcher ── + chrysopedia-watcher: + build: + context: . + dockerfile: docker/Dockerfile.api + container_name: chrysopedia-watcher + restart: unless-stopped + command: ["python", "watcher.py"] + environment: + WATCHER_API_URL: http://chrysopedia-api:8000/api/v1/ingest + WATCH_FOLDER: /watch + volumes: + - /vmPool/r/services/chrysopedia_watch:/watch + depends_on: + chrysopedia-api: + condition: service_healthy + networks: + - chrysopedia + healthcheck: + test: ["CMD-SHELL", "python -c \"import os; os.kill(1, 0)\" || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s + stop_grace_period: 15s + + # ── React web UI (nginx) ── + chrysopedia-web: + build: + context: . + dockerfile: docker/Dockerfile.web + args: + VITE_GIT_COMMIT: ${GIT_COMMIT_SHA:-dev} + container_name: chrysopedia-web-8096 + restart: unless-stopped + ports: + - "0.0.0.0:8096:80" + depends_on: + - chrysopedia-api + networks: + - chrysopedia + healthcheck: + test: ["CMD-SHELL", "curl -sf http://127.0.0.1:80/ || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + stop_grace_period: 15s + +networks: + chrysopedia: + driver: bridge + ipam: + config: + - subnet: "172.32.0.0/24"