From a48423d817c908a07cdb358137b6109ef940bee2 Mon Sep 17 00:00:00 2001 From: SZCJW <792430652@qq.com> Date: Tue, 10 Mar 2026 23:24:27 +0800 Subject: [PATCH] x --- README.md | 47 +++- build_and_run.sh | 210 ++++++++++++++---- .../__pycache__/start_vllm.cpython-313.pyc | Bin 0 -> 10445 bytes 3 files changed, 207 insertions(+), 50 deletions(-) create mode 100644 scripts/__pycache__/start_vllm.cpython-313.pyc diff --git a/README.md b/README.md index c8c0f71..cff7e19 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - 仅支持本地模型(无网络下载功能) - 多模型配置支持 - **提供标准的 OpenAI API 服务** +- **智能部署(自动检测 Git 更新)** ## 快速开始 @@ -237,22 +238,36 @@ server: ## 启动 vLLM 服务器 -### 使用构建和部署脚本(推荐) +### 使用智能部署脚本(推荐) -项目提供了 `build_and_run.sh` 脚本,用于一键构建镜像并部署运行: +项目提供了 `build_and_run.sh` 脚本,具有智能检测和自动部署功能: + +**智能部署逻辑:** + +1. **检测 Git 代码更新**: + - 自动拉取远程仓库最新代码 + - 比较本地和远程代码版本 + - 检测本地未提交的更改 + +2. **智能构建决策**: + - ✅ 代码有更新 → 重新构建镜像 + - ✅ 本地有更改 → 重新构建镜像 + - ✅ 镜像不存在 → 构建镜像 + - ⏭️ 代码无更新且镜像存在 → 跳过构建,直接部署 + +3. **自动部署**: + - 停止并删除旧容器 + - 使用新镜像(或现有镜像)启动新容器 ```bash # 赋予执行权限 chmod +x build_and_run.sh -# 构建并运行(默认后台运行) +# 智能部署(自动检测 Git 更新) ./build_and_run.sh -# 仅构建镜像 -./build_and_run.sh -b - -# 重新构建镜像(不使用缓存) -./build_and_run.sh -r +# 强制重新构建(忽略 Git 状态) +./build_and_run.sh -f # 仅停止并删除容器 ./build_and_run.sh -s @@ -265,20 +280,30 @@ chmod +x build_and_run.sh # 交互式运行(前台运行) ./build_and_run.sh -i + +# 指定 Git 分支 +./build_and_run.sh -b master ``` **脚本选项:** -- `-b, --build-only`:仅构建镜像,不运行容器 -- `-r, --rebuild`:重新构建镜像(不使用缓存) +- `-f, --force`:强制重新构建镜像(忽略 Git 状态) - `-s, --stop`:仅停止并删除容器 - `-d, --detach`:后台运行容器(默认) - `-i, --interactive`:交互式运行容器 - `-c, --config FILE`:指定配置文件路径 - `-m, --models DIR`:指定模型目录路径 - `-p, --port PORT`:指定服务端口 +- `-b, --branch NAME`:指定 Git 分支(默认:main) - `-h, --help`:显示帮助信息 +**使用场景:** + +- **首次部署**:直接运行 `./build_and_run.sh`,自动构建并部署 +- **日常更新**:运行 `./build_and_run.sh`,自动检测代码更新并重新部署 +- **快速重启**:代码无更新时,跳过构建,直接重启容器 +- **强制更新**:使用 `-f` 参数强制重新构建 + ### 手动部署 ```bash @@ -440,5 +465,5 @@ print(response.choices[0].text) - `README.md` - 项目说明文档 - `model_config.yaml.example` - 配置文件示例 - `params_config.yaml` - 参数配置文件(定义各类参数的类型和验证规则) -- `build_and_run.sh` - 构建和部署脚本 +- `build_and_run.sh` - 智能部署脚本(自动检测 Git 更新) - `scripts/start_vllm.py` - vLLM 启动脚本 diff --git a/build_and_run.sh b/build_and_run.sh index cd205cf..6c1a0b0 100644 --- a/build_and_run.sh +++ b/build_and_run.sh @@ -1,7 +1,7 @@ #!/bin/bash -# vLLM R9700 Container 构建和部署脚本 -# 用于一键构建镜像并部署运行 +# vLLM R9700 Container 智能部署脚本 +# 自动检测 Git 代码更新,智能构建和部署 set -e @@ -9,15 +9,18 @@ set -e IMAGE_NAME="custom-vllm-r9700" IMAGE_TAG="latest" CONTAINER_NAME="custom-vllm-r9700" +PROJECT_DIR="/opt/project/vllm-r9700-container" MODEL_DIR="${MODEL_DIR:-/opt/models}" CONFIG_FILE="${CONFIG_FILE:-/opt/model_config.yaml}" LOCAL_MODEL_DIR="${LOCAL_MODEL_DIR:-/models}" +GIT_BRANCH="${GIT_BRANCH:-main}" # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' NC='\033[0m' # No Color # 打印带颜色的消息 @@ -37,35 +40,36 @@ print_error() { echo -e "${RED}[ERROR]${NC} $1" } +print_step() { + echo -e "${CYAN}[STEP]${NC} $1" +} + # 显示使用帮助 show_usage() { echo "用法:$0 [选项]" echo "" echo "选项:" - echo " -b, --build-only 仅构建镜像,不运行容器" - echo " -r, --rebuild 重新构建镜像(不使用缓存)" + echo " -f, --force 强制重新构建镜像(忽略 Git 状态)" echo " -s, --stop 仅停止并删除容器" echo " -d, --detach 后台运行容器(默认)" echo " -i, --interactive 交互式运行容器" echo " -c, --config FILE 指定配置文件路径" echo " -m, --models DIR 指定模型目录路径" echo " -p, --port PORT 指定服务端口" + echo " -b, --branch NAME 指定 Git 分支(默认:main)" echo " -h, --help 显示此帮助信息" echo "" echo "示例:" - echo " $0 # 构建并运行" - echo " $0 -b # 仅构建镜像" - echo " $0 -r # 重新构建并运行" + echo " $0 # 智能部署(检测 Git 更新)" + echo " $0 -f # 强制重新构建并部署" echo " $0 -s # 停止并删除容器" echo " $0 -p 8080 # 使用端口 8080 运行" - echo " $0 -c /path/to/config.yaml # 使用指定配置文件" - echo " $0 -m /path/to/models # 使用指定模型目录" + echo " $0 -b master # 使用 master 分支" exit 0 } # 解析命令行参数 -BUILD_ONLY=false -REBUILD=false +FORCE_BUILD=false STOP_ONLY=false DETACH=true CUSTOM_CONFIG="" @@ -74,12 +78,8 @@ CUSTOM_PORT="" while [[ $# -gt 0 ]]; do case $1 in - -b|--build-only) - BUILD_ONLY=true - shift - ;; - -r|--rebuild) - REBUILD=true + -f|--force) + FORCE_BUILD=true shift ;; -s|--stop) @@ -106,6 +106,10 @@ while [[ $# -gt 0 ]]; do CUSTOM_PORT="$2" shift 2 ;; + -b|--branch) + GIT_BRANCH="$2" + shift 2 + ;; -h|--help) show_usage ;; @@ -147,28 +151,116 @@ check_docker() { print_success "Docker 检查通过" } -# 构建镜像 -build_image() { - print_info "开始构建 Docker 镜像..." - print_info "镜像名称:${IMAGE_NAME}:${IMAGE_TAG}" +# 检查并切换到项目目录 +check_project_dir() { + print_step "检查项目目录..." - BUILD_ARGS="--no-cache" - if [ "$REBUILD" = false ]; then - BUILD_ARGS="" + if [ ! -d "$PROJECT_DIR" ]; then + print_error "项目目录不存在:${PROJECT_DIR}" + exit 1 fi - if docker build ${BUILD_ARGS} -t "${IMAGE_NAME}:${IMAGE_TAG}" .; then - print_success "镜像构建成功" + # 切换到项目目录 + cd "$PROJECT_DIR" + print_info "项目目录:${PROJECT_DIR}" + print_success "已切换到项目目录" +} + +# 检查 Git 仓库 +check_git() { + if [ ! -d ".git" ]; then + print_warning "当前目录不是 Git 仓库" + return 1 + fi + + # 检查 git 命令是否存在 + if ! command -v git &> /dev/null; then + print_warning "Git 未安装,跳过 Git 检查" + return 1 + fi + + return 0 +} + +# 拉取最新代码 +pull_latest_code() { + print_step "拉取最新代码..." + + # 保存当前分支 + current_branch=$(git rev-parse --abbrev-ref HEAD) + + # 尝试拉取 + if git fetch origin "${GIT_BRANCH}" 2>/dev/null; then + # 检查是否有更新 + LOCAL=$(git rev-parse HEAD) + REMOTE=$(git rev-parse origin/"${GIT_BRANCH}") + + if [ "$LOCAL" != "$REMOTE" ]; then + print_info "检测到代码更新,正在更新..." + if git pull origin "${GIT_BRANCH}" 2>/dev/null; then + print_success "代码已更新到最新版本" + return 0 # 有更新 + else + print_warning "代码拉取失败,继续执行" + return 1 # 无更新或失败 + fi + else + print_info "代码已是最新版本" + return 1 # 无更新 + fi + else + print_warning "无法连接到远程仓库,使用本地代码" + return 1 # 失败 + fi +} + +# 检查代码是否有本地修改 +has_local_changes() { + # 检查是否有未提交的更改 + if ! git diff --quiet 2>/dev/null; then + print_info "检测到未提交的更改" + return 0 + fi + + # 检查是否有暂存的更改 + if ! git diff --cached --quiet 2>/dev/null; then + print_info "检测到暂存的更改" + return 0 + fi + + return 1 # 无更改 +} + +# 构建镜像 +build_image() { + print_step "开始构建 Docker 镜像..." + print_info "镜像名称:${IMAGE_NAME}:${IMAGE_TAG}" + print_info "构建目录:${PROJECT_DIR}" + + BUILD_START=$(date +%s) + + if docker build --no-cache -t "${IMAGE_NAME}:${IMAGE_TAG}" .; then + BUILD_END=$(date +%s) + BUILD_TIME=$((BUILD_END - BUILD_START)) + + print_success "镜像构建成功 (耗时:${BUILD_TIME}秒)" docker images "${IMAGE_NAME}:${IMAGE_TAG}" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" + return 0 # 构建成功 else print_error "镜像构建失败" exit 1 fi } +# 检查镜像是否存在 +image_exists() { + docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" > /dev/null 2>&1 + return $? +} + # 运行容器 run_container() { - print_info "开始部署容器..." + print_step "开始部署容器..." # 设置卷挂载参数 VOLUME_ARGS="" @@ -252,32 +344,72 @@ run_container() { fi } -# 主流程 -main() { +# 智能部署主流程 +smart_deploy() { echo "==========================================" - echo "vLLM R9700 Container 构建和部署" + echo "vLLM R9700 Container 智能部署" echo "==========================================" echo "开始时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "" + # 检查并切换到项目目录 + check_project_dir + # 检查 Docker check_docker # 停止旧容器 stop_container - # 构建镜像 - build_image + # 检查是否需要重新构建 + NEED_REBUILD=false - # 如果仅构建,退出 - if [ "$BUILD_ONLY" = true ]; then - print_success "镜像构建完成" - print_info "运行以下命令启动容器:" - echo " $0" - exit 0 + if [ "$FORCE_BUILD" = true ]; then + print_step "强制重新构建模式" + NEED_REBUILD=true + elif check_git; then + # 是 Git 仓库 + print_step "检查代码更新..." + + # 拉取最新代码并检查是否有更新 + if pull_latest_code; then + NEED_REBUILD=true + print_info "代码有更新,需要重新构建" + elif has_local_changes; then + NEED_REBUILD=true + print_info "检测到本地更改,需要重新构建" + else + # 检查镜像是否存在 + if ! image_exists; then + NEED_REBUILD=true + print_info "镜像不存在,需要构建" + else + print_info "代码无更新,跳过构建" + fi + fi + else + # 不是 Git 仓库或 Git 不可用 + print_warning "无法检测 Git 状态" + + # 检查镜像是否存在 + if ! image_exists; then + NEED_REBUILD=true + print_info "镜像不存在,需要构建" + else + print_info "使用现有镜像" + fi + fi + + # 构建镜像(如果需要) + if [ "$NEED_REBUILD" = true ]; then + echo "" + build_image + else + print_success "使用现有镜像" fi # 运行容器 + echo "" run_container echo "" @@ -288,4 +420,4 @@ main() { } # 执行主流程 -main +smart_deploy diff --git a/scripts/__pycache__/start_vllm.cpython-313.pyc b/scripts/__pycache__/start_vllm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cd44aca6a0dd87f4a399b173a2ec563a26017f0 GIT binary patch literal 10445 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 literal 0 HcmV?d00001