Files
operating-room-monitor-server/docs/离线镜像tarball部署.md
Kevin 1af442481e 重组为 backend/clients/docs 三层结构,并清理 git 污染。
将后端迁入 backend/,完善根目录 .gitignore,删除误提交的 .mypy_cache 缓存文件。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 16:02:25 +08:00

11 KiB
Executable File
Raw Blame History

离线镜像tarball部署说明

本文说明 方式 B:厂商在可联网环境构建并导出 API 镜像为 tar/tar.gz,客户在目标服务器 不拉取 API 镜像构建上下文 的情况下,通过 docker load 加载后启动完整后端PostgreSQL + MinIO + API

说明:仓库默认的 backend/docker-compose.ymlapi 使用 build:。客户侧使用 tarball 时,必须使用下方 「仅使用已加载镜像」 的 Compose 片段(将 build 换成 image),否则仍会尝试本地构建。


一、厂商侧:构建与导出

在已具备完整源码与 Docker 的环境中(与生产交付相同架构,一般为 linux/amd64

1. 构建并打标签

cd /path/to/operation-room-monitor/backend
docker compose build api
docker tag operation-room-monitor-server-api:latest <交付镜像名>:<版本号>
# 示例docker tag operation-room-monitor-server-api:latest acme/or-monitor-api:1.0.0

若在 Apple Silicon 等设备上打包给 x86 服务器,需启用跨平台构建(示例):

docker buildx build --platform linux/amd64 -t <交付镜像名>:<版本号> --load .

2. 导出为 tarball

单机文件交付常用压缩包:

docker save <交付镜像名>:<版本号> | gzip -9 > or-monitor-api_<版本号>_linux-amd64.tar.gz

导出多个镜像(可选,用于 完全离线 且客户机不能拉 Postgres/MinIO 时):

docker pull m.daocloud.io/docker.io/library/postgres:16-alpine
docker pull m.daocloud.io/docker.io/minio/minio:latest
docker save \
  <交付镜像名>:<版本号> \
  m.daocloud.io/docker.io/library/postgres:16-alpine \
  m.daocloud.io/docker.io/minio/minio:latest \
  | gzip -9 > or-monitor-stack_<版本号>_offline.tar.gz

3. 建议一并交付的文件

文件 说明
or-monitor-api_*.tar.gz(或全栈 *_offline.tar.gz 镜像包
本文档 客户部署步骤
docker-compose.yml 下文「离线 Compose」全文 api 使用 image: 而非 build:(位于 backend/
backend/.env.example(按项目实际改名) 环境变量模板,勿包含真实密钥 plaintext 于公共渠道
(可选)语音确认前端静态包、OR_SITE_CONFIG 样例 按需

二、客户侧:环境要求

  • DockerDocker ComposeCompose V2docker compose)。
  • CPU 架构:与 tarball 构建时声明一致(常见 amd64)。
  • 磁盘PostgreSQL / MinIO 使用命名卷持久化;外加镜像解压空间,建议预留数十 GB。
  • 端口(默认值,可在 .env 中修改):
    • 38080HTTP APIAPI_PORT
    • 9000 / 9001MinIO API / 控制台
    • 5432:仅在容器网络内使用,默认 不映射到宿主机
  • GPU必需:镜像内含 CUDA 版 PyTorch客户机需安装 NVIDIA 驱动与 NVIDIA Container Toolkit,并保证 nvidia-smidocker run --gpus all 正常。Compose 模板中 api 服务默认启用 gpus: all

三、客户侧:加载镜像

将 tarball 拷贝到服务器后:

gzip -dc or-monitor-api_<版本号>_linux-amd64.tar.gz | docker load
# 或docker load -i or-monitor-api_<版本号>_linux-amd64.tar

确认镜像已存在:

docker images | grep <交付镜像名>

若为「全栈离线包」,同样对整体 tar.gz 执行一次 docker load 即可导入多个镜像。


四、客户侧Compose 配置(使用已加载镜像)

在部署目录放置 docker-compose.ymlapi 服务改为使用预加载镜像,不要保留 build:。可直接使用下面模板(把镜像名与版本改成实际交付值):

# 与仓库主 compose 行为一致,但 api 使用已 load 的镜像(无 build
services:
  db:
    image: m.daocloud.io/docker.io/library/postgres:16-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      POSTGRES_DB: ${POSTGRES_DB:-operation_room}
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 5s

  minio:
    image: m.daocloud.io/docker.io/minio/minio:latest
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
      MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
    ports:
      - "${MINIO_PORT:-9000}:9000"
      - "${MINIO_CONSOLE_PORT:-9001}:9001"
    volumes:
      - minio_data:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

  api:
    image: <交付镜像名>:<版本号>
    gpus: all
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      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_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_CAPTURE_OPTIONS: ${OPENCV_FFMPEG_CAPTURE_OPTIONS:-rtsp_transport;tcp}
      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: ${DOCKER_MINIO_ENDPOINT:-minio:9000}
      MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
      MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
      MINIO_BUCKET: ${MINIO_BUCKET:-operation-room-voice}
      MINIO_SECURE: ${MINIO_SECURE:-false}
      MINIO_REGION: ${MINIO_REGION:-}
      DEMO_CORS_ENABLED: ${DEMO_CORS_ENABLED:-true}
      DEMO_CORS_ORIGINS: ${DEMO_CORS_ORIGINS:-*}
      DEMO_ORCHESTRATOR_ENABLED: ${DEMO_ORCHESTRATOR_ENABLED:-false}
      DEMO_ORCHESTRATOR_RTSP_PORT: ${DEMO_ORCHESTRATOR_RTSP_PORT:-18554}
      DEMO_ORCHESTRATOR_RTSP_JSON_HOST: ${DOCKER_DEMO_ORCHESTRATOR_RTSP_JSON_HOST:-host.docker.internal}
      MEDIAMTX_DOCKER_IMAGE: ${MEDIAMTX_DOCKER_IMAGE:-m.daocloud.io/docker.io/bluenviron/mediamtx:latest}
    command: >
      sh -c "alembic upgrade head &&
      uvicorn main:app --host 0.0.0.0 --port 8000"
    ports:
      - "${API_PORT:-38080}:8000"
    depends_on:
      db:
        condition: service_healthy
      minio:
        condition: service_started
    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: 20s

volumes:
  pgdata:
  minio_data:

离线且未交付 Postgres/MinIO 镜像时:需客户机对上述 dbminioimage: 仍能拉取(或改用厂商提供的全栈 docker save 包,image 名需与导出时完全一致)。


五、环境变量与密钥

在同一目录(backend/)放置 .env(可参考 backend/.env.example)。至少建议关注:

  • POSTGRES_PASSWORD:生产请务必改为强密码。
  • API_PORT:宿主机监听端口。
  • 语音链路:BAIDU_*MINIO_*(容器内需访问 MINIO_ENDPOINT,默认已通过 DOCKER_MINIO_ENDPOINT 指向 minio:9000)。
  • OR_SITE_CONFIG_JSON_FILE:容器内可读路径下的站点 JSON若在宿主机维护volumes 挂载进 api 容器(挂载方式由项目单独约定)。
  • RTSPhost.docker.internal 用于访问宿主机上的假流;详见 docs/video-backends.md

六、启动与健康检查

docker compose up -d
docker compose ps
curl -sf http://127.0.0.1:38080/health

局域网终端将「服务端 Base URL」配置为http://<服务器局域网IP>:38080(若修改了 API_PORT 则同步修改端口)。

查看日志:

docker compose logs -f api

七、升级 API 镜像

  1. 客户停止 API或整栈docker compose stop api
  2. 加载新 tarballdocker load -i …
  3. docker-compose.ymlapi.image 更新为新 <交付镜像名>:<新版本号>(或保持不变若标签滚动更新,需先在客户机 docker rmi 旧镜像或使用明确版本标签)。
  4. docker compose up -d。API 容器启动仍会执行 alembic upgrade head

八、常见问题

Qdocker compose up 仍尝试 build
Aapi 服务仍存在 build: 配置时会构建。必须使用本文第四节模板,仅用 image:

Q无 GPU 报错?
A本仓库默认要求 GPU 推理。若必须在无 GPU 环境运行,需移除 apigpus: all 并使用 CPU 兼容镜像(推理会显著变慢,需与厂商确认)。

QPostgreSQL / MinIO 数据在哪?
ADocker 命名卷(如 pgdataminio_datadocker compose down -v 会删除数据,操作前确认备份。

Qhost.docker.internal 在 Linux 无效?
ACompose 已为 api 配置 extra_hosts: host.docker.internal:host-gatewayDocker 20.10+)。若自定义运行 docker run,需自行添加同等 extra_hosts


九、与安全相关

  • tarball 等价于可复制软件制品,分发渠道注意保密与校验(校验和、签名)。
  • 勿将生产密钥写入镜像层;一律通过 .env 或编排系统注入运行时环境变量。

如需语音确认前端、演示客户端与本后端的局域网访问说明,参见 docs/Docker部署.mdclients/voice-confirmation/README.mdclients/demo-client/README.md