# JHAI **Repository Path**: hainingzhang/jhai ## Basic Information - **Project Name**: JHAI - **Description**: docker 服务化部署 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-10-31 - **Last Updated**: 2026-02-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JHAI - Docker 服务自动部署系统 #### 介绍 这是一个基于 Flask 的 RESTful API 服务,用于自动部署和管理基于 Docker 的机器学习服务。系统支持通过 HTTP 接口自动下载模型和代码文件,使用 Docker Compose 启动服务容器,并提供了完整的服务生命周期管理功能。 #### 主要功能 - **自动服务部署**:通过 HTTP 接口自动下载模型和代码文件,使用 Docker 容器化部署 - **服务生命周期管理**:支持启动、停止、删除服务 - **Docker 自动构建**:自动检测并构建 Docker 镜像(如果不存在) - **端口管理**:自动检查端口占用情况,避免端口冲突 - **权限自动处理**:自动检测是否需要 sudo 权限执行 Docker 命令 #### API 接口 ##### 1. 启动服务接口 `/start/` **请求方式:** `POST` **请求参数:** ```json { "serviceId": "100000001", // 服务ID(必需) "serviceTag": "start", // 服务标签,固定为 "start"(必需) "taskType": "classification", // 任务类型(可选): // - classification(视觉分类) // - segmentation(视觉分割) // - detection(目标检测) // - instance_segmentation(实例分割) // - regression(数值回归) // 默认为 classification "modelUrl": "http://localhost:8888/weights.zip", // 模型文件下载地址(必需) "pyUrl": "http://localhost:8888/devdeploy.zip", // 代码文件下载地址(必需) "port": 8775, // 服务端口号(必需) "cuda": "cuda:0" // CUDA设备(可选): // - "cuda:0" 表示使用0号GPU // - "cuda:1" 表示使用1号GPU // - 不提供此参数则使用CPU模式 // 仅对视觉任务(classification/segmentation/detection/instance_segmentation)有效 } ``` **功能说明:** 1. 检查端口是否被占用 2. 根据 `taskType` 从指定 URL 下载模型文件和代码文件: - **视觉任务**(classification/segmentation/detection/instance_segmentation): - 代码文件下载到:`server/{serviceId}-{port}/devdeploy/` - 模型文件下载到:`server/{serviceId}-{port}/weights/` - **数值任务**(regression): - 代码文件下载到:`server/{serviceId}-{port}/deployShuzhi/` - 模型文件下载到:`server/{serviceId}-{port}/save_model/` 3. 自动解压文件到对应目录 4. 查找模型文件: - 视觉任务:查找 `.onnx` 格式的模型文件 - 数值任务:查找 `.joblib` 格式的模型文件 5. 根据 `taskType` 参数生成 docker-compose 配置文件: - `taskType="classification"`:设置 `MODEL_PATH` 环境变量,启动分类服务 - `taskType="segmentation"`:设置 `SEGMENT_MODEL_PATH` 环境变量,启动分割服务 - `taskType="detection"`:设置 `DETECT_MODEL_PATH` 环境变量,启动检测服务 - `taskType="instance_segmentation"`:设置 `INSTANCE_MODEL_PATH` 环境变量,启动实例分割服务 - `taskType="regression"`:设置 `MODEL_PATH` 环境变量,启动回归服务 6. 检查并构建 Docker 镜像(如果不存在) 7. 使用 docker-compose 启动服务容器 **响应示例:** ```json { "status": 200, "message": "请求已收到!", "data": {} } ``` **注意:** 服务启动是异步执行的,接口会立即返回,实际启动过程在后台进行。 ##### 2. 停止服务接口 `/stop/` **请求方式:** `POST` **请求参数:** ```json { "serviceId": "100000001", // 服务ID(必需) "serviceTag": "stop", // 服务标签,固定为 "stop"(必需) "taskType": "segmentation", // 任务类型(必需) "port": 8775 // 服务端口号(必需) } ``` **功能说明:** 1. 查找对应的 docker-compose 配置文件(根据任务类型和端口号) 2. 执行 `docker-compose down --remove-orphans` 停止并删除容器,同时清理孤儿容器 3. **多服务共享网络处理**:当多个服务共享同一个Docker网络时,停止服务时网络删除可能会失败(因为其他服务仍在使用该网络),但只要容器已成功停止和删除,操作仍会被视为成功 **响应示例:** ```json { "status": 200, "message": "成功", "data": { "service_id": "100000001", "flag": true } } ``` **注意事项:** - 当开启多个服务时,它们可能共享同一个Docker网络 - 停止单个服务时,如果网络删除失败(报错 "network has active endpoints"),这是正常现象,只要容器已停止和删除,操作就视为成功 - 系统会自动清理孤儿容器(orphan containers),这些是之前启动但compose文件中没有定义的服务 ##### 3. 删除服务接口 `/delete/` **请求方式:** `POST` **请求参数:** ```json { "serviceId": "100000001", // 服务ID(必需) "serviceTag": "delete", // 服务标签,固定为 "delete"(必需) "port": 8775 // 服务端口号(必需) } ``` **功能说明:** 1. 先停止并删除 Docker 容器 2. 删除整个服务目录 `server/{serviceId}-{port}/` **响应示例:** ```json { "status": 200, "message": "请求已收到!", "data": {} } ``` #### 服务配置 **默认服务端口:** `8051` **启动服务:** ```bash python main.py --port 8051 ``` **服务地址:** - 启动接口:`http://localhost:8051/start/` - 停止接口:`http://localhost:8051/stop/` - 删除接口:`http://localhost:8051/delete/` #### 目录结构 ``` jhai/ ├── main.py # 主服务文件 ├── server/ # 服务目录(自动创建) │ └── {serviceId}-{port}/ # 每个服务的独立目录 │ ├── devdeploy/ # 视觉任务:解压后的代码文件 │ │ ├── api/ # API 代码 │ │ ├── Dockerfile.cpu │ │ └── docker-compose.*.template.yml │ ├── deployShuzhi/ # 数值任务:解压后的代码文件 │ │ ├── api/ # API 代码 │ │ ├── Dockerfile.cpu │ │ └── docker-compose.*.template.yml │ ├── weights/ # 视觉任务:解压后的模型文件 │ │ └── *.onnx # ONNX格式模型文件 │ └── save_model/ # 数值任务:解压后的模型文件 │ └── *.joblib # Joblib格式模型文件 ├── utils/ │ ├── pyget.py # 代码文件下载器 │ └── modelget.py # 模型文件下载器 └── script/ # 测试脚本 ├── cls_start.py # 分类服务启动脚本 ├── cls_stop.py # 分类服务停止脚本 ├── cls_dele.py # 分类服务删除脚本 ├── seg_start.py # 分割服务启动脚本 ├── seg_stop.py # 分割服务停止脚本 ├── seg_dele.py # 分割服务删除脚本 ├── det_start.py # 检测服务启动脚本 ├── det_stop.py # 检测服务停止脚本 ├── det_dele.py # 检测服务删除脚本 ├── instance_start.py # 实例分割服务启动脚本 ├── instance_stop.py # 实例分割服务停止脚本 ├── instance_dele.py # 实例分割服务删除脚本 ├── reg_start.py # 回归服务启动脚本 └── clone_and_zip.sh # Git仓库克隆和压缩脚本 ``` #### Docker 配置 **镜像名称:** `devdeploy-classifier-api-cpu:latest` **容器命名规则:** `devdeploy-classifier-cpu-{port}` **Docker Compose 文件:** `docker-compose.cpu.{port}.yml` **SSH 端口计算:** `4022 + port - 8796` #### 任务类型说明 系统通过配置字典统一管理所有任务类型,分为两类: ##### 视觉任务(CV任务) 通过 `CV_TASK_CONFIG` 配置字典管理,当前支持: - **classification(分类服务)**: - `taskType="classification"` 或省略此参数 - 系统会设置 `MODEL_PATH` 环境变量 - 启动后可通过 `/api/v1/classify/single` 接口进行图像分类 - 代码文件下载到:`devdeploy` 目录 - 模型文件下载到:`weights` 目录 - 模型文件格式:`.onnx` 文件 - **segmentation(分割服务)**: - `taskType="segmentation"` - 系统会设置 `SEGMENT_MODEL_PATH` 环境变量 - 启动后可通过 `/api/v1/segment/single` 接口进行图像分割 - 代码文件下载到:`devdeploy` 目录 - 模型文件下载到:`weights` 目录 - 模型文件格式:`.onnx` 文件 - **detection(检测服务)**: - `taskType="detection"` - 系统会设置 `DETECT_MODEL_PATH` 环境变量 - 启动后可通过 `/api/v1/detect/single` 接口进行目标检测 - 代码文件下载到:`devdeploy` 目录 - 模型文件下载到:`weights` 目录 - 模型文件格式:`.onnx` 文件 - **instance_segmentation(实例分割服务)**: - `taskType="instance_segmentation"` - 系统会设置 `INSTANCE_MODEL_PATH` 环境变量 - 启动后可通过 `/api/v1/instance/single` 接口进行实例分割 - 代码文件下载到:`devdeploy` 目录 - 模型文件下载到:`weights` 目录 - 模型文件格式:`.onnx` 文件 ##### 数值任务(Numeric任务) 通过 `NUMERIC_TASK_CONFIG` 配置字典管理,当前支持: - **regression(回归服务)**: - `taskType="regression"` - 系统会设置 `MODEL_PATH` 环境变量 - 启动后可通过 `/api/v1/regression/single` 接口进行回归预测 - 代码文件下载到:`deployShuzhi` 目录 - 模型文件下载到:`save_model` 目录 **扩展新任务类型:** 系统采用配置字典方式管理任务类型,后续可以轻松扩展新的任务类型。只需在 `main.py` 的相应配置字典中添加新配置即可: **扩展视觉任务(如 detection):** ```python CV_TASK_CONFIG = { 'classification': {...}, 'segmentation': {...}, 'detection': { # 检测服务 'name': '检测服务', 'env_var': 'DETECT_MODEL_PATH', 'api_path': '/api/v1/detect/single', 'api_name': '检测', 'code_dir': 'devdeploy', 'model_dir': 'weights' }, 'instance_segmentation': { # 实例分割服务 'name': '实例分割服务', 'env_var': 'INSTANCE_MODEL_PATH', 'api_path': '/api/v1/instance/single', 'api_name': '实例分割', 'code_dir': 'devdeploy', 'model_dir': 'weights' } } ``` **扩展数值任务:** ```python NUMERIC_TASK_CONFIG = { 'regression': {...}, 'classification': { # 新增数值分类服务 'name': '数值分类服务', 'env_var': 'MODEL_PATH', 'api_path': '/api/v1/numeric/classify/single', 'api_name': '数值分类', 'code_dir': 'deployShuzhi', 'model_dir': 'save_model' } } ``` **注意:** - 所有任务类型通过 `ALL_TASK_CONFIG` 统一管理 - 视觉任务的代码和模型文件分别下载到 `devdeploy` 和 `weights` 目录 - 数值任务的代码和模型文件分别下载到 `deployShuzhi` 和 `save_model` 目录 - 系统会根据 `taskType` 参数自动选择正确的下载路径 #### 使用示例 ##### 1. 启动 HTTP 文件服务器(用于提供模型和代码文件) ```bash # 在包含 weights.zip 和 devdeploy.zip 的目录下 cd /path/to/files python3 -m http.server 8888 ``` ##### 2. 启动主服务 ```bash cd /path/to/jhai python main.py --port 8051 ``` ##### 3. 使用测试脚本 **启动视觉分类服务:** ```bash python script/cls_start.py # 请求中需要包含 "taskType": "classification" ``` **启动视觉分割服务:** ```bash python script/seg_start.py [port] # 默认端口: 8775 # 模型文件: seg.zip ``` **启动目标检测服务:** ```bash python script/det_start.py [port] # 默认端口: 8775 # 模型文件: detect.zip ``` **启动实例分割服务:** ```bash python script/instance_start.py [port] # 默认端口: 8775 # 模型文件: instance.zip ``` **启动数值回归服务:** ```bash python script/reg_start.py [port] # 默认端口: 8775 ``` **停止服务:** ```bash # 停止分类服务 python script/cls_stop.py [port] # 停止分割服务 python script/seg_stop.py [port] # 停止检测服务 python script/det_stop.py [port] # 停止实例分割服务 python script/instance_stop.py [port] # 停止回归服务 python script/reg_stop.py [port] ``` **删除服务:** ```bash # 删除分类服务 python script/cls_dele.py [port] # 删除分割服务 python script/seg_dele.py [port] # 删除检测服务 python script/det_dele.py [port] # 删除实例分割服务 python script/instance_dele.py [port] # 删除回归服务 python script/reg_dele.py [port] ``` **克隆和压缩Git仓库:** ```bash # 克隆仓库并压缩为zip文件 bash script/clone_and_zip.sh # 或者直接执行(需要先添加执行权限) chmod +x script/clone_and_zip.sh ./script/clone_and_zip.sh ``` 脚本功能: - 克隆 `https://gitee.com/hainingzhang/deployShuzhi.git`,压缩为 `deploy_code_data.zip` - 克隆 `https://gitee.com/hainingzhang/devdeploy.git`,压缩为 `deploy_code_cv.zip` - 自动清理临时文件和.git目录 - 如果文件已存在,会询问是否覆盖 #### 依赖要求 - Python 3.6+ - Docker - Docker Compose - Flask - Flask-CORS - requests - psutil - Git(用于clone_and_zip.sh脚本) - zip工具(用于clone_and_zip.sh脚本) **安装Python依赖:** ```bash pip install flask flask-cors requests psutil ``` **安装系统依赖(Ubuntu/Debian):** ```bash # 安装git和zip工具 sudo apt-get update sudo apt-get install -y git zip ``` #### 注意事项 1. **权限要求:** - 系统会自动检测是否需要 sudo 权限执行 Docker 命令 - 如果当前用户不在 docker 组中,需要 sudo 权限 2. **端口要求:** - 确保指定的服务端口未被占用 - 建议使用 10000-12000 范围内的端口 3. **文件要求:** - 模型文件必须是 ZIP 格式,解压后包含 `.onnx` 格式的模型文件 - 代码文件必须是 ZIP 格式,解压后包含 `devdeploy` 目录结构 - 代码目录必须包含 `Dockerfile.cpu` 和 `docker-compose.classifier.cpu.template.yml` 4. **网络要求:** - 确保能够访问模型和代码文件的下载地址 - 确保 Docker daemon 正在运行 5. **多服务共享网络问题:** - 当开启多个服务时,它们可能共享同一个Docker网络(如 `devdeploy_devdeploy-network`) - 停止单个服务时,可能会出现以下情况: - **孤儿容器警告**:系统会自动使用 `--remove-orphans` 参数清理这些容器 - **网络删除失败**:如果其他服务仍在使用该网络,会出现 "network has active endpoints" 错误 - **解决方案**:系统已优化停止逻辑,即使网络删除失败,只要容器已成功停止和删除,操作仍会被视为成功 - 这是正常现象,不影响服务的正常停止和删除 #### 错误处理 系统会返回标准的 HTTP 状态码和错误信息: - **200**:请求成功接收(异步处理) - **204**:操作失败(如端口被占用、文件未找到等) - **400**:请求参数错误 - **500**:服务器内部错误 所有错误信息都会在响应的 `message` 字段中返回详细说明。