2026-05-18-22-23-07 增加Docker安装包与发布配置

This commit is contained in:
2026-05-18 23:17:52 +08:00
parent c818f0a663
commit aec02ba748
15 changed files with 572 additions and 0 deletions

15
.dockerignore Normal file
View File

@@ -0,0 +1,15 @@
.git
.pytest_cache
__pycache__
*.pyc
*.pyo
*.pyd
.DS_Store
dist
release_packages
packaging_build
storage/jobs
storage/uploads
storage/**/*.raw.mp4
*.tar.gz
*.zip

4
.gitignore vendored
View File

@@ -5,7 +5,11 @@ __pycache__/
.ruff_cache/
.venv/
venv/
data/
storage/jobs/
storage/uploads/
storage/demos/
packaging_build/
release_packages/
*.log

33
Dockerfile Normal file
View File

@@ -0,0 +1,33 @@
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_COMPILE=1 \
PORT=8001
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ffmpeg \
libgl1 \
libglib2.0-0 \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
COPY requirements-docker.txt ./
RUN pip install --no-cache-dir --no-compile -r requirements-docker.txt
COPY backend ./backend
COPY frontend ./frontend
COPY scripts ./scripts
COPY storage/samples ./storage/samples
RUN mkdir -p /app/storage/uploads /app/storage/jobs
EXPOSE 8001
HEALTHCHECK --interval=10s --timeout=5s --retries=6 --start-period=20s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8001/api/health', timeout=3).read()"
CMD ["sh", "-c", "python -m uvicorn backend.main:app --host 0.0.0.0 --port ${PORT:-8001}"]

View File

@@ -0,0 +1,38 @@
# ISISeg 纯净安装包
本包用于新环境部署。网络相关信息都在 `docker_compose.yaml` 中以 `TODO` 注释和环境变量形式保留,请先按目标环境填写。
## 启动应用
```bash
docker compose -f docker_compose.yaml up -d --build
```
默认本机端口为 `10004`,可通过环境变量调整:
```bash
ISISEG_HOST_PORT=8001 docker compose -f docker_compose.yaml up -d --build
```
## 启用 frpc
先填写 `docker_compose.yaml` 中:
- `FRP_SERVER_ADDR`
- `FRP_SERVER_PORT`
- `FRP_AUTH_TOKEN`
- `FRP_PROXY_NAME`
- `FRP_REMOTE_PORT`
然后运行:
```bash
docker compose -f docker_compose.yaml --profile frpc up -d --build
```
## 数据目录
- `data/uploads`
- `data/jobs`
内置样例视频已包含在镜像构建上下文中。

48
deploy/README_DOCKER.md Normal file
View File

@@ -0,0 +1,48 @@
# ISISeg Docker 部署说明
## 纯净模板
使用仓库根目录 `docker_compose.yaml`
```bash
docker compose -f docker_compose.yaml up -d --build
```
如需启用 frpc先填写 compose 中 `FRP_SERVER_ADDR``FRP_AUTH_TOKEN``FRP_REMOTE_PORT` 等待填写项,然后运行:
```bash
docker compose -f docker_compose.yaml --profile frpc up -d --build
```
## huijutec 直接运行版
使用 `docker_compose_huijutec.yaml`
```bash
docker compose -f docker_compose_huijutec.yaml up -d --build
```
默认本机端口和 frpc 远端端口均为 `10004`,公网域名为 `https://isiseg.huijutec.cn`
## QNAP NAS 版
将安装包解压到:
```text
/share/Container/ISISeg
```
在 QTS Container Station 中选择 `docker_compose_Nas.yaml` 运行。该文件已使用绝对路径、`192.168.31.7:7893` 代理和 frpc `10004` 远端端口。
## 数据目录
运行数据默认挂载到:
- `data/uploads`
- `data/jobs`
内置样例视频保留在镜像内的 `storage/samples`
## 依赖说明
Docker 镜像使用 `requirements-docker.txt`,其中 OpenCV 使用 headless 版本,避免在 NAS 或精简 Linux 镜像中额外安装 GUI 依赖。源码开发环境仍可使用 `requirements.txt`

View File

