# Production stack for the current codebase: FastAPI + PostgreSQL. # The API hard-fails on startup if the database is not reachable, so DB health is required. # # API 的 Dockerfile 必须使用带 DaoCloud 前缀的 Python 基础镜像(m.daocloud.io/docker.io/library/python:...), # 不能使用「FROM python:...」,否则会直连 registry-1.docker.io 导致构建失败。 # # Base images: DaoCloud「增加前缀」镜像(README 推荐,减少 BuildKit 直连 registry-1.docker.io) # See https://github.com/DaoCloud/public-image-mirror # # The ``api`` service is built from ``Dockerfile``: Debian apt → 阿里云, uv/PyPI → 清华, # PyTorch CPU wheels / pytorch.org index files → 南大镜像; see Dockerfile for details. # # Published API port defaults to 38080 on the host (override with API_PORT). # # GPU (NVIDIA) inference: uv.lock pins torch/torchvision from the PyTorch *CPU* index (see pyproject.toml). # For CUDA on Linux, use a separate image/lockfile that installs torch+cu*, NVIDIA Container Toolkit on the host, # and assign GPUs to the api service (deploy.resources.reservations.devices). services: db: image: m.daocloud.io/docker.io/library/postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB:-operation_room} volumes: - pgdata_prod:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 5s timeout: 5s retries: 10 start_period: 10s api: build: context: . dockerfile: Dockerfile # 容器内访问「宿主机上监听的 RTSP」(假流 / MediaMTX 等)需要解析 host.docker.internal。 # Linux 默认无该主机名;host-gateway 解析为宿主机在容器网桥上的地址(Docker 20.10+)。 extra_hosts: - "host.docker.internal:host-gateway" environment: POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB:-operation_room} POSTGRES_HOST: db POSTGRES_PORT: 5432 CONSUMABLE_CLASSIFIER_IMGSZ: ${CONSUMABLE_CLASSIFIER_IMGSZ:-224} CONSUMABLE_CLASSIFIER_DEVICE: ${CONSUMABLE_CLASSIFIER_DEVICE:-} CONSUMABLE_CLASSIFIER_TOPK: ${CONSUMABLE_CLASSIFIER_TOPK:-5} CONSUMABLE_MIN_CLS_CONFIDENCE: ${CONSUMABLE_MIN_CLS_CONFIDENCE:-0.5} CONSUMABLE_VISION_WINDOW_SEC: ${CONSUMABLE_VISION_WINDOW_SEC:-15} HAND_DETECTION_WEIGHTS: ${HAND_DETECTION_WEIGHTS:-} HAND_DETECTION_IMGSZ: ${HAND_DETECTION_IMGSZ:-640} HAND_DETECTION_DEVICE: ${HAND_DETECTION_DEVICE:-} # Video backends (RTSP / optional Hikvision SDK) — see docs/video-backends.md VIDEO_DEFAULT_BACKEND: ${VIDEO_DEFAULT_BACKEND:-rtsp} VIDEO_RTSP_URL_TEMPLATE: ${VIDEO_RTSP_URL_TEMPLATE:-} OR_SITE_CONFIG_JSON_FILE: ${OR_SITE_CONFIG_JSON_FILE:-} VIDEO_CAMERA_BACKEND_OVERRIDES_JSON: ${VIDEO_CAMERA_BACKEND_OVERRIDES_JSON:-} HIKVISION_SDK_ENABLED: ${HIKVISION_SDK_ENABLED:-false} HIKVISION_LIB_DIR: ${HIKVISION_LIB_DIR:-/opt/hikvision/lib} HIKVISION_DEVICE_IP: ${HIKVISION_DEVICE_IP:-} HIKVISION_USER: ${HIKVISION_USER:-} HIKVISION_PASSWORD: ${HIKVISION_PASSWORD:-} HIKVISION_PREVIEW_RTSP_TEMPLATE: ${HIKVISION_PREVIEW_RTSP_TEMPLATE:-} # OpenCV/FFmpeg 拉 RTSP:TCP 在容器 NAT/防火墙场景下比 UDP 更可靠;可用 .env 覆盖为空禁用 OPENCV_FFMPEG_CAPTURE_OPTIONS: ${OPENCV_FFMPEG_CAPTURE_OPTIONS:-rtsp_transport;tcp} # 语音待确认:TTS/ASR 需百度;resolve 上传 WAV 还需 MinIO(见 .env.example) BAIDU_APP_ID: ${BAIDU_APP_ID:-} BAIDU_API_KEY: ${BAIDU_API_KEY:-} BAIDU_SECRET_KEY: ${BAIDU_SECRET_KEY:-} BAIDU_ASR_DEV_PID: ${BAIDU_ASR_DEV_PID:-1537} MINIO_ENDPOINT: ${MINIO_ENDPOINT:-} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-} MINIO_BUCKET: ${MINIO_BUCKET:-operation-room-voice} MINIO_SECURE: ${MINIO_SECURE:-false} MINIO_REGION: ${MINIO_REGION:-} DEMO_CORS_ENABLED: ${DEMO_CORS_ENABLED:-false} DEMO_CORS_ORIGINS: ${DEMO_CORS_ORIGINS:-*} ports: - "${API_PORT:-38080}:8000" depends_on: db: condition: service_healthy restart: unless-stopped healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2)", ] interval: 10s timeout: 5s retries: 5 start_period: 10s volumes: pgdata_prod: