## # Kneron Model Converter — Development docker-compose # # Usage: # docker-compose up # local mode (shared volume) # STORAGE_BACKEND=s3 docker-compose up # S3/MinIO mode # docker-compose up --scale bie-worker=3 # scale BIE workers ## volumes: job-data: services: # ---------- Infrastructure ---------- redis: image: redis:7-alpine expose: - "6379" command: redis-server --save "" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 3 # ---------- Web UI ---------- web: build: ./apps/web ports: - "9500:3000" depends_on: scheduler: condition: service_healthy restart: unless-stopped # ---------- Scheduler ---------- # # T10:Phase 1 env 透傳清單。所有值都用 ${VAR} 從 .env / shell 讀取, # 不在 docker-compose.yml hardcode(避免 secret 被 commit)。 # 必填變數缺漏 → scheduler container 會啟動失敗(fail-fast)。 scheduler: build: ./apps/task-scheduler ports: - "9501:4000" depends_on: redis: condition: service_healthy volumes: - job-data:/data/jobs environment: # === 應用基本 === - PORT=4000 - NODE_ENV=${NODE_ENV:-development} - LOG_LEVEL=${LOG_LEVEL:-info} # === Redis === - REDIS_URL=redis://redis:6379 # === Job 資料目錄 / CORS === - JOB_DATA_DIR=/data/jobs - FRONTEND_URL=${FRONTEND_URL:-http://localhost:9500} # === Storage backend === - STORAGE_BACKEND=${STORAGE_BACKEND:-local} - MINIO_ENDPOINT_URL=${MINIO_ENDPOINT_URL:-http://192.168.0.130:9000} - MINIO_BUCKET=${MINIO_BUCKET:-convertet-working-space} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-convuser} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_REGION=${MINIO_REGION:-us-east-1} - MINIO_LIFECYCLE_DAYS=${MINIO_LIFECYCLE_DAYS:-7} # === Phase 0.8b A2:visionA → converter API key 認證(1:1 internal trust)=== # 不能給 default — 必須使用者明示設置;未設時 apiKeyMiddleware 對外 API 一律回 503。 # 兩端(converter / visionA)值必須完全一致;rotate 時雙端同時更新。 - CONVERTER_API_KEY=${CONVERTER_API_KEY} # === Phase 0.8b A7:trust proxy(audit log forensic source_ip)=== # stage Nginx 1 hop → 1;prod cloud LB + Nginx → 2;明確 CIDR 也支援 - TRUST_PROXY=${TRUST_PROXY:-loopback} # === Member Center Token Endpoint(converter → FAA OAuth client 用,保留)=== # visionA → converter 已改 API key 不再走 OAuth/JWKS;但 converter promote # 階段仍以 client_credentials grant 跟 Member Center 取 token、再 PUT 到 FAA。 - MEMBER_CENTER_TOKEN_URL=${MEMBER_CENTER_TOKEN_URL} # === Converter OAuth Client 身份(converter → FAA 用,必填)=== - KNERON_CONVERTER_CLIENT_ID=${KNERON_CONVERTER_CLIENT_ID} - KNERON_CONVERTER_CLIENT_SECRET=${KNERON_CONVERTER_CLIENT_SECRET} # === File Access Agent(必填)=== - FILE_ACCESS_AGENT_BASE_URL=${FILE_ACCESS_AGENT_BASE_URL} - FILE_ACCESS_AGENT_AUDIENCE=${FILE_ACCESS_AGENT_AUDIENCE} # === OAuth Client cache(可選)=== - OAUTH_TOKEN_REFRESH_SKEW_MS=${OAUTH_TOKEN_REFRESH_SKEW_MS:-60000} - OAUTH_TOKEN_TIMEOUT_MS=${OAUTH_TOKEN_TIMEOUT_MS:-10000} # === Promote 行為(可選)=== - PROMOTE_TIMEOUT_MS=${PROMOTE_TIMEOUT_MS:-300000} # === Multipart 上限(T10 修 D5)=== - MULTIPART_MODEL_MAX_BYTES=${MULTIPART_MODEL_MAX_BYTES:-524288000} - MULTIPART_REF_IMAGE_MAX_BYTES=${MULTIPART_REF_IMAGE_MAX_BYTES:-10485760} - MULTIPART_REF_IMAGES_MAX_COUNT=${MULTIPART_REF_IMAGES_MAX_COUNT:-100} # === Upload concurrency(T10 修 D5)=== - MAX_CONCURRENT_UPLOADS=${MAX_CONCURRENT_UPLOADS:-5} - UPLOAD_RETRY_AFTER_SECONDS=${UPLOAD_RETRY_AFTER_SECONDS:-30} # === Phase 0.8b Phase B:/result rate limit(per token_fingerprint)=== # 命名以 §16.2.1 canonical names 為準(_PER_MIN + _WINDOW_MS 配對、不用 _MAX) - RESULT_RATE_LIMIT_BURST_PER_10S=${RESULT_RATE_LIMIT_BURST_PER_10S:-5} - RESULT_RATE_LIMIT_BURST_WINDOW_MS=${RESULT_RATE_LIMIT_BURST_WINDOW_MS:-10000} - RESULT_RATE_LIMIT_SUSTAINED_PER_MIN=${RESULT_RATE_LIMIT_SUSTAINED_PER_MIN:-20} - RESULT_RATE_LIMIT_SUSTAINED_WINDOW_MS=${RESULT_RATE_LIMIT_SUSTAINED_WINDOW_MS:-60000} # === Phase 0.8b Phase B:/result bandwidth quota(保護 mass download)=== # 1 GiB = 1073741824 / 6 GiB = 6442450944 - RESULT_BANDWIDTH_QUOTA_PER_HOUR_BYTES=${RESULT_BANDWIDTH_QUOTA_PER_HOUR_BYTES:-1073741824} - RESULT_BANDWIDTH_QUOTA_PER_DAY_BYTES=${RESULT_BANDWIDTH_QUOTA_PER_DAY_BYTES:-6442450944} # === Phase 0.8b Phase B:/result concurrent stream cap + stream timeout === - MAX_CONCURRENT_RESULT_STREAMS=${MAX_CONCURRENT_RESULT_STREAMS:-10} - RESULT_STREAM_TIMEOUT_MS=${RESULT_STREAM_TIMEOUT_MS:-300000} restart: unless-stopped # ---------- Workers (stub mode) ---------- onnx-worker: build: context: . dockerfile: services/workers/Dockerfile.stub depends_on: redis: condition: service_healthy volumes: - job-data:/data/jobs environment: - STAGE=onnx - REDIS_URL=redis://redis:6379 - JOB_DATA_DIR=/data/jobs - WORKER_MODE=${WORKER_MODE:-stub} - STORAGE_BACKEND=${STORAGE_BACKEND:-local} - MINIO_ENDPOINT_URL=${MINIO_ENDPOINT_URL:-http://192.168.0.130:9000} - MINIO_BUCKET=${MINIO_BUCKET:-convertet-working-space} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-convuser} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_REGION=${MINIO_REGION:-us-east-1} - MINIO_LIFECYCLE_DAYS=${MINIO_LIFECYCLE_DAYS:-7} restart: unless-stopped bie-worker: build: context: . dockerfile: services/workers/Dockerfile.stub depends_on: redis: condition: service_healthy volumes: - job-data:/data/jobs environment: - STAGE=bie - REDIS_URL=redis://redis:6379 - JOB_DATA_DIR=/data/jobs - WORKER_MODE=${WORKER_MODE:-stub} - STORAGE_BACKEND=${STORAGE_BACKEND:-local} - MINIO_ENDPOINT_URL=${MINIO_ENDPOINT_URL:-http://192.168.0.130:9000} - MINIO_BUCKET=${MINIO_BUCKET:-convertet-working-space} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-convuser} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_REGION=${MINIO_REGION:-us-east-1} - MINIO_LIFECYCLE_DAYS=${MINIO_LIFECYCLE_DAYS:-7} restart: unless-stopped nef-worker: build: context: . dockerfile: services/workers/Dockerfile.stub depends_on: redis: condition: service_healthy volumes: - job-data:/data/jobs environment: - STAGE=nef - REDIS_URL=redis://redis:6379 - JOB_DATA_DIR=/data/jobs - WORKER_MODE=${WORKER_MODE:-stub} - STORAGE_BACKEND=${STORAGE_BACKEND:-local} - MINIO_ENDPOINT_URL=${MINIO_ENDPOINT_URL:-http://192.168.0.130:9000} - MINIO_BUCKET=${MINIO_BUCKET:-convertet-working-space} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-convuser} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_REGION=${MINIO_REGION:-us-east-1} - MINIO_LIFECYCLE_DAYS=${MINIO_LIFECYCLE_DAYS:-7} restart: unless-stopped