@@ -0,0 +1,27 @@
# ISISeg huijutec 直接运行安装包
本包已预置:
- 公网访问域名:`https://isiseg.huijutec.cn`
- 本机端口:`10004`
- frpc 远端端口:`10004`
- frpc 服务端:`82.157.255.195:7000`
## 启动
```bash
docker compose -f docker_compose.yaml up -d --build
```
启动后frpc 会将容器内 `isiseg_app:8001` 映射到公网 frp 服务器的 `10004` 端口。按你给的 Nginx Proxy Manager 配置,`isiseg.huijutec.cn` 转发到 `172.17.0.1:10004` 后即可访问。
## 本机检查
```bash
curl http://127.0.0.1:10004/api/health
```
## 数据目录
- `data/uploads`
- `data/jobs`

View File

@@ -0,0 +1,36 @@
# ISISeg QNAP NAS 安装包
本包适用于 QNAP QTS / Container Station已预置
- 项目路径:`/share/Container/ISISeg`
- 公网访问域名:`https://isiseg.huijutec.cn`
- 本机端口:`10004`
- frpc 远端端口:`10004`
- 构建代理:`http://192.168.31.7:7893`
## 安装步骤
1. 将安装包解压到:
```text
/share/Container/ISISeg
```
2. 在 Container Station 中使用 `docker_compose_Nas.yaml` 创建应用。
3. 启动后访问:
```text
https://isiseg.huijutec.cn
```
## 数据目录
- `/share/Container/ISISeg/data/uploads`
- `/share/Container/ISISeg/data/jobs`
## 本地检查
```bash
curl http://127.0.0.1:10004/api/health
```

76
docker_compose.yaml Normal file
View File

@@ -0,0 +1,76 @@
# ISISeg clean deployment template.
# Copy this file as docker_compose.yaml, then fill the TODO values below.
# Start app only:
# docker compose -f docker_compose.yaml up -d --build
# Start app + frpc:
# docker compose -f docker_compose.yaml --profile frpc up -d --build
name: isiseg
services:
isiseg_app:
image: isiseg/app:latest
build:
context: .
dockerfile: Dockerfile
# TODO: Uncomment args only if this host needs a proxy during docker build.
# args:
# HTTP_PROXY: http://<proxy-host>:<proxy-port>
# HTTPS_PROXY: http://<proxy-host>:<proxy-port>
# NO_PROXY: localhost,127.0.0.1,<your-lan-cidr>,isiseg_app
restart: unless-stopped
environment:
TZ: Asia/Shanghai
PORT: 8001
# TODO: Fill with your public URL, for example https://isiseg.example.com
PUBLIC_URL: ""
ports:
# TODO: Change host port if 10004 is occupied.
- "${ISISEG_HOST_PORT:-10004}:8001"
volumes:
- ./data/uploads:/app/storage/uploads
- ./data/jobs:/app/storage/jobs
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8001/api/health', timeout=3).read()\""]
interval: 10s
timeout: 5s
retries: 12
start_period: 20s
isiseg_frpc:
# TODO: Remove profiles if you want frpc to start by default.
profiles: ["frpc"]
image: snowdreamtech/frpc:latest
restart: unless-stopped
environment:
# TODO: Fill these values before enabling frpc.
FRP_SERVER_ADDR: "<FRP_SERVER_ADDR>"
FRP_SERVER_PORT: "7000"
FRP_AUTH_TOKEN: "<FRP_AUTH_TOKEN>"
FRP_PROXY_NAME: "ISISeg"
FRP_REMOTE_PORT: "10004"
entrypoint: ["/bin/sh"]
command:
- -c
- |
cat > /tmp/frpc.toml <<EOF
serverAddr = "$${FRP_SERVER_ADDR}"
serverPort = $${FRP_SERVER_PORT}
auth.method = "token"
auth.token = "$${FRP_AUTH_TOKEN}"
transport.poolCount = 5
transport.heartbeatTimeout = -1
[[proxies]]
name = "$${FRP_PROXY_NAME}"
type = "tcp"
localIP = "isiseg_app"
localPort = 8001
remotePort = $${FRP_REMOTE_PORT}
EOF
exec frpc -c /tmp/frpc.toml
depends_on:
isiseg_app:
condition: service_healthy

