Files
operating-room-monitor-server/docs/离线镜像tarball部署.md

274 lines
11 KiB
Markdown
Raw Normal View History

2026-05-21 15:48:03 +08:00
# 离线镜像tarball部署说明
本文说明 **方式 B**:厂商在可联网环境构建并导出 API 镜像为 `tar`/`tar.gz`,客户在目标服务器 **不拉取 API 镜像构建上下文** 的情况下,通过 `docker load` 加载后启动完整后端PostgreSQL + MinIO + API
> 说明:仓库默认的 `docker-compose.yml` 中 `api` 使用 `build:`。客户侧使用 tarball 时,必须使用下方 **「仅使用已加载镜像」** 的 Compose 片段(将 `build` 换成 `image`),否则仍会尝试本地构建。
---
## 一、厂商侧:构建与导出
在已具备完整源码与 Docker 的环境中(与生产交付相同架构,一般为 **linux/amd64**
### 1. 构建并打标签
```bash
cd /path/to/operation-room-monitor-server
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 服务器,需启用跨平台构建(示例):
```bash
docker buildx build --platform linux/amd64 -t <交付镜像名>:<版本号> --load .
```
### 2. 导出为 tarball
单机文件交付常用压缩包:
```bash
docker save <交付镜像名>:<版本号> | gzip -9 > or-monitor-api_<版本号>_linux-amd64.tar.gz
```
导出多个镜像(可选,用于 **完全离线** 且客户机不能拉 Postgres/MinIO 时):
```bash
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:` |
| `.env.example`(按项目实际改名) | 环境变量模板,勿包含真实密钥 plaintext 于公共渠道 |
|(可选)语音确认前端静态包、`OR_SITE_CONFIG` 样例 | 按需 |
---
## 二、客户侧:环境要求
- **Docker** 与 **Docker Compose**Compose V2`docker compose`)。
- **CPU 架构**:与 tarball 构建时声明一致(常见 **amd64**)。
- **磁盘**PostgreSQL / MinIO 使用命名卷持久化;外加镜像解压空间,建议预留数十 GB。
- **端口**(默认值,可在 `.env` 中修改):
- `38080`HTTP API`API_PORT`
- `9000` / `9001`MinIO API / 控制台
- `5432`:仅在容器网络内使用,默认 **不映射到宿主机**
- **GPU必需**:镜像内含 CUDA 版 PyTorch客户机需安装 NVIDIA 驱动与 **NVIDIA Container Toolkit**,并保证 `nvidia-smi``docker run --gpus all` 正常。Compose 模板中 `api` 服务默认启用 `gpus: all`
2026-05-21 15:48:03 +08:00
---
## 三、客户侧:加载镜像
将 tarball 拷贝到服务器后:
```bash
gzip -dc or-monitor-api_<版本号>_linux-amd64.tar.gz | docker load
# 或docker load -i or-monitor-api_<版本号>_linux-amd64.tar
```
确认镜像已存在:
```bash
docker images | grep <交付镜像名>
```
若为「全栈离线包」,同样对整体 `tar.gz` 执行一次 `docker load` 即可导入多个镜像。
---
## 四、客户侧Compose 配置(使用已加载镜像)
在部署目录放置 `docker-compose.yml`**将 `api` 服务改为使用预加载镜像**,不要保留 `build:`。可直接使用下面模板(把镜像名与版本改成实际交付值):
```yaml
# 与仓库主 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
2026-05-21 15:48:03 +08:00
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 镜像时**:需客户机对上述 `db``minio``image:` 仍能拉取(或改用厂商提供的全栈 `docker save` 包,`image` 名需与导出时完全一致)。
---
## 五、环境变量与密钥
在同一目录放置 `.env`(可参考仓库内 `.env.example`)。至少建议关注:
- `POSTGRES_PASSWORD`:生产请务必改为强密码。
- `API_PORT`:宿主机监听端口。
- 语音链路:`BAIDU_*``MINIO_*`(容器内需访问 `MINIO_ENDPOINT`,默认已通过 `DOCKER_MINIO_ENDPOINT` 指向 `minio:9000`)。
- `OR_SITE_CONFIG_JSON_FILE`:容器内可读路径下的站点 JSON若在宿主机维护**`volumes`** 挂载进 `api` 容器(挂载方式由项目单独约定)。
- RTSP`host.docker.internal` 用于访问**宿主机**上的假流;详见 `docs/video-backends.md`
---
## 六、启动与健康检查
```bash
docker compose up -d
docker compose ps
curl -sf http://127.0.0.1:38080/health
```
局域网终端将「服务端 Base URL」配置为`http://<服务器局域网IP>:38080`(若修改了 `API_PORT` 则同步修改端口)。
查看日志:
```bash
docker compose logs -f api
```
---
## 七、升级 API 镜像
1. 客户停止 API或整栈`docker compose stop api`
2. 加载新 tarball`docker load -i …`
3.`docker-compose.yml``api.image` 更新为新 `<交付镜像名>:<新版本号>`(或保持不变若标签滚动更新,需先在客户机 `docker rmi` 旧镜像或使用明确版本标签)。
4. `docker compose up -d`。API 容器启动仍会执行 `alembic upgrade head`
---
## 八、常见问题
**Q`docker compose up` 仍尝试 build**
A`api` 服务仍存在 `build:` 配置时会构建。必须使用本文第四节模板,仅用 `image:`
**Q无 GPU 报错?**
A本仓库默认要求 GPU 推理。若必须在无 GPU 环境运行,需移除 `api``gpus: all` 并使用 CPU 兼容镜像(推理会显著变慢,需与厂商确认)。
2026-05-21 15:48:03 +08:00
**QPostgreSQL / MinIO 数据在哪?**
ADocker 命名卷(如 `pgdata``minio_data``docker compose down -v` 会删除数据,操作前确认备份。
**Q`host.docker.internal` 在 Linux 无效?**
ACompose 已为 `api` 配置 `extra_hosts: host.docker.internal:host-gateway`Docker 20.10+)。若自定义运行 `docker run`,需自行添加同等 `extra_hosts`
---
## 九、与安全相关
- tarball 等价于可复制软件制品,分发渠道注意保密与校验(校验和、签名)。
- 勿将生产密钥写入镜像层;一律通过 `.env` 或编排系统注入运行时环境变量。
如需语音确认前端、演示客户端与本后端的局域网访问说明,参见 `docs/Docker部署.md``clients/voice-confirmation/README.md``clients/demo-client/README.md`