Files
vllm-r9700-container/build_and_run.sh
T
2026-03-14 00:07:37 +08:00

453 lines
12 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# vLLM R9700 Container 智能部署脚本
# 自动检测 Git 代码更新,智能构建和部署
set -e
# ============== 日志配置 ==============
LOG_DIR="/opt/webhook"
LOG_FILE="${LOG_DIR}/vllm-deploy-$(date +%Y%m%d-%H%M%S).log"
LATEST_LOG="${LOG_DIR}/vllm-deploy-latest.log"
# 创建日志目录
mkdir -p "$LOG_DIR"
# 重定向所有输出到日志文件
exec > >(tee -a "$LOG_FILE") 2>&1
# 创建最新日志的软链接
ln -sf "$LOG_FILE" "$LATEST_LOG"
# ============== 配置变量(可修改) ==============
# 项目目录
PROJECT_DIR="/opt/project/vllm-r9700-container"
# 挂载配置
MODEL_DIR="${MODEL_DIR:-/opt/model}" # 本地模型目录(独立于项目目录)
CONFIG_FILE="${CONFIG_FILE:-${PROJECT_DIR}/model_config.yaml}" # 本地配置文件(项目根目录下)
# 容器内配置
LOCAL_MODEL_DIR="${LOCAL_MODEL_DIR:-/model}" # 容器内模型目录(与 Dockerfile 保持一致)
CONFIG_FILE_IN_CONTAINER="/etc/vllm/model_config.yaml" # 容器内配置文件路径
# Docker 镜像配置
IMAGE_NAME="custom-vllm-r9700"
IMAGE_TAG="latest"
CONTAINER_NAME="custom-vllm-r9700"
# Git 配置
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
# 打印带颜色的消息
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_step() {
echo -e "${CYAN}[STEP]${NC} $1"
}
# 显示使用帮助
show_usage() {
echo "用法:$0 [选项]"
echo ""
echo "选项:"
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 # 智能部署(检测 Git 更新)"
echo " $0 -f # 强制重新构建并部署"
echo " $0 -s # 停止并删除容器"
echo " $0 -p 8080 # 使用端口 8080 运行"
echo " $0 -b master # 使用 master 分支"
exit 0
}
# 解析命令行参数
FORCE_BUILD=false
STOP_ONLY=false
DETACH=true
CUSTOM_CONFIG=""
CUSTOM_MODELS=""
CUSTOM_PORT=""
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
FORCE_BUILD=true
shift
;;
-s|--stop)
STOP_ONLY=true
shift
;;
-d|--detach)
DETACH=true
shift
;;
-i|--interactive)
DETACH=false
shift
;;
-c|--config)
CUSTOM_CONFIG="$2"
shift 2
;;
-m|--models)
CUSTOM_MODELS="$2"
shift 2
;;
-p|--port)
CUSTOM_PORT="$2"
shift 2
;;
-b|--branch)
GIT_BRANCH="$2"
shift 2
;;
-h|--help)
show_usage
;;
*)
print_error "未知选项:$1"
show_usage
;;
esac
done
# 停止并删除容器
stop_container() {
print_info "停止并删除容器..."
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
docker stop "${CONTAINER_NAME}" 2>/dev/null || true
print_success "容器已停止"
docker rm "${CONTAINER_NAME}" 2>/dev/null || true
print_success "容器已删除"
else
print_warning "容器不存在"
fi
}
# 如果只需要停止容器,直接退出
if [ "$STOP_ONLY" = true ]; then
stop_container
exit 0
fi
# 检查 Docker 是否运行
check_docker() {
if ! docker info > /dev/null 2>&1; then
print_error "Docker 未运行或无权限访问"
print_error "请确保 Docker 服务正在运行,并且当前用户有 Docker 访问权限"
exit 1
fi
print_success "Docker 检查通过"
}
# 检查并切换到项目目录
check_project_dir() {
print_step "检查项目目录..."
if [ ! -d "$PROJECT_DIR" ]; then
print_error "项目目录不存在:${PROJECT_DIR}"
exit 1
fi
# 切换到项目目录
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}"
print_info "使用缓存策略:未修改的层将使用缓存"
BUILD_START=$(date +%s)
if DOCKER_BUILDKIT=1 docker build --build-arg MAX_JOBS=32 -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_step "开始部署容器..."
# 设置卷挂载参数
VOLUME_ARGS=""
# 模型目录
if [ -n "$CUSTOM_MODELS" ]; then
MODEL_DIR="$CUSTOM_MODELS"
fi
if [ -d "$MODEL_DIR" ]; then
VOLUME_ARGS="$VOLUME_ARGS -v ${MODEL_DIR}:${LOCAL_MODEL_DIR}"
print_info "模型目录:${MODEL_DIR} -> ${LOCAL_MODEL_DIR}"
else
print_warning "模型目录不存在:${MODEL_DIR}"
print_info "创建模型目录..."
mkdir -p "$MODEL_DIR"
VOLUME_ARGS="$VOLUME_ARGS -v ${MODEL_DIR}:${LOCAL_MODEL_DIR}"
fi
# 配置文件
if [ -n "$CUSTOM_CONFIG" ]; then
CONFIG_FILE="$CUSTOM_CONFIG"
fi
if [ -f "$CONFIG_FILE" ]; then
VOLUME_ARGS="$VOLUME_ARGS -v ${CONFIG_FILE}:${CONFIG_FILE_IN_CONTAINER}"
print_info "配置文件:${CONFIG_FILE} -> ${CONFIG_FILE_IN_CONTAINER}"
else
print_warning "配置文件不存在:${CONFIG_FILE}"
print_info "将使用容器内默认配置"
fi
# 端口设置
PORT_ARG="-p 8000:8000"
if [ -n "$CUSTOM_PORT" ]; then
PORT_ARG="-p ${CUSTOM_PORT}:8000"
fi
# 运行模式
RUN_MODE="-d"
if [ "$DETACH" = false ]; then
RUN_MODE="-it"
fi
# 构建运行命令
print_info "启动容器..."
docker run ${RUN_MODE} \
--name "${CONTAINER_NAME}" \
--device /dev/dri \
--device /dev/kfd \
--group-add video \
--group-add render \
--security-opt seccomp=unconfined \
${VOLUME_ARGS} \
${PORT_ARG} \
-e LOCAL_MODEL_DIR="${LOCAL_MODEL_DIR}" \
"${IMAGE_NAME}:${IMAGE_TAG}"
if [ $? -eq 0 ]; then
print_success "容器启动成功"
if [ "$DETACH" = true ]; then
echo ""
print_info "容器信息:"
echo " 容器名称:${CONTAINER_NAME}"
echo " 镜像:${IMAGE_NAME}:${IMAGE_TAG}"
echo " 状态:运行中"
echo " API 地址:http://localhost:8000"
echo ""
print_info "常用命令:"
echo " 查看日志:docker logs -f ${CONTAINER_NAME}"
echo " 停止容器:docker stop ${CONTAINER_NAME}"
echo " 启动容器:docker start ${CONTAINER_NAME}"
echo " 删除容器:docker rm -f ${CONTAINER_NAME}"
echo " 进入容器:docker exec -it ${CONTAINER_NAME} /bin/bash"
fi
else
print_error "容器启动失败"
exit 1
fi
}
# 智能部署主流程
smart_deploy() {
echo "=========================================="
echo "vLLM R9700 Container 智能部署"
echo "=========================================="
echo "开始时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo "日志文件:${LOG_FILE}"
echo ""
# 检查并切换到项目目录
check_project_dir
# 检查 Docker
check_docker
# 停止旧容器
stop_container
# 检查是否需要重新构建
NEED_REBUILD=false
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 ""
echo "=========================================="
print_success "部署完成"
echo "=========================================="
echo "完成时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo "日志文件:${LOG_FILE}"
echo "最新日志:${LATEST_LOG}"
}
# 执行主流程
smart_deploy