85 lines
3.5 KiB
YAML
85 lines
3.5 KiB
YAML
# 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.
|
||
#
|
||
# Base images use DaoCloud public mirror (大陆可访问): docker.io → docker.m.daocloud.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: docker.m.daocloud.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
|
||
args:
|
||
# 大陆默认经 DaoCloud;若拉取失败可 export PYTHON_BASE_IMAGE=docker.io/library/python:3.13-slim-bookworm 后 build
|
||
PYTHON_BASE_IMAGE: ${PYTHON_BASE_IMAGE:-m.daocloud.io/docker.io/library/python:3.13-slim-bookworm}
|
||
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:-}
|
||
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:
|