Files

1 line
428 KiB
JSON

{"repo": "camier/kannadoc", "files": {"scripts/README_VALIDATE_ENV.md": {"010a694d34dc4771f1cd9b8b7207501a270e1c06": {"t": 1785746605.05763, "n": 4350, "h": "ec07dea3e1218ae8", "c": "# Environment Validation Script\n\n## Overview\n\n`validate_env.py` validates your `.env` file against `.env.example` to ensure all required configuration variables are present and properly configured.\n\n## Features\n\n- **Missing Variable Detection**: Identifies required variables that are not set\n- **Placeholder Detection**: Finds variables that still contain placeholder values like `your_secret_here`\n- **Categorized Output**: Groups variables by type (Database, Security, API Keys, Services, Other)\n- **CI/CD Ready**: Returns proper exit codes (0 = valid, 1 = invalid)\n- **Strict Mode**: Optionally flag extra variables not in `.env.example`\n- **Flexible Paths**: Works with any `.env` file location\n\n## Usage\n\n### Basic Usage\n\nValidate the default `.env` file:\n\n```bash\npython3 scripts/validate_env.py\n```\n\n### Custom Environment File\n\nValidate a specific environment file:\n\n```bash\npython3 scripts/validate_env.py --env-file .env.production\n```\n\n### Strict Mode\n\nTreat extra variables (not in `.env.example`) as errors:\n\n```bash\npython3 scripts/validate_env.py --strict\n```\n\n### Verbose Output\n\nShow additional information:\n\n```bash\npython3 scripts/validate_env.py --verbose\n```\n\n### CI/CD Integration\n\n```bash\n# In your CI pipeline\npython3 scripts/validate_env.py || exit 1\n```\n\n## Exit Codes\n\n- `0` - Validation passed (all required variables present, no placeholders)\n- `1` - Validation failed (missing variables or placeholders detected)\n\n## Output Examples\n\n### Success Case\n\n```\n✅ ENVIRONMENT VALIDATION PASSED\n============================================================\nAll required variables are present and properly configured.\n```\n\n### Missing Variables\n\n```\n❌ MISSING REQUIRED VARIABLES\n============================================================\n\nSECURITY:\n - SECRET_KEY\n - REDIS_PASSWORD\n\nDATABASE:\n - MONGODB_URL\n - MINIO_ACCESS_KEY\n\nTotal missing: 4 variable(s)\n```\n\n### Placeholder Values\n\n```\n⚠️ PLACEHOLDER VALUES DETECTED\n============================================================\n\nThe following variables still contain placeholder values:\n - OPENAI_API_KEY=sk-your-openai-api-key-here\n - SECRET_KEY=change_me\n - REDIS_PASSWORD=your_secure_redis_password_here\n\nTotal placeholders: 3 variable(s)\n```\n\n## Required vs Optional Variables\n\n- **Required**: Variables in `.env.example` that are NOT commented out\n- **Optional**: Variables in `.env.example` that start with `#` (commented)\n\nExample:\n\n```bash\n# Required (not commented)\nOPENAI_API_KEY=sk-...\n\n# Optional (commented)\n# DEEPSEEK_API_KEY=sk-...\n```\n\n## Placeholder Patterns\n\nThe script detects these placeholder patterns:\n\n- `your_*_here` (e.g., `your_secret_here`)\n- `your-*-here` (e.g., `your-api-key-here`)\n- `change_me`\n- `change-this`\n- `placeholder`\n- `xxx`\n- `REPLACE_WITH`\n- `sk-*-*-here` (e.g., `sk-your-key-here`)\n\n## Integration with Docker Compose\n\nAdd to your deployment script:\n\n```bash\n#!/bin/bash\nset -e\n\necho \"Validating environment...\"\npython3 scripts/validate_env.py\n\necho \"Starting services...\"\ndocker-compose up -d\n```\n\n## Troubleshooting\n\n### False Positives\n\nIf a legitimate value is flagged as a placeholder, ensure it doesn't match any placeholder patterns. For example:\n\n- Bad: `OPENAI_API_KEY=sk-abc-here` (matches pattern)\n- Good: `OPENAI_API_KEY=sk-abc123xyz` (doesn't match)\n\n### Missing Variables\n\nIf variables are reported as missing but they exist in your `.env`:\n\n1. Check for typos in variable names\n2. Ensure the variable isn't commented out (no `#` at the start)\n3. Verify the format: `VARIABLE_NAME=value` (no spaces around `=`)\n\n### Adding New Variables\n\n1. Add to `.env.example` (uncomment if already present)\n2. Add to your `.env` file\n3. Run validation script to verify\n\n## Development\n\n### Testing\n\n```bash\n# Test with missing variables\npython3 scripts/validate_env.py --env-file /tmp/test_missing.env\n\n# Test with placeholders\npython3 scripts/validate_env.py --env-file /tmp/test_placeholders.env\n\n# Test valid env\npython3 scripts/validate_env.py --env-file /tmp/test_valid.env\n```\n\n### Code Quality\n\nThe script follows PEP 8 style guidelines and includes:\n- Type hints\n- Docstrings\n- Error handling\n- Clear separation of concerns\n\n## See Also\n\n- [Environment Configuration Best Practices](../docs/CONFIGURATION.md)\n- [Docker Compose Configuration](../docker-compose.yml)\n- [Deployment Guide](../README.md#deployment)\n"}, "4460c954a0a388f13a99e3be568162206f63357e": {"t": 1785766226.4618495, "n": 4350, "h": "ec07dea3e1218ae8", "c": "# Environment Validation Script\n\n## Overview\n\n`validate_env.py` validates your `.env` file against `.env.example` to ensure all required configuration variables are present and properly configured.\n\n## Features\n\n- **Missing Variable Detection**: Identifies required variables that are not set\n- **Placeholder Detection**: Finds variables that still contain placeholder values like `your_secret_here`\n- **Categorized Output**: Groups variables by type (Database, Security, API Keys, Services, Other)\n- **CI/CD Ready**: Returns proper exit codes (0 = valid, 1 = invalid)\n- **Strict Mode**: Optionally flag extra variables not in `.env.example`\n- **Flexible Paths**: Works with any `.env` file location\n\n## Usage\n\n### Basic Usage\n\nValidate the default `.env` file:\n\n```bash\npython3 scripts/validate_env.py\n```\n\n### Custom Environment File\n\nValidate a specific environment file:\n\n```bash\npython3 scripts/validate_env.py --env-file .env.production\n```\n\n### Strict Mode\n\nTreat extra variables (not in `.env.example`) as errors:\n\n```bash\npython3 scripts/validate_env.py --strict\n```\n\n### Verbose Output\n\nShow additional information:\n\n```bash\npython3 scripts/validate_env.py --verbose\n```\n\n### CI/CD Integration\n\n```bash\n# In your CI pipeline\npython3 scripts/validate_env.py || exit 1\n```\n\n## Exit Codes\n\n- `0` - Validation passed (all required variables present, no placeholders)\n- `1` - Validation failed (missing variables or placeholders detected)\n\n## Output Examples\n\n### Success Case\n\n```\n✅ ENVIRONMENT VALIDATION PASSED\n============================================================\nAll required variables are present and properly configured.\n```\n\n### Missing Variables\n\n```\n❌ MISSING REQUIRED VARIABLES\n============================================================\n\nSECURITY:\n - SECRET_KEY\n - REDIS_PASSWORD\n\nDATABASE:\n - MONGODB_URL\n - MINIO_ACCESS_KEY\n\nTotal missing: 4 variable(s)\n```\n\n### Placeholder Values\n\n```\n⚠️ PLACEHOLDER VALUES DETECTED\n============================================================\n\nThe following variables still contain placeholder values:\n - OPENAI_API_KEY=sk-your-openai-api-key-here\n - SECRET_KEY=change_me\n - REDIS_PASSWORD=your_secure_redis_password_here\n\nTotal placeholders: 3 variable(s)\n```\n\n## Required vs Optional Variables\n\n- **Required**: Variables in `.env.example` that are NOT commented out\n- **Optional**: Variables in `.env.example` that start with `#` (commented)\n\nExample:\n\n```bash\n# Required (not commented)\nOPENAI_API_KEY=sk-...\n\n# Optional (commented)\n# DEEPSEEK_API_KEY=sk-...\n```\n\n## Placeholder Patterns\n\nThe script detects these placeholder patterns:\n\n- `your_*_here` (e.g., `your_secret_here`)\n- `your-*-here` (e.g., `your-api-key-here`)\n- `change_me`\n- `change-this`\n- `placeholder`\n- `xxx`\n- `REPLACE_WITH`\n- `sk-*-*-here` (e.g., `sk-your-key-here`)\n\n## Integration with Docker Compose\n\nAdd to your deployment script:\n\n```bash\n#!/bin/bash\nset -e\n\necho \"Validating environment...\"\npython3 scripts/validate_env.py\n\necho \"Starting services...\"\ndocker-compose up -d\n```\n\n## Troubleshooting\n\n### False Positives\n\nIf a legitimate value is flagged as a placeholder, ensure it doesn't match any placeholder patterns. For example:\n\n- Bad: `OPENAI_API_KEY=sk-abc-here` (matches pattern)\n- Good: `OPENAI_API_KEY=sk-abc123xyz` (doesn't match)\n\n### Missing Variables\n\nIf variables are reported as missing but they exist in your `.env`:\n\n1. Check for typos in variable names\n2. Ensure the variable isn't commented out (no `#` at the start)\n3. Verify the format: `VARIABLE_NAME=value` (no spaces around `=`)\n\n### Adding New Variables\n\n1. Add to `.env.example` (uncomment if already present)\n2. Add to your `.env` file\n3. Run validation script to verify\n\n## Development\n\n### Testing\n\n```bash\n# Test with missing variables\npython3 scripts/validate_env.py --env-file /tmp/test_missing.env\n\n# Test with placeholders\npython3 scripts/validate_env.py --env-file /tmp/test_placeholders.env\n\n# Test valid env\npython3 scripts/validate_env.py --env-file /tmp/test_valid.env\n```\n\n### Code Quality\n\nThe script follows PEP 8 style guidelines and includes:\n- Type hints\n- Docstrings\n- Error handling\n- Clear separation of concerns\n\n## See Also\n\n- [Environment Configuration Best Practices](../docs/CONFIGURATION.md)\n- [Docker Compose Configuration](../docker-compose.yml)\n- [Deployment Guide](../README.md#deployment)\n"}}, "docker-compose.yml": {"010a694d34dc4771f1cd9b8b7207501a270e1c06": {"t": 1785746977.9213512, "n": 15954, "h": "b49b08ef31edb140", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n # --- Milvus 向量数据库服务 ---\n # NOTE:\n # - Dev uses an in-compose Milvus by default to avoid \"host Milvus required\" drift.\n # - For an external Milvus, set MILVUS_URI in `.env` and optionally remove/stop these services.\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use in-compose Milvus (milvus-standalone). Override in `.env` for external Milvus.\n - MILVUS_URI=${MILVUS_URI:-http://milvus-standalone:19530}\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:80\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "97cecb12d9e71fa1aff37e14e6b8b582c5a3480e": {"t": 1785766228.3560858, "n": 15954, "h": "b49b08ef31edb140", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n # --- Milvus 向量数据库服务 ---\n # NOTE:\n # - Dev uses an in-compose Milvus by default to avoid \"host Milvus required\" drift.\n # - For an external Milvus, set MILVUS_URI in `.env` and optionally remove/stop these services.\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use in-compose Milvus (milvus-standalone). Override in `.env` for external Milvus.\n - MILVUS_URI=${MILVUS_URI:-http://milvus-standalone:19530}\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:80\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "18b5d073a03f36c9d6ab24f23ecd04919b8d7e49": {"t": 1785766230.2065694, "n": 13586, "h": "90779efc318e45e5", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use the host/system Milvus stack via host.docker.internal.\n - MILVUS_URI=${MILVUS_URI:-http://host.docker.internal:19530}\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:80\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "89a3ad2f14d3e8eb2c415730f6b1abf52e02008e": {"t": 1785766231.608507, "n": 13550, "h": "4001fa791fc65e55", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use the host/system Milvus stack via host.docker.internal.\n - MILVUS_URI=${MILVUS_URI:-http://host.docker.internal:19530}\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:3000\"]\n interval: 30s\n timeout: 10s\n retries: 3\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:80\"]\n interval: 30s\n timeout: 10s\n retries: 3\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "f82be3ae0d4aae6ba32d4698e973aceaaca72374": {"t": 1785766233.2171443, "n": 13264, "h": "c0538e23c2c6aecf", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use the host/system Milvus stack via host.docker.internal.\n - MILVUS_URI=${MILVUS_URI:-http://host.docker.internal:19530}\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "a3f59e229a91f36653a40155ed6e50ba1b8e410f": {"t": 1785766234.6283233, "n": 13302, "h": "2f14e85d1a046842", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use the host/system Milvus stack via host.docker.internal.\n - MILVUS_URI=${MILVUS_URI:-http://host.docker.internal:19530}\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "b47afe8b641add90bc7d5b69f5e377d3647f4c6b": {"t": 1785766235.927244, "n": 15703, "h": "bba1c0a6b38534e4", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n # Milvus 2.6 can also require these explicit keys\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n # Avoid clashing with a host/system Milvus on :19530\n ports:\n - \"127.0.0.1:19531:19530\"\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Default: use the docker-compose Milvus stack (milvus-standalone).\n # If you want a host/system Milvus, set MILVUS_URI explicitly in .env.\n - MILVUS_URI=${MILVUS_URI:-http://milvus-standalone:19530}\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "861134b91c4c488bd8a64c005957b079289409de": {"t": 1785766237.3497097, "n": 13387, "h": "5cc80eb635193c63", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Thesis deployment uses the host Milvus (holds the 3.56M patch vectors).\n # Default keeps local/dev compose working when MILVUS_URI isn't provided.\n - MILVUS_URI=${MILVUS_URI:-http://host.docker.internal:19530}\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "72bf40ee8a98fdae94e5e1a701ed0835aed0d3be": {"t": 1785766238.80586, "n": 15597, "h": "b2e49efb3643919b", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n ports:\n - \"127.0.0.1:19531:19530\"\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n # Thesis deployment uses the host Milvus (holds the 3.56M patch vectors).\n # Default keeps local/dev compose working when MILVUS_URI isn't provided.\n - MILVUS_URI=${MILVUS_URI:-http://milvus-standalone:19530}\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Thesis retrieval mode (sparse recall -> ColPali rerank, optionally fused with dense recall)\n - RAG_RETRIEVAL_MODE=${RAG_RETRIEVAL_MODE:-dense}\n - RAG_PAGES_SPARSE_SUFFIX=${RAG_PAGES_SPARSE_SUFFIX:-_pages_sparse}\n - RAG_DEFAULT_TOP_K=${RAG_DEFAULT_TOP_K:-50}\n - RAG_TOP_K_CAP=${RAG_TOP_K_CAP:-120}\n - RAG_DIVERSE_FILE_LIMIT=${RAG_DIVERSE_FILE_LIMIT:-20}\n - RAG_DIVERSE_PAGES_PER_FILE_CAP=${RAG_DIVERSE_PAGES_PER_FILE_CAP:-3}\n - RAG_FALLBACK_TEXT_CAP=${RAG_FALLBACK_TEXT_CAP:-20}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - OLLAMA_CLOUD_API_KEY=${OLLAMA_CLOUD_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "a852d5adaad6d9eb9f6bf6e01d37a7ce1d1f12c2": {"t": 1785766240.3093581, "n": 14846, "h": "2fcb92622c5e4379", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n ports:\n - \"127.0.0.1:19531:19530\"\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "45ef054225f2f26ea3b7851c0706ae823bdfc5b2": {"t": 1785766241.7821667, "n": 14803, "h": "48b247e0c13a7a26", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "cda05d634b45e7db312b86597e5efbcca92fff15": {"t": 1785766243.6994805, "n": 15033, "h": "23791cb2891acd0e", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/health/ready\"]\n interval: 30s\n start_period: 120s\n timeout: 10s\n retries: 15\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "9fe46ae5fe8feddbe8c53759f3ef65332b4682a3": {"t": 1785766245.116493, "n": 14989, "h": "78fc943fe03a51e8", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n frontend:\n condition: service_started\n backend:\n condition: service_healthy\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:v3.5.1\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:12.3.2\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "7bf1ab16aa9386a606acb8c6c526e3393f2d8613": {"t": 1785766247.8917897, "n": 14921, "h": "5eb7781485f75edb", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"mysqladmin ping -h localhost -u root -p\\\"$MYSQL_ROOT_PASSWORD\\\"\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://milvus-etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ROOT_USER=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_ROOT_PASSWORD=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "42ee9e46d557d6b253db1b17b2c72c1a49fb6cd5": {"t": 1785766249.4572523, "n": 14943, "h": "7c133410982324b1", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "cf0d77f1fceb3f230ca03a748577d10076cd4f93": {"t": 1785766251.05533, "n": 14952, "h": "7ba1bff64763791e", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n cliproxyapi:\n container_name: layra-cliproxyapi\n image: eceasy/cli-proxy-api:latest\n ports:\n - \"8085:8085\"\n - \"8317:8317\"\n volumes:\n - ${HOME}/.cli-proxy-api/config.yaml:/CLIProxyAPI/config.yaml:ro\n - cliproxyapi_auth:/root/.cli-proxy-api\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8317/v1/models\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 5s\n\n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - CLIPROXYAPI_BASE_URL=${CLIPROXYAPI_BASE_URL:-}\n - CLIPROXYAPI_API_KEY=${CLIPROXYAPI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n cliproxyapi_auth:\n"}, "b877c258a0a333977845cc58feb836ec5e824f1f": {"t": 1785766253.1056566, "n": 14280, "h": "66d80fc7bc341df8", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - ZAI_API_KEY=${ZAI_API_KEY:-}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "3c1f4cc0b7085035d67b568118e8271e5e81927d": {"t": 1785766254.7783635, "n": 14243, "h": "9035c95510a5bcc3", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # Hybrid Search Configuration\n - RAG_HYBRID_ENABLED=${RAG_HYBRID_ENABLED:-false}\n - RAG_HYBRID_RANKER=${RAG_HYBRID_RANKER:-rrf}\n - RAG_HYBRID_RRF_K=${RAG_HYBRID_RRF_K:-60}\n - RAG_HYBRID_DENSE_WEIGHT=${RAG_HYBRID_DENSE_WEIGHT:-0.7}\n - RAG_HYBRID_SPARSE_WEIGHT=${RAG_HYBRID_SPARSE_WEIGHT:-0.3}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "7eebb55e6bb6addbbb8a14c9454e193422e70750": {"t": 1785766256.441731, "n": 13920, "h": "1a9e6412b650a7b6", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # RAG Tuning (optional - defaults in config.py)\n - RAG_MAX_QUERY_VECS=${RAG_MAX_QUERY_VECS:-48}\n - RAG_SEARCH_LIMIT_CAP=${RAG_SEARCH_LIMIT_CAP:-120}\n - RAG_CANDIDATE_IMAGES_CAP=${RAG_CANDIDATE_IMAGES_CAP:-120}\n - RAG_SEARCH_LIMIT_MIN=${RAG_SEARCH_LIMIT_MIN:-50}\n - RAG_EF_MIN=${RAG_EF_MIN:-100}\n - RAG_LOAD_COLLECTION_ONCE=${RAG_LOAD_COLLECTION_ONCE:-true}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "1cd467072cb918737d4df2fc7da9c00b57942c6c": {"t": 1785766257.6739433, "n": 13527, "h": "cec8353c61b76c3e", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n # Local thesis dev: expose MinIO for browser-accessible presigned URLs.\n # Host port 9080 maps to container port 9000 (system minio uses 9000)\n ports:\n - \"127.0.0.1:9080:9000\"\n - \"127.0.0.1:9081:9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n - MINIO_ACCESS_KEY_ID=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_ACCESS_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n # LLM Provider Keys\n - OPENAI_API_KEY=${OPENAI_API_KEY}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}\n - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY}\n - MOONSHOT_API_KEY=${MOONSHOT_API_KEY}\n - MINIMAX_API_KEY=${MINIMAX_API_KEY}\n - COHERE_API_KEY=${COHERE_API_KEY}\n - OLLAMA_API_KEY=${OLLAMA_API_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n - GEMINI_API_KEY=${GEMINI_API_KEY:-}\n - DEFAULT_LLM_PROVIDER=${DEFAULT_LLM_PROVIDER:-openai}\n - DEFAULT_LLM_MODEL=${DEFAULT_LLM_MODEL:-gpt-4o-mini}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "13ff394e1a5d12f22fd6de473e032aa5f5ebcde5": {"t": 1785766260.0402982, "n": 12840, "h": "f381b48375b886df", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n qdrant:\n container_name: layra-qdrant\n image: qdrant/qdrant:v1.16.2\n restart: unless-stopped\n ports:\n - \"6333:6333\"\n - \"6334:6334\"\n environment:\n - QDRANT__SERVICE__HTTP_PORT=6333\n - QDRANT__SERVICE__METRICS_PORT=6334\n volumes:\n - qdrant_data:/qdrant/storage\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"curl\", \"-f\", \"http://localhost:6333/healthz\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n - GF_USERS_ALLOW_SIGN_UP=false\n - GF_SERVER_ROOT_URL=http://localhost:3001\n - GF_INSTALL_PLUGINS=\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/provisioning:/etc/grafana/provisioning\n - ./grafana/dashboards:/etc/grafana/provisioning/dashboards\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n qdrant_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "4460c954a0a388f13a99e3be568162206f63357e": {"t": 1785766261.8938973, "n": 12603, "h": "a4028429ae5eed99", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n qdrant:\n container_name: layra-qdrant\n image: qdrant/qdrant:v1.16.2\n restart: unless-stopped\n ports:\n - \"6333:6333\"\n - \"6334:6334\"\n environment:\n - QDRANT__SERVICE__HTTP_PORT=6333\n - QDRANT__SERVICE__METRICS_PORT=6334\n volumes:\n - qdrant_data:/qdrant/storage\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"curl\", \"-f\", \"http://localhost:6333/healthz\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL}\n - MINIO_PUBLIC_PORT=${MINIO_PUBLIC_PORT:-9000}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - MODEL_SERVER_URL=${MODEL_SERVER_URL:-http://model-server:8005}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n volumes:\n - grafana_data:/var/lib/grafana\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n qdrant_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "0f7b54d4d434da6743774c20e12f43c0727e4b62": {"t": 1785766263.4508586, "n": 12434, "h": "d44cbbfa85b30819", "c": "services:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:8.0\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}\n - MYSQL_DATABASE=${MYSQL_DATABASE:-layra}\n - MYSQL_USER=${MYSQL_USER:-layra}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD:-layra}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-p$$MYSQL_ROOT_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n qdrant:\n container_name: layra-qdrant\n image: qdrant/qdrant:v1.16.2\n restart: unless-stopped\n ports:\n - \"6333:6333\"\n - \"6334:6334\"\n environment:\n - QDRANT__SERVICE__HTTP_PORT=6333\n - QDRANT__SERVICE__METRICS_PORT=6334\n volumes:\n - qdrant_data:/qdrant/storage\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"curl\", \"-f\", \"http://localhost:6333/healthz\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=${MILVUS_MINIO_ACCESS_KEY:-minioadmin}\n - MINIO_SECRET_KEY=${MILVUS_MINIO_SECRET_KEY:-minioadmin}\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n - ./backend:/app\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - SINGLE_TENANT_MODE=${SINGLE_TENANT_MODE:-false}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - QDRANT_URL=http://qdrant:6333\n - VECTOR_DB=${VECTOR_DB:-milvus}\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORT=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - TRANSFORMERS_OFFLINE=1\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=${HF_TOKEN:-}\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n volumes:\n - grafana_data:/var/lib/grafana\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n external: true\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n qdrant_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "bfda3eee7b62d8a50965105b91a6d550e1be8618": {"t": 1785766265.2647698, "n": 11864, "h": "f59f78a7782005f9", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.6.9\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=0\n - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.9,max_split_size_mb:32\n - PYTORCH_NO_CUDA_MEMORY_CACHING=1\n - PYTORCH_CUDA_MEMORY_FRACTION=0.9\n - TOKENIZERS_PARALLELISM=false\n - REDIS_HOST=redis\n - REDIS_PORT=6379\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - HF_TOKEN=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\n - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface\n shm_size: 8gb\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\n prometheus:\n image: prom/prometheus:latest\n container_name: layra-prometheus\n volumes:\n - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml\n - prometheus_data:/prometheus\n command:\n - '--config.file=/etc/prometheus/prometheus.yml'\n - '--storage.tsdb.path=/prometheus'\n networks:\n - layra-net\n ports:\n - \"9090:9090\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: layra-grafana\n depends_on:\n - prometheus\n ports:\n - \"3001:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}\n volumes:\n - grafana_data:/var/lib/grafana\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n\nvolumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n prometheus_data:\n grafana_data:\n"}, "f32f6b99aff37ed45f6c84029ab0bec6013ab351": {"t": 1785766266.495826, "n": 10667, "h": "d6be6de2c6899ac2", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n layra_sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n container_name: layra-kafka-init\n build:\n context: ./init-kafka\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n container_name: layra-kafka\n image: apache/kafka:3.8.0\n environment:\n - KAFKA_NODE_ID=0\n - KAFKA_PROCESS_ROLES=controller,broker\n - KAFKA_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_DEFAULT_REPLICATION_FACTOR=1\n - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\n - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\n - KAFKA_MIN_INSYNC_REPLICAS=1\n - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10}\n volumes:\n - kafka_data:/var/lib/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"sh\", \"-c\", \"/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list\"]\n interval: 15s\n timeout: 10s\n retries: 15\n start_period: 30s\n\n minio:\n container_name: layra-minio\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n container_name: layra-mongodb\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n container_name: layra-redis\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli -a \\\"${REDIS_PASSWORD}\\\" ping | grep -q PONG\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n container_name: layra-mysql\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n # --- Milvus Vector Database Services ---\n milvus-etcd:\n container_name: layra-milvus-etcd\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n container_name: layra-milvus-minio\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n container_name: layra-milvus-standalone\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n container_name: layra-python-sandbox\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - layra_sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n container_name: layra-model-weights-init\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n deploy:\n resources:\n limits:\n memory: 4G\n reservations:\n memory: 1G\n\n unoserver:\n container_name: layra-unoserver\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1}\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n container_name: layra-backend\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n - layra_sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=http://milvus-standalone:19530\n - KAFKA_RETRY_BACKOFF_MS=${KAFKA_RETRY_BACKOFF_MS:-5000}\n - KAFKA_SESSION_TIMEOUT_MS=${KAFKA_SESSION_TIMEOUT_MS:-30000}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n - SIMPLE_AUTH_MODE=${SIMPLE_AUTH_MODE:-false}\n - SIMPLE_API_KEY=${SIMPLE_API_KEY:-layra-dev-key-2024}\n - SIMPLE_USERNAME=${SIMPLE_USERNAME:-layra}\n - SIMPLE_PASSWORD=${SIMPLE_PASSWORD:-layra123}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n container_name: layra-model-server\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - CUDA_VISIBLE_DEVICES=\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n\n frontend:\n container_name: layra-frontend\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n container_name: layra-nginx\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"8090:80\"\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "e646bef9f087f1ff0f369067bff2ead3c460dddf": {"t": 1785766268.4976368, "n": 9631, "h": "d3be6015bb136876", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n build:\n context: ./init-kafka # 指向包含 Dockerfile 和脚本的目录\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_CFG_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10} # 添加分区数配置\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n # 新增 unoserver 服务\n unoserver:\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1} # 实例数=CPU核心数\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"] # 检查第一个实例\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDINGS_V4_URL=${JINA_EMBEDDINGS_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "851a7f7e7413686f65949342ca8b78923570584d": {"t": 1785766270.5399013, "n": 9629, "h": "24188031930513e0", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n build:\n context: ./init-kafka # 指向包含 Dockerfile 和脚本的目录\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_CFG_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10} # 添加分区数配置\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n # 新增 unoserver 服务\n unoserver:\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1} # 实例数=CPU核心数\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"] # 检查第一个实例\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n - EMBEDDING_IMAGE_DPI=${EMBEDDING_IMAGE_DPI}\n - EMBEDDING_MODEL=${EMBEDDING_MODEL}\n - JINA_API_KEY=${JINA_API_KEY}\n - JINA_EMBEDDING_V4_URL=${JINA_EMBEDDING_V4_URL}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "66841658708590dc3043d0c1e9d7d8d5f5e32145": {"t": 1785766272.9966712, "n": 9443, "h": "2d56228952a9dda5", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n build:\n context: ./init-kafka # 指向包含 Dockerfile 和脚本的目录\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_CFG_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10} # 添加分区数配置\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n # 新增 unoserver 服务\n unoserver:\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1} # 实例数=CPU核心数\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"] # 检查第一个实例\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINIO_IMAGE_URL_PREFIX=${MINIO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "4c86dc69b851090c55f5cd959cdd15002fc063f3": {"t": 1785766274.5312378, "n": 9441, "h": "530c21c5fb7e2fea", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n build:\n context: ./init-kafka # 指向包含 Dockerfile 和脚本的目录\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_CFG_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10} # 添加分区数配置\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n # 新增 unoserver 服务\n unoserver:\n build:\n context: ./unoserver\n environment:\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES:-1} # 实例数=CPU核心数\n - BASE_PORT=${UNOSERVER_BASE_PORT}\n - BASE_UNO_PORT=${UNOSERVER_BASE_UNO_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"nc\", \"-z\", \"localhost\", \"2003\"] # 检查第一个实例\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 10s\n \n backend:\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n unoserver:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n - UNOSERVER_INSTANCES=${UNOSERVER_INSTANCES}\n - UNOSERVER_HOST=${UNOSERVER_HOST}\n - UNOSERVER_BASE_PORTS=${UNOSERVER_BASE_PORT}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINO_IMAGE_URL_PREFIX=${MINO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "a13f26452c49185300dc0134dd5bdc5ae8bfb10a": {"t": 1785766275.8021595, "n": 8799, "h": "a8e1ab5e7a1bae94", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka-init:\n build:\n context: ./init-kafka # 指向包含 Dockerfile 和脚本的目录\n depends_on:\n kafka:\n condition: service_healthy\n environment:\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_PARTITIONS_NUMBER=${KAFKA_PARTITIONS_NUMBER}\n - KAFKA_REPLICATION_FACTOR=${KAFKA_REPLICATION_FACTOR:-1}\n networks:\n - layra-net\n\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n - KAFKA_CFG_NUM_PARTITIONS=${KAFKA_PARTITIONS_NUMBER:-10} # 添加分区数配置\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n backend:\n build:\n context: ./backend\n depends_on:\n kafka-init:\n condition: service_completed_successfully\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINO_IMAGE_URL_PREFIX=${MINO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}, "e3fa94356a830b7a8d40274f0b55a37997f9db9c": {"t": 1785766277.4135113, "n": 8305, "h": "1bca222453e5f3e4", "c": "volumes:\n kafka_data:\n minio_data:\n redis_data:\n mysql_data:\n mongo_data:\n milvus_etcd:\n milvus_minio:\n milvus_data:\n model_weights:\n sandbox_volume:\n mysql_migrations:\n\nservices:\n # --- 基础设施服务 ---\n kafka:\n image: bitnami/kafka:3.8.0\n environment:\n - KAFKA_CFG_NODE_ID=0\n - KAFKA_CFG_PROCESS_ROLES=controller,broker\n - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093\n - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094\n - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://kafka:9094\n - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT\n - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER\n - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT\n - KAFKA_MAX_MESSAGE_SIZE=2097152000\n - KAFKA_MESSAGE_MAX_BYTES=2097152000\n volumes:\n - kafka_data:/bitnami/kafka/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"kafka-topics.sh\", \"--bootstrap-server\", \"localhost:9092\", \"--list\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n minio:\n image: minio/minio:RELEASE.2024-10-13T13-34-11Z\n environment:\n - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}\n - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}\n command: server /data --console-address \":9001\"\n volumes:\n - minio_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n mongodb:\n image: mongo:7.0.12\n environment:\n - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n volumes:\n - mongo_data:/data/db\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n redis:\n image: redis:7.2.5\n command: [\"redis-server\", \"--requirepass\", \"${REDIS_PASSWORD}\"]\n volumes:\n - redis_data:/data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n mysql:\n image: mysql:9.0.1\n environment:\n - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}\n - MYSQL_DATABASE=${MYSQL_DATABASE}\n - MYSQL_USER=${MYSQL_USER}\n - MYSQL_PASSWORD=${MYSQL_PASSWORD}\n volumes:\n - mysql_data:/var/lib/mysql\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"$$MYSQL_USER\", \"-p$$MYSQL_PASSWORD\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-etcd:\n image: quay.io/coreos/etcd:v3.5.18\n environment:\n - ETCD_AUTO_COMPACTION_MODE=revision\n - ETCD_AUTO_COMPACTION_RETENTION=1000\n - ETCD_QUOTA_BACKEND_BYTES=4294967296\n - ETCD_SNAPSHOT_COUNT=50000\n volumes:\n - milvus_etcd:/etcd\n command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"etcdctl\", \"endpoint\", \"health\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-minio:\n image: minio/minio:RELEASE.2023-03-20T20-16-18Z\n environment:\n - MINIO_ACCESS_KEY=minioadmin\n - MINIO_SECRET_KEY=minioadmin\n command: minio server /minio_data --console-address \":9001\"\n volumes:\n - milvus_minio:/minio_data\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9000/minio/health/live\"]\n interval: 10s\n timeout: 5s\n retries: 10\n\n milvus-standalone:\n image: milvusdb/milvus:v2.5.6\n command: [\"milvus\", \"run\", \"standalone\"]\n security_opt:\n - seccomp:unconfined\n environment:\n - ETCD_ENDPOINTS=milvus-etcd:2379\n - MINIO_ADDRESS=milvus-minio:9000\n volumes:\n - milvus_data:/var/lib/milvus\n depends_on:\n milvus-etcd:\n condition: service_healthy\n milvus-minio:\n condition: service_healthy\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:9091/healthz\"]\n interval: 10s\n start_period: 30s\n timeout: 5s\n retries: 30\n\n # --- 应用服务 ---\n python-sandbox:\n build:\n context: ./sandbox\n image: python-sandbox\n volumes:\n - sandbox_volume:/shared\n networks:\n - layra-net\n\n model-weights-init:\n build:\n context: ./init-db\n dockerfile: Dockerfile\n volumes:\n - model_weights:/model_weights\n environment:\n - MODEL_BASE_URL=${MODEL_BASE_URL}\n networks:\n - layra-net\n\n backend:\n build:\n context: ./backend\n depends_on:\n mysql:\n condition: service_healthy\n redis:\n condition: service_healthy\n mongodb:\n condition: service_healthy\n kafka:\n condition: service_healthy\n minio:\n condition: service_healthy\n milvus-standalone:\n condition: service_healthy\n model-server:\n condition: service_healthy\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n - /var/run/docker.sock:/var/run/docker.sock\n - sandbox_volume:${SANDBOX_SHARED_VOLUME}\n - mysql_migrations:/app/migrations_previous\n environment:\n - MAX_WORKERS=${MAX_WORKERS}\n - LOG_LEVEL=${LOG_LEVEL}\n - DB_URL=${DB_URL}\n - DB_POOL_SIZE=${DB_POOL_SIZE}\n - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW}\n - REDIS_URL=${REDIS_URL}\n - REDIS_PASSWORD=${REDIS_PASSWORD}\n - REDIS_TOKEN_DB=${REDIS_TOKEN_DB}\n - REDIS_TASK_DB=${REDIS_TASK_DB}\n - REDIS_LOCK_DB=${REDIS_LOCK_DB}\n - SECRET_KEY=${SECRET_KEY}\n - ALGORITHM=${ALGORITHM}\n - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}\n - MONGODB_URL=${MONGODB_URL}\n - MONGODB_ROOT_USERNAME=${MONGODB_ROOT_USERNAME}\n - MONGODB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD}\n - MONGODB_DB=${MONGODB_DB}\n - MONGODB_POOL_SIZE=${MONGODB_POOL_SIZE}\n - MONGODB_MIN_POOL_SIZE=${MONGODB_MIN_POOL_SIZE}\n - KAFKA_BROKER_URL=${KAFKA_BROKER_URL}\n - KAFKA_TOPIC=${KAFKA_TOPIC}\n - KAFKA_GROUP_ID=${KAFKA_GROUP_ID}\n - MINIO_URL=${MINIO_URL}\n - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}\n - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}\n - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}\n - DEBUG_MODE=${DEBUG_MODE}\n - MILVUS_URI=${MILVUS_URI}\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - SANDBOX_SHARED_VOLUME=${SANDBOX_SHARED_VOLUME}\n - SERVER_IP=${SERVER_IP}\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/api/v1/health/check\"]\n interval: 30s\n start_period: 10s\n timeout: 5s\n retries: 10\n\n model-server:\n build:\n context: ./model-server\n depends_on:\n model-weights-init:\n condition: service_completed_successfully\n volumes:\n - model_weights:/model_weights\n environment:\n - COLBERT_MODEL_PATH=${COLBERT_MODEL_PATH}\n - NVIDIA_VISIBLE_DEVICES=all # 暴露所有 GPU\n networks:\n - layra-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8005/healthy-check\"]\n interval: 30s\n start_period: 30s\n timeout: 5s\n retries: 10\n # 添加 GPU 支持配置\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1 # 使用 1 块 GPU\n capabilities: [gpu]\n\n frontend:\n build:\n context: ./frontend\n args:\n - NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}\n - MINO_IMAGE_URL_PREFIX=${MINO_IMAGE_URL_PREFIX}\n - MINIO_URL=${MINIO_URL}\n depends_on:\n backend:\n condition: service_healthy\n environment:\n - NODE_ENV=${NODE_ENV}\n networks:\n - layra-net\n\n nginx:\n image: nginx:alpine\n depends_on:\n - frontend\n - backend\n ports:\n - \"80:80\"\n # - \"443:443\" # 如需 HTTPS 可启用\n volumes:\n - ./frontend/nginx.conf:/etc/nginx/nginx.conf\n # - ./frontend/ssl:/etc/nginx/ssl # 如需 HTTPS\n networks:\n - layra-net\n\nnetworks:\n layra-net:\n driver: bridge\n"}}}}