68
docker_compose_Nas.yaml Normal file
View File

@@ -0,0 +1,68 @@
# ISISeg huijutec.cn / QNAP QTS Container Station deployment.
# This file is prefilled for /share/Container/ISISeg, https://isiseg.huijutec.cn,
# 192.168.31.7:7893 proxy, and frpc remotePort 10004.
# Copy the package to /share/Container/ISISeg, then run this file in Container Station.
name: isiseg_qnap
services:
isiseg_app:
image: isiseg/app:qnap-20260518
build:
context: /share/Container/ISISeg
dockerfile: Dockerfile
args:
HTTP_PROXY: http://192.168.31.7:7893
HTTPS_PROXY: http://192.168.31.7:7893
NO_PROXY: localhost,127.0.0.1,192.168.31.0/24,isiseg_app
restart: unless-stopped
environment:
TZ: Asia/Shanghai
PORT: 8001
PUBLIC_URL: https://isiseg.huijutec.cn
HTTP_PROXY: http://192.168.31.7:7893
HTTPS_PROXY: http://192.168.31.7:7893
http_proxy: http://192.168.31.7:7893
https_proxy: http://192.168.31.7:7893
NO_PROXY: localhost,127.0.0.1,192.168.31.0/24,isiseg_app
ports:
- "10004:8001"
volumes:
- /share/Container/ISISeg/data/uploads:/app/storage/uploads
- /share/Container/ISISeg/data/jobs:/app/storage/jobs
healthcheck:
# Use 127.0.0.1 to avoid localhost IPv6 preference on some NAS firmwares.
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8001/api/health', timeout=3).read()\""]
interval: 10s
timeout: 5s
retries: 12
start_period: 20s
isiseg_frpc:
image: snowdreamtech/frpc:latest
restart: unless-stopped
entrypoint: ["/bin/sh"]
command:
- -c
- |
cat > /tmp/frpc.toml <<'EOF'
serverAddr = "82.157.255.195"
serverPort = 7000
auth.method = "token"
auth.token = "en.xjtu.edu.cn"
transport.poolCount = 5
transport.heartbeatTimeout = -1
[[proxies]]
name = "ISISeg_QNAP"
type = "tcp"
localIP = "isiseg_app"
localPort = 8001
remotePort = 10004
EOF
exec frpc -c /tmp/frpc.toml
depends_on:
isiseg_app:
condition: service_healthy

View File

@@ -0,0 +1,58 @@
# ISISeg huijutec.cn direct deployment.
# This file is prefilled for https://isiseg.huijutec.cn and frpc remotePort 10004.
# Run:
# docker compose -f docker_compose_huijutec.yaml up -d --build
name: isiseg_huijutec
services:
isiseg_app:
image: isiseg/app:huijutec-20260518
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
TZ: Asia/Shanghai
PORT: 8001
PUBLIC_URL: https://isiseg.huijutec.cn
ports:
- "10004:8001"
volumes:
- ./data/uploads:/app/storage/uploads
- ./data/jobs:/app/storage/jobs
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8001/api/health', timeout=3).read()\""]
interval: 10s
timeout: 5s
retries: 12
start_period: 20s
isiseg_frpc:
image: snowdreamtech/frpc:latest
restart: unless-stopped
entrypoint: ["/bin/sh"]
command:
- -c
- |
cat > /tmp/frpc.toml <<'EOF'
serverAddr = "82.157.255.195"
serverPort = 7000
auth.method = "token"
auth.token = "en.xjtu.edu.cn"
transport.poolCount = 5
transport.heartbeatTimeout = -1
[[proxies]]
name = "ISISeg"
type = "tcp"
localIP = "isiseg_app"
localPort = 8001
remotePort = 10004
EOF
exec frpc -c /tmp/frpc.toml
depends_on:
isiseg_app:
condition: service_healthy

7
requirements-docker.txt Normal file
View File

@@ -0,0 +1,7 @@
fastapi==0.116.1
uvicorn[standard]==0.35.0
python-multipart==0.0.20
opencv-python-headless==4.13.0.92
numpy==2.4.4
scikit-image==0.26.0
pillow==12.2.0

View File

