From 42622e8e1e4bc61c0478ef9477515a8eccec76d6 Mon Sep 17 00:00:00 2001 From: colomi <1421901449@qq.com> Date: Sat, 11 Jul 2026 19:11:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E7=BD=B2=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=85=BC=E5=AE=B9=E6=80=A7=E5=B9=B6=E7=94=9F=E6=88=90?= =?UTF-8?q?=E8=AF=A6=E7=BB=86=E9=83=A8=E7=BD=B2=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setup.sh: 自动检测 CONDA_BASE(conda info --base),移除 set -e 改为 错误收集,新增第8步验证 kohya 训练环境(测试 torch/accelerate 加载) - start_all_services.sh: 自动检测 CONDA_BASE,新增端口冲突检测和 WebUI 就绪等待(最多60秒轮询) - stop_all_services.sh: 新增停止脚本,按端口停止三个服务 - py310.yml: 移除硬编码 prefix 路径,避免跨机器部署问题 - DEPLOY.md: 详细部署操作手册,覆盖从 clone 到验证三大功能的全流程 - 已验证: kohya.tar.gz 解压→conda-unpack→PYTHONPATH 加载全部通过 --- DEPLOY.md | 644 ++++++++++++++++++++++++++++++++++++++++++ conda_envs/py310.yml | 1 - setup.sh | 88 ++++-- start_all_services.sh | 85 +++++- stop_all_services.sh | 19 ++ 5 files changed, 806 insertions(+), 31 deletions(-) create mode 100644 DEPLOY.md create mode 100755 stop_all_services.sh diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..7872a61 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,644 @@ +# 换发型项目部署操作手册 + +本手册详细描述在全新的 Ubuntu 22.04 + NVIDIA 3090 机器上,从零开始部署并运行换发型、换发色、训练模型三大功能的完整步骤。 + +--- + +## 目录 + +1. [环境前提](#1-环境前提) +2. [安装系统依赖](#2-安装系统依赖) +3. [Clone 代码](#3-clone-代码) +4. [下载网盘文件](#4-下载网盘文件) +5. [运行部署脚本](#5-运行部署脚本) +6. [启动服务](#6-启动服务) +7. [验证功能](#7-验证功能) +8. [API 接口参考](#8-api-接口参考) +9. [停止与重启服务](#9-停止与重启服务) +10. [常见问题排查](#10-常见问题排查) +11. [架构说明](#11-架构说明) + +--- + +## 1. 环境前提 + +| 项目 | 要求 | +|------|------| +| 操作系统 | Ubuntu 22.04 LTS | +| GPU | NVIDIA RTX 3090(或其他 10GB+ 显存的 NVIDIA GPU) | +| NVIDIA 驱动 | ≥ 525.x(支持 CUDA 11.8) | +| 磁盘空间 | ≥ 90GB(代码 + 模型 + 环境) | +| 网络 | 需要访问 PyPI(创建 py310 环境时下载 pip 包) | +| Python | 不需要预装(通过 conda 管理) | + +验证 GPU 和驱动: +```bash +nvidia-smi +# 应显示 GPU 信息和 CUDA 版本(≥ 11.8) +``` + +--- + +## 2. 安装系统依赖 + +### 2.1 安装基础工具 + +```bash +sudo apt update +sudo apt install -y git curl wget lsof +``` + +### 2.2 安装 Miniconda(如未安装) + +```bash +# 下载并安装 Miniconda +wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 + +# 初始化 conda +$HOME/miniconda3/bin/conda init bash +source ~/.bashrc + +# 验证 +conda --version +``` + +### 2.3 配置 conda 镜像(国内可选,加速 py310 创建) + +```bash +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main +conda config --set show_channel_urls yes +``` + +--- + +## 3. Clone 代码 + +```bash +cd ~ +git clone <仓库地址> change_hair_3090 +cd change_hair_3090 +``` + +Clone 后的目录结构(仅代码,不含模型/环境): +``` +change_hair_3090/ +├── hair_service_sd/ # 换发算法服务代码 +├── photo_service/ # 训练调度服务代码 +├── stable-diffusion-webui/ # SD WebUI 代码(不含 models/) +├── kohya_ss_home/ # kohya_ss 训练代码(不含 .local/) +├── conda_envs/ +│ └── py310.yml # py310 环境定义文件 +├── setup.sh # 部署脚本 +├── start_all_services.sh # 启动脚本 +├── stop_all_services.sh # 停止脚本 +└── README.md +``` + +--- + +## 4. 下载网盘文件 + +将网盘上的以下文件/目录下载到项目根目录(与代码合并)。**所有路径必须与下表一致**。 + +### 4.1 网盘文件清单 + +| 路径 | 大小 | 说明 | +|------|------|------| +| `hair_service_sd/weights/` | 9.9G | 换发算法模型(matting、分割等) | +| `stable-diffusion-webui/models/` | 41G | SD 模型 + 228 个 LoRA + 辅助模型 | +| `stable-diffusion-webui/extensions/sd-webui-controlnet/` | 4.4G | ControlNet 扩展 | +| `stable-diffusion-webui/repositories/` | 225M | SD 依赖仓库(k-diffusion 等) | +| `kohya_ss_home/.local/` | 7.6G | 训练 pip 包(torch、accelerate、diffusers 等) | +| `kohya_ss_home/.cache/` | 3.3M | CLIP tokenizer 缓存 | +| `kohya_ss_home/kohya_ss/` | 439M | kohya_ss 训练代码 | +| `data/` | 4.9G | 业务数据(参考发型图等) | +| `conda_envs/my_hair.tar.gz` | 4.2G | conda 环境(换发算法服务用) | +| `conda_envs/sdwebui.tar.gz` | 3.6G | conda 环境(SD WebUI 用) | +| `conda_envs/kohya.tar.gz` | 84M | conda 环境(LoRA 训练用,仅含 Python+numpy) | + +**合计约 76G** + +### 4.2 下载后验证 + +```bash +cd ~/change_hair_3090 + +# 检查关键文件是否存在 +ls -lh conda_envs/*.tar.gz +ls -d hair_service_sd/weights/ +ls -d kohya_ss_home/.local/ +ls -d stable-diffusion-webui/models/Stable-diffusion/v1-5-pruned-emaonly.safetensors +``` + +期望输出: +``` +-rw------- ... 84M ... conda_envs/kohya.tar.gz +-rw------- ... 4.2G ... conda_envs/my_hair.tar.gz +-rw------- ... 3.6G ... conda_envs/sdwebui.tar.gz +hair_service_sd/weights/ +kohya_ss_home/.local/ +stable-diffusion-webui/models/Stable-diffusion/v1-5-pruned-emaonly.safetensors +``` + +### 4.3 关于 kohya 训练环境的架构说明 + +LoRA 训练环境由两部分组成,缺一不可: + +``` +conda_envs/kohya.tar.gz (84M) kohya_ss_home/.local/ (7.6G) +┌─────────────────────────┐ ┌──────────────────────────────────┐ +│ Python 3.10 解释器 │ │ torch 2.0.1+cu118 │ +│ numpy 1.24.4 │ +PYTHONPATH→ │ accelerate 0.23.0 │ +│ tqdm │ │ bitsandbytes 0.41.1 │ +│ setuptools 69.5.1 │ │ diffusers 0.21.4 │ +│ (conda-pack 打包) │ │ transformers 4.30.2 │ +│ │ │ xformers 0.0.21 │ +│ │ │ safetensors, pytorch_lightning │ +│ │ │ (从 Docker 容器复制的 pip 包) │ +└─────────────────────────┘ └──────────────────────────────────┘ +``` + +`kohya.tar.gz` 仅包含 Python 解释器和 numpy(解决版本兼容性),通过 `PYTHONPATH` 环境变量指向 `.local/` 目录来加载 torch 等大包。这样避免重新下载 7.6G 的训练依赖。 + +--- + +## 5. 运行部署脚本 + +```bash +cd ~/change_hair_3090 +chmod +x setup.sh start_all_services.sh stop_all_services.sh +./setup.sh +``` + +### 5.1 setup.sh 执行的 8 个步骤 + +| 步骤 | 说明 | +|------|------| +| 1/8 | 检查前提条件(git、conda、NVIDIA 驱动) | +| 2/8 | 生成 `configure.ini`(自动替换 `__BASE_DIR__` 为实际路径) | +| 3/8 | 从 conda-pack 恢复 my_hair、sdwebui、kohya 三个 conda 环境 | +| 4/8 | 从 `py310.yml` 创建 py310 环境(需联网下载 pip 包) | +| 5/8 | 检查模型和数据目录完整性 | +| 6/8 | 检查训练底模 `v1-5-pruned-emaonly.safetensors` | +| 7/8 | 创建运行时目录(logs、data/tmp 等) | +| 8/8 | 验证 kohya 训练环境(测试 torch/accelerate 能否加载) | + +### 5.2 成功输出 + +``` +=== 换发型项目部署脚本 === +BASE_DIR: /home/xxx/change_hair_3090 +CONDA_BASE: /home/xxx/miniconda3 + +[1/8] 检查前提条件... + ✓ 前提条件满足 +[2/8] 生成 configure.ini... + ✓ configure.ini 已生成 +[3/8] 恢复 conda 环境(my_hair, sdwebui, kohya)... + ✓ my_hair 已恢复 + ✓ sdwebui 已恢复 + ✓ kohya 已恢复 +[4/8] 创建 py310 环境(从 yml)... + ✓ py310 已创建 +[5/8] 检查模型和数据目录... + ✓ hair_service_sd/weights + ✓ stable-diffusion-webui/models/Lora + ...(全部 ✓) +[6/8] 检查训练底模 v1-5-pruned-emaonly... + ✓ v1-5-pruned-emaonly.safetensors 已存在 +[7/8] 创建运行时目录... + ✓ 运行时目录已创建 +[8/8] 验证 kohya 训练环境... + 测试 torch/accelerate 加载... + torch=2.0.1+cu118 cuda=True + accelerate=0.23.0 + ✓ 训练环境验证通过 + +=== 部署结果 === +✓ 所有检查通过,部署完成! +``` + +### 5.3 如果 CONDA_BASE 不在默认路径 + +```bash +# 方法1:设置环境变量 +export CONDA_BASE=/your/conda/path +./setup.sh + +# 方法2:脚本会自动通过 `conda info --base` 检测 +# 只要 conda 在 PATH 中即可自动找到 +``` + +--- + +## 6. 启动服务 + +```bash +./start_all_services.sh +``` + +### 6.1 启动的三个服务 + +| 服务 | 端口 | conda 环境 | 功能 | +|------|------|-----------|------| +| hair_service_sd | 8801 | my_hair | 换发型/换发色核心算法 | +| stable-diffusion-webui | 57860 | sdwebui | SD 图生图推理 | +| photo_service | 32678 | py310 | LoRA 训练调度(训练子进程使用 kohya 环境) | + +### 6.2 成功输出 + +``` +=== 启动换发型服务 === +[1/3] 启动 hair_service_sd (端口 8801)... + hair_service_sd 已启动 (PID: xxxx, 端口 8801) +[2/3] 启动 stable-diffusion-webui (端口 57860)... + stable-diffusion-webui 已启动 (PID: xxxx, 端口 57860) + ℹ WebUI 首次启动需加载模型(约 30-60 秒),请耐心等待 +[3/3] 启动 photo_service (端口 32678)... + photo_service 已启动 (PID: xxxx, 端口 32678) + +=== 所有服务已启动 === +等待 WebUI 就绪(检查端口 57860)... +✓ WebUI 已就绪 +``` + +### 6.3 查看日志 + +```bash +# 换发型/换发色服务日志 +tail -f logs/hair_service.log + +# SD WebUI 日志 +tail -f logs/webui.log + +# 训练服务日志 +tail -f logs/photo_service.log +``` + +--- + +## 7. 验证功能 + +### 7.1 验证服务状态 + +```bash +# 检查三个端口是否在监听 +curl -s http://127.0.0.1:8801/ | head -5 +curl -s http://127.0.0.1:57860/sdapi/v1/options | head -5 +curl -s http://127.0.0.1:32678/ | head -5 +``` + +### 7.2 验证换发色功能 + +```bash +# 准备一张人像照片(base64 编码) +IMG_B64=$(base64 -w0 test_photo.jpg) + +curl -X POST http://127.0.0.1:8801/hairColor/v2 \ + -H "Content-Type: application/json" \ + -d "{ + \"img\": \"data:image/jpeg;base64,${IMG_B64}\", + \"userId\": \"test_user\", + \"rgb\": \"120,60,30\" + }" +``` + +参数说明: +- `img`:用户照片,支持 base64(`data:image/jpeg;base64,...`)或 URL +- `userId`:用户标识 +- `rgb`:目标发色 RGB 值(如 `120,60,30` 表示棕色) + +### 7.3 验证换发型功能 + +```bash +curl -X POST http://127.0.0.1:8801/api/swapHair/v1 \ + -H "Content-Type: application/json" \ + -d "{ + \"hair_id\": \"<发型ID>\", + \"task_id\": \"test_001\", + \"is_hr\": \"false\", + \"user_img_path\": \"data:image/jpeg;base64,${IMG_B64}\" + }" +``` + +参数说明: +- `hair_id`:发型 ID(对应 `data/ref_hairstyle/` 目录下的发型) +- `task_id`:任务唯一标识 +- `is_hr`:是否高清模式(`"true"` 或 `"false"`) +- `user_img_path`:用户照片,支持 base64 或 URL + +### 7.4 验证训练功能 + +训练需要准备训练素材(10+ 张同一发型的照片),组织成指定目录结构: + +``` +hair_material_dir/ +├── images/ +│ └── 1_hairstyle/ # 数字开头的文件夹 +│ ├── 001.png # 训练图片(≥10张) +│ ├── 001.txt # 标签文件(自动生成) +│ ├── 002.png +│ ├── 002.txt +│ └── ... +└── model/ # 训练输出目录(需提前创建) +``` + +发起训练请求: + +```bash +curl -X POST http://127.0.0.1:32678/api/hair/train \ + -H "Content-Type: application/json" \ + -d "{ + \"task_id\": \"train_001\", + \"hair_id\": \"test_hair\", + \"hair_material_dir\": \"/home/xxx/change_hair_3090/data/tmp/test_material\" + }" +``` + +训练日志查看: +```bash +tail -f logs/photo_service.log +# 训练开始后会看到 cmd_train 输出 +# 训练完成后会在 hair_material_dir/model/ 下生成 hairstyle_hd_lora.safetensors +``` + +训练参数(已在代码中固定): +- 底模:`v1-5-pruned-emaonly.safetensors` +- 训练步数:1500 步 +- 分辨率:2000x2000 +- LoRA dim:128,alpha:64 +- 优化器:AdamW8bit +- 精度:fp16 + +--- + +## 8. API 接口参考 + +### 8.1 换发色 API + +| 项目 | 值 | +|------|-----| +| URL | `POST http://:8801/hairColor/v2` | +| 参数 | `img`(base64或URL), `userId`, `rgb`(如`"120,60,30"`) | +| 返回 | JSON,含换色后的图片 URL 或 base64 | + +### 8.2 换发型 API + +| 项目 | 值 | +|------|-----| +| URL | `POST http://:8801/api/swapHair/v1` | +| 参数 | `hair_id`, `task_id`, `is_hr`, `user_img_path`(base64或URL), `output_format`(可选) | +| 返回 | JSON,含换发后的图片 URL 或 base64 | + +### 8.3 训练 API + +| 项目 | 值 | +|------|-----| +| URL | `POST http://:32678/api/hair/train` | +| 参数 | `task_id`, `hair_id`, `hair_material_dir`(训练素材目录绝对路径) | +| 返回 | `{"state": 0, "msg": "头发lora训练开始", "task_id": "..."}` | +| 训练回调 | 训练完成/失败后 POST 到 `http://127.0.0.1:8801/api/hair/trainCallBack` | + +### 8.4 推理 API + +| 项目 | 值 | +|------|-----| +| URL | `POST http://:32678/api/hair/inference` | +| 参数 | `hair_id`, `inference_port`, `hair_material_dir`, `request_json`(SD WebUI img2img 请求体) | + +--- + +## 9. 停止与重启服务 + +### 9.1 停止所有服务 + +```bash +./stop_all_services.sh +``` + +输出: +``` +=== 停止换发型服务 === + ✓ 已停止 hair_service_sd (端口 8801, PID xxxx) + ✓ 已停止 webui (端口 57860, PID xxxx) + ✓ 已停止 photo_service (端口 32678, PID xxxx) +``` + +### 9.2 重启服务 + +```bash +./stop_all_services.sh +./start_all_services.sh +``` + +### 9.3 手动停止单个服务 + +```bash +kill $(lsof -t -i:8801) # 停止换发服务 +kill $(lsof -t -i:57860) # 停止 WebUI +kill $(lsof -t -i:32678) # 停止训练服务 +``` + +--- + +## 10. 常见问题排查 + +### 10.1 setup.sh 报错 "conda 未安装" + +```bash +# 确认 conda 已安装 +which conda +# 如果没有,参考第2.2节安装 Miniconda +``` + +### 10.2 setup.sh 报错 "CONDA_BASE 不正确" + +```bash +# 查找 conda 安装路径 +which conda +# 或 +conda info --base + +# 设置后重新运行 +export CONDA_BASE=$(conda info --base) +./setup.sh +``` + +### 10.3 py310 环境创建失败(网络问题) + +py310 环境需要从 PyPI 下载 pip 包(flask、gevent、opencv-python 等)。如果网络不通: + +```bash +# 使用国内 PyPI 镜像 +pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple + +# 手动创建 +conda create -n py310 python=3.10 -y +conda activate py310 +pip install flask==3.0.3 gevent==24.2.1 opencv-python==4.10.0.84 numpy==2.0.0 scipy==1.14.0 scikit-image==0.24.0 pillow==10.4.0 requests==2.32.3 +``` + +### 10.4 start_all_services.sh 报 "端口已被占用" + +```bash +# 先停止已有服务 +./stop_all_services.sh + +# 确认端口已释放 +lsof -i :8801 +lsof -i :57860 +lsof -i :32678 + +# 重新启动 +./start_all_services.sh +``` + +### 10.5 WebUI 启动失败 + +```bash +# 查看详细日志 +tail -100 logs/webui.log + +# 常见原因: +# 1. 模型文件缺失 → 检查 stable-diffusion-webui/models/Stable-diffusion/ +# 2. 显存不足 → 检查 nvidia-smi +# 3. xformers 兼容性 → 尝试去掉 --xformers 参数 +``` + +### 10.6 训练失败 + +```bash +# 查看训练日志 +tail -100 logs/photo_service.log + +# 常见原因: +# 1. 训练素材目录结构不对 → 参考 7.4 节 +# 2. 底模缺失 → 检查 v1-5-pruned-emaonly.safetensors +# 3. .local 包损坏 → 重新从网盘下载 kohya_ss_home/.local/ +# 4. kohya 环境问题 → 重新运行 setup.sh +``` + +### 10.7 GPU 不可用(CUDA error) + +```bash +# 检查驱动 +nvidia-smi + +# 检查 CUDA +nvcc --version + +# 测试 torch CUDA +CONDA_BASE=$(conda info --base) +PYTHONPATH=$PWD/kohya_ss_home/.local/lib/python3.10/site-packages \ + $CONDA_BASE/envs/kohya/bin/python -c "import torch; print(torch.cuda.is_available())" +# 应输出 True +``` + +### 10.8 内存不足(OOM) + +3090 有 24GB 显存。如果同时运行三个服务出现 OOM: +```bash +# 先停止不需要的服务 +./stop_all_services.sh + +# 只启动需要的服务(如只换发型) +$CONDA_BASE/envs/my_hair/bin/python hair_service_sd/run_copy_cost_colorb64.py & +$CONDA_BASE/envs/sdwebui/bin/python stable-diffusion-webui/webui.py --api --listen --xformers --port 57860 & +``` + +--- + +## 11. 架构说明 + +### 11.1 系统架构 + +``` + 用户请求 + │ + ┌─────────────┼─────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────────┐ + │hair_service│ │photo_service│ │stable-diffusion│ + │ _sd │ │ │ │ -webui │ + │ (8801) │ │ (32678) │ │ (57860) │ + └────┬─────┘ └─────┬─────┘ └───────┬────────┘ + │ │ │ + │ 训练子进程 │ + │ │ │ + │ ┌────────┴────────┐ │ + │ │ kohya conda env │ │ + │ │ + .local/ 包 │ │ + │ │ accelerate │ │ + │ │ train_network │ │ + │ └─────────────────┘ │ + │ │ + ▼ ▼ + weights/ models/Stable-diffusion/ + (换发模型) v1-5-pruned-emaonly.safetensors + models/Lora/ (228个LoRA) +``` + +### 11.2 数据流 + +**换发型/换发色**: +``` +用户图片 → hair_service_sd(8801) → 调用 WebUI(57860) img2img → 返回结果 +``` + +**训练 LoRA**: +``` +训练素材 → photo_service(32678) → kohya环境+accelerate → train_network.py → 生成 LoRA +``` + +**训练后推理**: +``` +用户图片 + LoRA → photo_service(32678) → 复制LoRA到webui/models/Lora/ → WebUI img2img → 返回 +``` + +### 11.3 conda 环境说明 + +| 环境 | 用途 | 包含内容 | 大小 | +|------|------|---------|------| +| my_hair | 换发算法服务 | torch, cv2, flask, 换发依赖 | 4.2G | +| sdwebui | SD WebUI | torch, xformers, webui 依赖 | 3.6G | +| py310 | 训练调度服务 | flask, gevent, cv2, numpy 2.0 | ~500M | +| kohya | LoRA 训练 | Python 3.10, numpy 1.24 | 84M | +| .local/ | 训练大包(非conda) | torch 2.0.1, accelerate, diffusers | 7.6G | + +kohya 环境通过 `PYTHONPATH` 指向 `.local/` 目录来复用训练大包,避免重复下载。 + +--- + +## 附录:完整部署速查 + +```bash +# 1. 安装 conda(如未安装) +wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 +source ~/.bashrc + +# 2. Clone 代码 +cd ~ +git clone <仓库地址> change_hair_3090 +cd change_hair_3090 + +# 3. 下载网盘文件(合并到项目根目录) +# ... 手动下载 ... + +# 4. 部署 +chmod +x setup.sh start_all_services.sh stop_all_services.sh +./setup.sh + +# 5. 启动 +./start_all_services.sh + +# 6. 验证 +curl http://127.0.0.1:8801/ +curl http://127.0.0.1:57860/sdapi/v1/options +curl http://127.0.0.1:32678/ + +# 7. 停止 +./stop_all_services.sh +``` diff --git a/conda_envs/py310.yml b/conda_envs/py310.yml index 5277234..155cf6a 100644 --- a/conda_envs/py310.yml +++ b/conda_envs/py310.yml @@ -52,4 +52,3 @@ dependencies: - werkzeug==3.0.3 - zope-event==5.0 - zope-interface==6.4.post2 -prefix: /home/szlc/miniconda3/envs/py310 diff --git a/setup.sh b/setup.sh index e7faa9c..3120bae 100755 --- a/setup.sh +++ b/setup.sh @@ -1,8 +1,14 @@ #!/bin/bash -set -e BASE_DIR="$(cd "$(dirname "$0")" && pwd)" -CONDA_BASE="${CONDA_BASE:-/home/szlc/miniconda3}" + +# 自动检测 conda 安装路径(优先用环境变量,其次用 conda info --base) +if [ -z "$CONDA_BASE" ]; then + if command -v conda >/dev/null 2>&1; then + CONDA_BASE="$(conda info --base 2>/dev/null)" + fi +fi +CONDA_BASE="${CONDA_BASE:-$HOME/miniconda3}" # 初始化 conda(脚本中需要 source 才能用 conda activate) if [ -f "$CONDA_BASE/etc/profile.d/conda.sh" ]; then @@ -13,68 +19,112 @@ fi echo "=== 换发型项目部署脚本 ===" echo "BASE_DIR: $BASE_DIR" +echo "CONDA_BASE: $CONDA_BASE" +echo "" + +ERRORS=() # 1. 检查前提条件 -echo "[1/7] 检查前提条件..." -command -v git >/dev/null || { echo "ERROR: git 未安装"; exit 1; } -command -v conda >/dev/null || { echo "ERROR: conda 未安装"; exit 1; } -nvidia-smi >/dev/null 2>&1 || { echo "ERROR: NVIDIA 驱动未安装"; exit 1; } +echo "[1/8] 检查前提条件..." +command -v git >/dev/null || { echo " ✗ git 未安装"; ERRORS+=("git"); } +command -v conda >/dev/null || { echo " ✗ conda 未安装"; ERRORS+=("conda"); } +nvidia-smi >/dev/null 2>&1 || { echo " ✗ NVIDIA 驱动未安装"; ERRORS+=("nvidia-smi"); } +if [ ${#ERRORS[@]} -eq 0 ]; then + echo " ✓ 前提条件满足" +fi # 2. 生成 configure.ini -echo "[2/7] 生成 configure.ini..." +echo "[2/8] 生成 configure.ini..." sed "s|__BASE_DIR__|$BASE_DIR|g" "$BASE_DIR/hair_service_sd/config/configure.ini.template" > "$BASE_DIR/hair_service_sd/config/configure.ini" -echo " configure.ini 已生成" +echo " ✓ configure.ini 已生成" # 3. 恢复 conda 环境(conda-pack) -echo "[3/7] 恢复 conda 环境..." +echo "[3/8] 恢复 conda 环境(my_hair, sdwebui, kohya)..." for env_name in my_hair sdwebui kohya; do if [ -f "$BASE_DIR/conda_envs/${env_name}.tar.gz" ]; then echo " 恢复 $env_name ..." rm -rf "$CONDA_BASE/envs/$env_name" mkdir -p "$CONDA_BASE/envs/$env_name" tar -xzf "$BASE_DIR/conda_envs/${env_name}.tar.gz" -C "$CONDA_BASE/envs/$env_name" - conda activate "$env_name" - conda-unpack # 修复打包后的硬编码路径 - conda deactivate + conda activate "$env_name" 2>/dev/null + conda-unpack 2>/dev/null # 修复打包后的硬编码路径 + conda deactivate 2>/dev/null echo " ✓ $env_name 已恢复" else echo " ✗ conda_envs/${env_name}.tar.gz 不存在,请从网盘下载" + ERRORS+=("conda_envs/${env_name}.tar.gz") fi done # 4. 创建 py310 环境(从 yml) -echo "[4/7] 创建 py310 环境..." +echo "[4/8] 创建 py310 环境(从 yml)..." if [ -f "$BASE_DIR/conda_envs/py310.yml" ]; then - conda env create -f "$BASE_DIR/conda_envs/py310.yml" -n py310 2>/dev/null || echo " py310 环境已存在,跳过" - echo " ✓ py310 已就绪" + conda env create -f "$BASE_DIR/conda_envs/py310.yml" -n py310 2>/dev/null && echo " ✓ py310 已创建" || echo " ℹ py310 环境已存在,跳过" else echo " ✗ conda_envs/py310.yml 不存在" + ERRORS+=("conda_envs/py310.yml") fi # 5. 检查模型/数据目录 -echo "[5/7] 检查模型和数据目录..." +echo "[5/8] 检查模型和数据目录..." for dir in hair_service_sd/weights stable-diffusion-webui/models/Lora stable-diffusion-webui/models/Stable-diffusion stable-diffusion-webui/extensions/sd-webui-controlnet stable-diffusion-webui/repositories kohya_ss_home/.local kohya_ss_home/kohya_ss data/ref_hairstyle; do if [ -d "$BASE_DIR/$dir" ]; then echo " ✓ $dir" else echo " ✗ $dir 缺失,请从网盘下载" + ERRORS+=("$dir") fi done # 6. 检查训练底模 -echo "[6/7] 检查训练底模 v1-5-pruned-emaonly..." +echo "[6/8] 检查训练底模 v1-5-pruned-emaonly..." if [ -f "$BASE_DIR/stable-diffusion-webui/models/Stable-diffusion/v1-5-pruned-emaonly.safetensors" ]; then echo " ✓ v1-5-pruned-emaonly.safetensors 已存在" else echo " ✗ v1-5-pruned-emaonly.safetensors 缺失,请从网盘下载 models/ 目录" + ERRORS+=("v1-5-pruned-emaonly.safetensors") fi # 7. 创建运行时目录 -echo "[7/7] 创建运行时目录..." +echo "[7/8] 创建运行时目录..." mkdir -p "$BASE_DIR/logs" mkdir -p "$BASE_DIR/data/tmp" "$BASE_DIR/data/res_dir" "$BASE_DIR/data/userImage" "$BASE_DIR/data/user_info" mkdir -p "$BASE_DIR/kohya_ss_home/train_material" +echo " ✓ 运行时目录已创建" + +# 8. 验证 kohya 训练环境 +echo "[8/8] 验证 kohya 训练环境..." +KOHYA_PYTHON="$CONDA_BASE/envs/kohya/bin/python" +KOHYA_LOCAL="$BASE_DIR/kohya_ss_home/.local/lib/python3.10/site-packages" +if [ -f "$KOHYA_PYTHON" ] && [ -d "$KOHYA_LOCAL" ]; then + echo " 测试 torch/accelerate 加载..." + if PYTHONPATH="$KOHYA_LOCAL" "$KOHYA_PYTHON" -c " +import torch, accelerate, bitsandbytes, diffusers +print(f' torch={torch.__version__} cuda={torch.cuda.is_available()}') +print(f' accelerate={accelerate.__version__}') +print(' ✓ 训练环境验证通过') +" 2>/dev/null; then + true + else + echo " ✗ 训练环境验证失败,请检查 kohya 环境和 .local 目录" + ERRORS+=("kohya验证") + fi +else + echo " ✗ kohya Python 或 .local 目录不存在,跳过验证" + ERRORS+=("kohya环境") +fi echo "" -echo "=== 部署完成 ===" +echo "=== 部署结果 ===" +if [ ${#ERRORS[@]} -eq 0 ]; then + echo "✓ 所有检查通过,部署完成!" +else + echo "⚠ 有 ${#ERRORS[@]} 个问题需要处理:" + for err in "${ERRORS[@]}"; do + echo " - $err" + done + echo "" + echo "请从网盘下载缺失的文件后重新运行 ./setup.sh" +fi +echo "" echo "启动服务: ./start_all_services.sh" diff --git a/start_all_services.sh b/start_all_services.sh index 96895a4..fc693ff 100755 --- a/start_all_services.sh +++ b/start_all_services.sh @@ -1,24 +1,87 @@ #!/bin/bash + BASE_DIR="$(cd "$(dirname "$0")" && pwd)" -CONDA_BASE="${CONDA_BASE:-/home/szlc/miniconda3}" + +# 自动检测 conda 安装路径 +if [ -z "$CONDA_BASE" ]; then + if command -v conda >/dev/null 2>&1; then + CONDA_BASE="$(conda info --base 2>/dev/null)" + fi +fi +CONDA_BASE="${CONDA_BASE:-$HOME/miniconda3}" export CONDA_BASE mkdir -p "$BASE_DIR/logs" +# 检查端口是否被占用,如占用则提示 +check_port() { + local port=$1 + local name=$2 + if lsof -i :$port >/dev/null 2>&1; then + echo "⚠ 端口 $port ($name) 已被占用,可能服务已在运行" + echo " 如需重启,请先运行: ./stop_all_services.sh" + return 1 + fi + return 0 +} + +echo "=== 启动换发型服务 ===" +echo "BASE_DIR: $BASE_DIR" +echo "CONDA_BASE: $CONDA_BASE" +echo "" + +# 检查 conda 环境是否存在 +for env_name in my_hair sdwebui py310; do + if [ ! -f "$CONDA_BASE/envs/$env_name/bin/python" ]; then + echo "✗ conda 环境 $env_name 不存在,请先运行 ./setup.sh" + exit 1 + fi +done + # 1. hair_service_sd (端口 8801) -cd "$BASE_DIR/hair_service_sd" -nohup "$CONDA_BASE/envs/my_hair/bin/python" run_copy_cost_colorb64.py > "$BASE_DIR/logs/hair_service.log" 2>&1 & -echo "hair_service_sd 已启动 (PID: $!, 端口 8801)" +echo "[1/3] 启动 hair_service_sd (端口 8801)..." +if check_port 8801 "hair_service_sd"; then + cd "$BASE_DIR/hair_service_sd" + nohup "$CONDA_BASE/envs/my_hair/bin/python" run_copy_cost_colorb64.py > "$BASE_DIR/logs/hair_service.log" 2>&1 & + echo " hair_service_sd 已启动 (PID: $!, 端口 8801)" +fi # 2. stable-diffusion-webui (端口 57860) -cd "$BASE_DIR/stable-diffusion-webui" -nohup "$CONDA_BASE/envs/sdwebui/bin/python" webui.py --api --listen --xformers --port 57860 > "$BASE_DIR/logs/webui.log" 2>&1 & -echo "stable-diffusion-webui 已启动 (PID: $!, 端口 57860)" +echo "[2/3] 启动 stable-diffusion-webui (端口 57860)..." +if check_port 57860 "webui"; then + cd "$BASE_DIR/stable-diffusion-webui" + nohup "$CONDA_BASE/envs/sdwebui/bin/python" webui.py --api --listen --xformers --port 57860 > "$BASE_DIR/logs/webui.log" 2>&1 & + echo " stable-diffusion-webui 已启动 (PID: $!, 端口 57860)" + echo " ℹ WebUI 首次启动需加载模型(约 30-60 秒),请耐心等待" +fi # 3. photo_service (端口 32678) -cd "$BASE_DIR/photo_service" -nohup "$CONDA_BASE/envs/py310/bin/python" lora_train_service_1.py > "$BASE_DIR/logs/photo_service.log" 2>&1 & -echo "photo_service 已启动 (PID: $!, 端口 32678)" +echo "[3/3] 启动 photo_service (端口 32678)..." +if check_port 32678 "photo_service"; then + cd "$BASE_DIR/photo_service" + nohup "$CONDA_BASE/envs/py310/bin/python" lora_train_service_1.py > "$BASE_DIR/logs/photo_service.log" 2>&1 & + echo " photo_service 已启动 (PID: $!, 端口 32678)" +fi echo "" -echo "所有服务已在后台启动,日志在 $BASE_DIR/logs/" +echo "=== 所有服务已启动 ===" +echo "日志目录: $BASE_DIR/logs/" +echo "" +echo "查看日志:" +echo " tail -f $BASE_DIR/logs/hair_service.log # 换发型/换发色" +echo " tail -f $BASE_DIR/logs/webui.log # SD WebUI" +echo " tail -f $BASE_DIR/logs/photo_service.log # 训练服务" +echo "" +echo "停止服务: ./stop_all_services.sh" +echo "" +echo "等待 WebUI 就绪(检查端口 57860)..." +for i in $(seq 1 60); do + if curl -s http://127.0.0.1:57860/sdapi/v1/options >/dev/null 2>&1; then + echo "✓ WebUI 已就绪" + break + fi + sleep 2 + if [ $i -eq 60 ]; then + echo "⚠ WebUI 60 秒内未就绪,请检查日志: tail -f $BASE_DIR/logs/webui.log" + fi +done diff --git a/stop_all_services.sh b/stop_all_services.sh new file mode 100755 index 0000000..d42de06 --- /dev/null +++ b/stop_all_services.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo "=== 停止换发型服务 ===" + +# 按端口停止服务 +for port_info in "8801:hair_service_sd" "57860:webui" "32678:photo_service"; do + port="${port_info%%:*}" + name="${port_info##*:}" + if lsof -i :$port >/dev/null 2>&1; then + pid=$(lsof -t -i :$port) + kill $pid 2>/dev/null + echo " ✓ 已停止 $name (端口 $port, PID $pid)" + else + echo " ℹ $name 未在运行 (端口 $port)" + fi +done + +echo "" +echo "=== 所有服务已停止 ==="