@@ -0,0 +1,55 @@
# 实现方案
开始时间2026-05-18-22-23-07
## 容器化
1. 新增 `Dockerfile`
- 基于 `python:3.12-slim`
- 安装 `ffmpeg``libgl1``libglib2.0-0``libgomp1`
- 安装 Docker 专用 `requirements-docker.txt`OpenCV 使用 headless 版本。
- 复制 `backend``frontend``storage/samples``scripts`
- 默认监听 `8001`
2. 新增 `requirements-docker.txt`
- 保留 FastAPI、Uvicorn、NumPy、scikit-image、Pillow 等运行依赖。
- 使用 `opencv-python-headless==4.13.0.92`,避免容器中额外 GUI 依赖和构建不稳定。
3. 新增 `.dockerignore` 和扩展 `.gitignore`
- 排除 `.git`、缓存、测试产物、`storage/jobs``storage/uploads`、打包目录。
## Compose 文件
1. `docker_compose.yaml`
- 纯净模板。
- app 服务可直接构建启动。
- frpc 服务放在 `frpc` profile 中。
- 域名、FRP 地址、token、远端端口等以注释和环境变量形式标记为待填写。
2. `docker_compose_huijutec.yaml`
- 直接运行版。
- app 端口映射 `10004:8001`
- frpc 连接用户参考配置中的服务器和 token`remotePort = 10004`
- 预置 `PUBLIC_URL=https://isiseg.huijutec.cn`
3. `docker_compose_Nas.yaml`
- QNAP 版。
- build context 使用 `/share/Container/ISISeg`
- 数据目录使用 `/share/Container/ISISeg/data/...`
- 代理使用 `192.168.31.7:7893`
- frpc 远端端口 `10004`
## 安装包
- `isiseg-clean-install-2026-05-18-22-23-07.tar.gz`
- `isiseg-huijutec-direct-2026-05-18-22-23-07.tar.gz`
- `isiseg-qnap-nas-2026-05-18-22-23-07.tar.gz`
每个包包含运行所需源码、Dockerfile、compose 文件和安装说明。
## Gitea 发布
- 提交代码到 main。
- 创建 tag`v2026-05-18-22-23-07-docker-packages`
- 调用 Gitea API 创建 release。
- 上传三个 tar.gz 作为 release 附件。

View File

@@ -0,0 +1,50 @@
# 测试方案
开始时间2026-05-18-22-23-07
## 自动化测试
- `python3 -m compileall backend tests`
- `node --check frontend/app.js`
- `pytest -q`
执行结果:全部通过,`pytest -q``5 passed`
## Docker/Compose 校验
- `docker compose -f docker_compose.yaml config`
- `docker compose -f docker_compose.yaml --profile frpc config`
- `docker compose -f docker_compose_huijutec.yaml config`
- `docker compose -f docker_compose_Nas.yaml config`
- `docker build -t isiseg:docker-package-test .`
- 运行容器并请求 `/api/health`
执行结果:
- 三个 compose 文件及纯净模板 frpc profile 均可解析。
- Docker 镜像构建通过。
- 测试容器 `/api/health` 返回 `{"status":"ok","service":"ISISeg","version":"0.1.0"}`
- 首次使用完整 `requirements.txt` 构建时,`opencv-python` 安装阶段触发段错误;已改为 Docker 专用 `requirements-docker.txt`,使用 `opencv-python-headless` 并保留运行时所需依赖。
## 安装包校验
- 检查三个 tar.gz 都存在。
- 分别列出包内文件,确认包含 compose、Dockerfile、backend、frontend、样例视频和 README。
- 解包后分别执行 compose config 校验。
- 确认安装包内不包含 `__pycache__``.pyc`
执行结果:
| 安装包 | SHA256 |
| --- | --- |
| `isiseg-clean-install-2026-05-18-22-23-07.tar.gz` | `dc62b411877271375b91833a48c933ff1201f87f9710626f4ba637c04eb039d1` |
| `isiseg-huijutec-direct-2026-05-18-22-23-07.tar.gz` | `84df7476152dd98a44b5b6479bd713f639bd12485ee4385559c5571dfba8850e` |
| `isiseg-qnap-nas-2026-05-18-22-23-07.tar.gz` | `f31bb0bc290b0d3e8f68721c16c638964dc80f6ac1a0b4d11063e4dcf80a71dd` |
## Gitea 发布校验
- push main。
- push tag。
- 创建 release。
- 上传三个附件。
- 查询 release 附件列表确认三个包均存在。

View File

@@ -175,3 +175,35 @@ B. 产生问题原因:上一版使用“最近一次分割掩膜持续叠加
C. 解决问题方案:后端生成右侧完整结果视频时改为每一帧都运行当前方法分割;`max_frames` 只控制下方结果卡片保存数量,不再控制主结果视频是否分割。关键帧卡片与视频对应帧用像素差测试校验一致性。
D. 后续如何避免问题:用于播放的结果视频必须逐帧使用当前帧结果;抽帧限制只能用于结果列表、调参预览或摘要,不应用来偷换主视频的逐帧分割语义。
## 2026-05-18-22-23-07 Docker 安装包与发布
### 1. Docker 构建阶段 OpenCV 安装段错误
A. 具体问题:用完整 `requirements.txt` 构建 Docker 镜像时,`pip install` 在安装 `opencv-python` 相关依赖后发生段错误,镜像无法稳定构建。
B. 产生问题原因:服务端容器只需要无界面 OpenCV 运行时,完整 `opencv-python` 轮子会携带 GUI 相关依赖,构建阶段资源和二进制兼容风险更高。
C. 解决问题方案:新增 `requirements-docker.txt`,将容器依赖收敛为运行时所需库,并使用 `opencv-python-headless==4.13.0.92`Dockerfile 使用 `--no-cache-dir --no-compile` 安装,最终构建通过。
D. 后续如何避免问题:服务端 Docker 镜像优先使用 headless 图像处理依赖,并将开发依赖与容器运行依赖拆分,避免把本地开发包完整带入生产镜像。
### 2. Compose 模板注释块导致配置解析失败
A. 具体问题:纯净模板最初只注释了 build args 的值,保留了空的 `args:``docker compose config` 解析失败。
B. 产生问题原因YAML 中空配置节点不是注释说明Compose 会按实际配置校验类型。
C. 解决问题方案:将整个可选 `args:` 块都注释掉,只保留待填写说明;需要代理时再整体取消注释。
D. 后续如何避免问题:带 TODO 的 Compose 模板必须用 `docker compose config` 同时校验默认路径和可选 profile确保注释示例不会变成无效配置。
### 3. 安装包夹带 Python 缓存
A. 具体问题:首次打包后安装包内包含 `__pycache__``.pyc`,不符合纯净安装包预期。
B. 产生问题原因:自动化测试和 Docker 构建前已运行 Python 代码,工作目录中生成了缓存文件,打包清单没有排除。
C. 解决问题方案:重新打包时排除 `__pycache__``.pyc`、临时目录和发布目录,并新增 `.gitignore` 避免后续误提交。
D. 后续如何避免问题:发布包生成后必须执行包内文件检查,至少确认无缓存、无临时目录、无本地数据和无敏感配置外泄。

View File

@@ -0,0 +1,25 @@
# 需求分析
开始时间2026-05-18-22-23-07
## 用户需求
1. 系统可以通过 `docker_compose.yaml` 安装。
2. 打包一个纯净安装包:
- 网络相关信息保留注释,作为待填写项。
- 适合迁移到任意环境后再配置域名、端口、FRP。
3. 打包一个可直接运行安装包:
- 预置 `isiseg.huijutec.cn`
- 参考用户提供的 frpc 配置。
- frpc 远端映射端口使用 `10004`
4. 打包一个威联通 NAS 版安装包:
- 提供 `docker_compose_Nas.yaml`
- 参考 QNAP QTS / Container Station 的绝对路径和代理写法。
- 端口和 frpc 映射与直接运行版保持一致。
5. 三个安装包都需要在 Gitea 发布。
## 约束
- 当前项目是 FastAPI 后端直接挂载静态前端,不需要单独 nginx。
- OpenCV 和视频转码依赖 `ffmpeg`、系统图形/运行库,需要 Docker 镜像补齐。
- 应保留内置样例视频,但不要把运行产生的 jobs/uploads 打进安装包。