优化 run.sh 三种部署方式的升级清理逻辑
- 共用 clean/build 函数,升级前清理 frontend/dist、backend/static 与旧二进制 - binary upgrade/start/stop/status:停服、备份、重编译、后台启动并持久化启动参数 - docker up/upgrade:--force-recreate --remove-orphans,支持 up fresh 无缓存重建 - kubernetes upgrade:--no-cache 构建 + 时间戳镜像 tag,set image 触发滚动更新
This commit is contained in:
1 parent
921b44d6a4
commit
ac69ae75d4
1 file changed
+356
-110
@@ -14,7 +14,8 @@
|
|||||||
#
|
#
|
||||||
# 方式 3 — 独立二进制:
|
# 方式 3 — 独立二进制:
|
||||||
# ./run.sh build 编译独立二进制 (embed 前端)
|
# ./run.sh build 编译独立二进制 (embed 前端)
|
||||||
# ./run.sh binary 编译并启动
|
# ./run.sh binary upgrade 停服 → 清旧产物 → 重编译 → 后台启动
|
||||||
|
# ./run.sh binary start|stop|status
|
||||||
#
|
#
|
||||||
# 本地开发:
|
# 本地开发:
|
||||||
# ./run.sh dev 前后端热重载
|
# ./run.sh dev 前后端热重载
|
||||||
@@ -60,6 +61,8 @@ cd "$SCRIPT_DIR"
|
|||||||
# PID 文件
|
# PID 文件
|
||||||
BACKEND_PID_FILE="$SCRIPT_DIR/$BACKEND_DIR/.backend.pid"
|
BACKEND_PID_FILE="$SCRIPT_DIR/$BACKEND_DIR/.backend.pid"
|
||||||
FRONTEND_PID_FILE="$SCRIPT_DIR/$FRONTEND_DIR/.frontend.pid"
|
FRONTEND_PID_FILE="$SCRIPT_DIR/$FRONTEND_DIR/.frontend.pid"
|
||||||
|
BINARY_PID_FILE="$SCRIPT_DIR/$BACKEND_DIR/.aiops.pid"
|
||||||
|
BINARY_ARGS_FILE="$SCRIPT_DIR/$BACKEND_DIR/.aiops.args"
|
||||||
|
|
||||||
# 检测操作系统
|
# 检测操作系统
|
||||||
detect_os() {
|
detect_os() {
|
||||||
@@ -290,6 +293,196 @@ cmd_logs() {
|
|||||||
wait
|
wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 构建清理(三种部署方式共用,避免旧代码/旧静态资源残留)// Logan.Li
|
||||||
|
# ============================================================
|
||||||
|
clean_frontend_artifacts() {
|
||||||
|
info "清理前端旧构建产物..."
|
||||||
|
rm -rf "$SCRIPT_DIR/$FRONTEND_DIR/dist"
|
||||||
|
rm -rf "$SCRIPT_DIR/$FRONTEND_DIR/node_modules/.vite"
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_embed_static() {
|
||||||
|
info "清理 embed static 旧文件..."
|
||||||
|
mkdir -p "$SCRIPT_DIR/$BACKEND_DIR/static"
|
||||||
|
find "$SCRIPT_DIR/$BACKEND_DIR/static" -mindepth 1 -delete 2>/dev/null || rm -rf "$SCRIPT_DIR/$BACKEND_DIR/static"/*
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_go_artifacts() {
|
||||||
|
info "清理 Go 旧编译产物..."
|
||||||
|
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN"
|
||||||
|
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$BACKEND_BIN"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建前端并同步到 backend/static(二进制 / embed 镜像共用)// Logan.Li
|
||||||
|
build_frontend_embed() {
|
||||||
|
clean_frontend_artifacts
|
||||||
|
cd "$SCRIPT_DIR/$FRONTEND_DIR"
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
fi
|
||||||
|
npm run build 2>&1 | tail -8
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
clean_embed_static
|
||||||
|
cp -r "$FRONTEND_DIR/dist/"* "$BACKEND_DIR/static/"
|
||||||
|
local fe_files
|
||||||
|
fe_files=$(find "$BACKEND_DIR/static" -type f | wc -l | tr -d ' ')
|
||||||
|
success "前端已嵌入 static/ ($fe_files 个文件)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 停止独立二进制进程(PID 文件 + 端口双保险)// Logan.Li
|
||||||
|
stop_binary_process() {
|
||||||
|
local port=${1:-8080}
|
||||||
|
if [ -f "$BINARY_PID_FILE" ]; then
|
||||||
|
local pid
|
||||||
|
pid=$(cat "$BINARY_PID_FILE" 2>/dev/null || echo "")
|
||||||
|
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
|
success "已停止二进制进程 (PID: $pid)"
|
||||||
|
fi
|
||||||
|
rm -f "$BINARY_PID_FILE"
|
||||||
|
fi
|
||||||
|
# 兜底:按端口和进程名清理残留
|
||||||
|
if [ "$OS" = "macos" ]; then
|
||||||
|
local pids
|
||||||
|
pids=$(lsof -ti:"$port" 2>/dev/null || echo "")
|
||||||
|
[ -n "$pids" ] && echo "$pids" | xargs kill -9 2>/dev/null || true
|
||||||
|
else
|
||||||
|
fuser -k "${port}/tcp" 2>/dev/null || true
|
||||||
|
pkill -f "${SCRIPT_DIR}/${BACKEND_DIR}/${STANDALONE_BIN}" 2>/dev/null || true
|
||||||
|
pkill -f "./${STANDALONE_BIN} --port" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_binary_ready() {
|
||||||
|
local port=${1:-8080}
|
||||||
|
local retries=0
|
||||||
|
while ! curl -sf "http://localhost:${port}/version" > /dev/null 2>&1; do
|
||||||
|
retries=$((retries + 1))
|
||||||
|
if [ $retries -ge 30 ]; then
|
||||||
|
error "二进制启动超时,查看日志: $BACKEND_DIR/logs/aiops.log"
|
||||||
|
tail -20 "$SCRIPT_DIR/$BACKEND_DIR/logs/aiops.log" 2>/dev/null || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 确保二进制默认 config.yaml 存在 // Logan.Li
|
||||||
|
ensure_binary_config() {
|
||||||
|
local cfg="$SCRIPT_DIR/$BACKEND_DIR/config.yaml"
|
||||||
|
if [ -f "$cfg" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
info "生成默认 config.yaml (SQLite + 无Redis)..."
|
||||||
|
cat > "$cfg" << 'YAML'
|
||||||
|
mode: "release"
|
||||||
|
port: "8080"
|
||||||
|
log_level: "info"
|
||||||
|
|
||||||
|
database:
|
||||||
|
driver: "sqlite"
|
||||||
|
host: "localhost"
|
||||||
|
port: 3306
|
||||||
|
user: ""
|
||||||
|
password: ""
|
||||||
|
dbname: "data/aiops.db"
|
||||||
|
charset: "utf8mb4"
|
||||||
|
max_conn: 100
|
||||||
|
max_idle: 10
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: "localhost"
|
||||||
|
port: 6379
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: "CHANGE_ME_JWT_SECRET" # ⚠️ 生产环境必须替换
|
||||||
|
expire_hour: 24
|
||||||
|
|
||||||
|
websocket:
|
||||||
|
read_buffer_size: 1024
|
||||||
|
write_buffer_size: 1024
|
||||||
|
YAML
|
||||||
|
}
|
||||||
|
|
||||||
|
# 编译独立二进制(含 embed 前端)// Logan.Li
|
||||||
|
build_standalone_binary() {
|
||||||
|
build_frontend_embed
|
||||||
|
ensure_binary_config
|
||||||
|
clean_go_artifacts
|
||||||
|
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
||||||
|
info "编译中..."
|
||||||
|
CGO_ENABLED=1 GOPROXY=https://goproxy.cn,direct GONOSUMCHECK='*' GOTOOLCHAIN=go1.24.0 \
|
||||||
|
go build -ldflags="-w -s" -o "$STANDALONE_BIN" .
|
||||||
|
if [ ! -f "$STANDALONE_BIN" ]; then
|
||||||
|
error "编译失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local bin_size
|
||||||
|
bin_size=$(du -h "$STANDALONE_BIN" | cut -f1)
|
||||||
|
success "编译完成: $BACKEND_DIR/$STANDALONE_BIN ($bin_size)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 解析/持久化二进制启动参数 // Logan.Li
|
||||||
|
save_binary_args() {
|
||||||
|
printf '%s\n' "$@" > "$BINARY_ARGS_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_binary_args() {
|
||||||
|
local -n _out=$1
|
||||||
|
_out=()
|
||||||
|
if [ -f "$BINARY_ARGS_FILE" ]; then
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
[ -n "$line" ] && _out+=("$line")
|
||||||
|
done < "$BINARY_ARGS_FILE"
|
||||||
|
fi
|
||||||
|
if [ ${#_out[@]} -eq 0 ]; then
|
||||||
|
_out=(--port 8080 --db-driver sqlite)
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_listen_port() {
|
||||||
|
local args=("$@")
|
||||||
|
local i=0
|
||||||
|
while [ $i -lt ${#args[@]} ]; do
|
||||||
|
if [ "${args[$i]}" = "--port" ] && [ $((i + 1)) -lt ${#args[@]} ]; then
|
||||||
|
echo "${args[$((i + 1))]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
echo "8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 后台启动独立二进制 // Logan.Li
|
||||||
|
start_binary_daemon() {
|
||||||
|
local args=("$@")
|
||||||
|
local port
|
||||||
|
port=$(binary_listen_port "${args[@]}")
|
||||||
|
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
||||||
|
mkdir -p data logs uploads
|
||||||
|
rotate_log "logs/aiops.log"
|
||||||
|
stop_binary_process "$port"
|
||||||
|
nohup ./"$STANDALONE_BIN" "${args[@]}" >> logs/aiops.log 2>&1 &
|
||||||
|
echo $! > "$BINARY_PID_FILE"
|
||||||
|
save_binary_args "${args[@]}"
|
||||||
|
if wait_binary_ready "$port"; then
|
||||||
|
success "二进制已启动 (PID: $(cat "$BINARY_PID_FILE"), 端口: $port)"
|
||||||
|
info "日志: $BACKEND_DIR/logs/aiops.log"
|
||||||
|
info "健康检查: http://localhost:${port}/version"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Docker Compose 部署
|
# Docker Compose 部署
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -331,9 +524,21 @@ cmd_docker() {
|
|||||||
info "BuildKit: 已启用 (增量编译 + 缓存复用)"
|
info "BuildKit: 已启用 (增量编译 + 缓存复用)"
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
up)
|
up|upgrade|update)
|
||||||
banner "========== Docker Compose 启动 =========="
|
local fresh_build=0
|
||||||
$DOCKER_COMPOSE up -d --build
|
if [ "${2:-}" = "fresh" ] || [ "$action" = "fresh" ]; then
|
||||||
|
fresh_build=1
|
||||||
|
fi
|
||||||
|
banner "========== Docker Compose 启动/升级 =========="
|
||||||
|
clean_frontend_artifacts
|
||||||
|
if [ "$fresh_build" -eq 1 ]; then
|
||||||
|
info "全量重建镜像 (--no-cache)..."
|
||||||
|
$DOCKER_COMPOSE build --no-cache
|
||||||
|
$DOCKER_COMPOSE up -d --force-recreate --remove-orphans
|
||||||
|
else
|
||||||
|
info "增量构建 + 强制重建容器..."
|
||||||
|
$DOCKER_COMPOSE up -d --build --force-recreate --remove-orphans
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
# 清理历史 build cache 和悬空镜像,防止占满磁盘
|
# 清理历史 build cache 和悬空镜像,防止占满磁盘
|
||||||
info "清理历史构建缓存..."
|
info "清理历史构建缓存..."
|
||||||
@@ -389,15 +594,20 @@ cmd_docker() {
|
|||||||
echo " $(df -h / | awk 'NR==2{print $3, "used /", $2, "total ("$5")"}')"
|
echo " $(df -h / | awk 'NR==2{print $3, "used /", $2, "total ("$5")"}')"
|
||||||
success "清理完成"
|
success "清理完成"
|
||||||
;;
|
;;
|
||||||
|
fresh)
|
||||||
|
cmd_docker up fresh
|
||||||
|
;;
|
||||||
help|--help|-h)
|
help|--help|-h)
|
||||||
echo "Docker Compose 部署命令:"
|
echo "Docker Compose 部署命令:"
|
||||||
echo " ./run.sh docker up 构建并启动所有容器"
|
echo " ./run.sh docker up 增量构建 + 强制重建容器"
|
||||||
echo " ./run.sh docker down 停止并移除容器"
|
echo " ./run.sh docker up fresh 无缓存全量重建镜像并启动"
|
||||||
echo " ./run.sh docker build 仅构建镜像"
|
echo " ./run.sh docker upgrade 同 up(升级别名)"
|
||||||
echo " ./run.sh docker restart 重启容器"
|
echo " ./run.sh docker down 停止并移除容器"
|
||||||
echo " ./run.sh docker logs 查看实时日志"
|
echo " ./run.sh docker build 仅构建镜像"
|
||||||
echo " ./run.sh docker ps 查看容器状态"
|
echo " ./run.sh docker restart 重启容器"
|
||||||
echo " ./run.sh docker clean 清理构建缓存/无用镜像"
|
echo " ./run.sh docker logs 查看实时日志"
|
||||||
|
echo " ./run.sh docker ps 查看容器状态"
|
||||||
|
echo " ./run.sh docker clean 清理构建缓存/无用镜像"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
error "未知 docker 子命令: $action"
|
error "未知 docker 子命令: $action"
|
||||||
@@ -416,78 +626,12 @@ cmd_build() {
|
|||||||
case "$target" in
|
case "$target" in
|
||||||
binary|aiops)
|
binary|aiops)
|
||||||
banner "========== 编译独立二进制 =========="
|
banner "========== 编译独立二进制 =========="
|
||||||
|
build_standalone_binary || exit 1
|
||||||
# 构建前端 → 拷贝到 embed 目录(go:embed 需要 static/ 下有文件)
|
echo ""
|
||||||
info "构建前端 → embed..."
|
info "使用方法:"
|
||||||
cd "$SCRIPT_DIR/$FRONTEND_DIR"
|
echo " ./run.sh binary upgrade # 停服重编译并后台启动"
|
||||||
if [ ! -d "node_modules" ]; then
|
echo " ./$BACKEND_DIR/$STANDALONE_BIN --port 8080 # 前台启动"
|
||||||
npm install --registry=https://registry.npmmirror.com
|
echo " ./$BACKEND_DIR/$STANDALONE_BIN --config config.yaml # 使用配置文件"
|
||||||
fi
|
|
||||||
npm run build 2>&1 | tail -5
|
|
||||||
|
|
||||||
# 拷贝前端构建产物到后端 embed 目录
|
|
||||||
cd "$SCRIPT_DIR"
|
|
||||||
rm -rf "$BACKEND_DIR/static"/*
|
|
||||||
cp -r "$FRONTEND_DIR/dist/"* "$BACKEND_DIR/static/"
|
|
||||||
local fe_files=$(find "$BACKEND_DIR/static" -type f | wc -l)
|
|
||||||
success "前端已嵌入 ($fe_files 文件)"
|
|
||||||
|
|
||||||
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
|
||||||
|
|
||||||
# 确保 config.yaml 存在(二进制模式默认 SQLite)
|
|
||||||
if [ ! -f "config.yaml" ]; then
|
|
||||||
info "生成默认 config.yaml (SQLite + 无Redis)..."
|
|
||||||
cat > config.yaml << 'YAML'
|
|
||||||
mode: "release"
|
|
||||||
port: "8080"
|
|
||||||
log_level: "info"
|
|
||||||
|
|
||||||
database:
|
|
||||||
driver: "sqlite"
|
|
||||||
host: "localhost"
|
|
||||||
port: 3306
|
|
||||||
user: ""
|
|
||||||
password: ""
|
|
||||||
dbname: "data/aiops.db"
|
|
||||||
charset: "utf8mb4"
|
|
||||||
max_conn: 100
|
|
||||||
max_idle: 10
|
|
||||||
|
|
||||||
redis:
|
|
||||||
host: "localhost"
|
|
||||||
port: 6379
|
|
||||||
password: ""
|
|
||||||
db: 0
|
|
||||||
|
|
||||||
jwt:
|
|
||||||
secret: "CHANGE_ME_JWT_SECRET" # ⚠️ 生产环境必须替换
|
|
||||||
expire_hour: 24
|
|
||||||
|
|
||||||
websocket:
|
|
||||||
read_buffer_size: 1024
|
|
||||||
write_buffer_size: 1024
|
|
||||||
YAML
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 静态编译(CGO_ENABLED=1 因为 SQLite 需要 CGO)
|
|
||||||
info "编译中..."
|
|
||||||
CGO_ENABLED=1 GOPROXY=https://goproxy.cn,direct GONOSUMCHECK='*' GOTOOLCHAIN=go1.24.0 go build -ldflags="-w -s" -o "$STANDALONE_BIN" .
|
|
||||||
|
|
||||||
if [ -f "$STANDALONE_BIN" ]; then
|
|
||||||
local bin_size=$(du -h "$STANDALONE_BIN" | cut -f1)
|
|
||||||
success "编译完成: $BACKEND_DIR/$STANDALONE_BIN ($bin_size)"
|
|
||||||
echo ""
|
|
||||||
info "使用方法:"
|
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN # 默认启动 (SQLite, 端口 8080)"
|
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --port 9090 # 自定义端口"
|
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --db-driver mysql \\"
|
|
||||||
echo " --db-host localhost --db-user root \\"
|
|
||||||
echo " --db-password pass --db-name aiops # MySQL 模式"
|
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --config config.yaml # 使用配置文件"
|
|
||||||
else
|
|
||||||
error "编译失败"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
agent)
|
agent)
|
||||||
banner "========== 编译 Agent =========="
|
banner "========== 编译 Agent =========="
|
||||||
@@ -531,19 +675,96 @@ YAML
|
|||||||
# 独立二进制模式
|
# 独立二进制模式
|
||||||
# ============================================================
|
# ============================================================
|
||||||
cmd_binary() {
|
cmd_binary() {
|
||||||
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
local action="run"
|
||||||
|
if [ $# -gt 0 ] && [[ "$1" =~ ^(upgrade|start|stop|status|help|--help|-h)$ ]]; then
|
||||||
# 先编译(如果不存在)
|
action=$1
|
||||||
if [ ! -f "$STANDALONE_BIN" ]; then
|
shift
|
||||||
info "二进制不存在,先编译..."
|
|
||||||
cmd_build binary
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
banner "========== 启动独立二进制 =========="
|
case "$action" in
|
||||||
mkdir -p data logs uploads
|
upgrade|update)
|
||||||
|
banner "========== 二进制升级部署 =========="
|
||||||
# 传递额外参数
|
local args=()
|
||||||
./"$STANDALONE_BIN" "$@"
|
if [ $# -gt 0 ]; then
|
||||||
|
args=("$@")
|
||||||
|
else
|
||||||
|
load_binary_args args
|
||||||
|
fi
|
||||||
|
local port
|
||||||
|
port=$(binary_listen_port "${args[@]}")
|
||||||
|
info "启动参数: ${args[*]}"
|
||||||
|
stop_binary_process "$port"
|
||||||
|
if [ -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" ]; then
|
||||||
|
local backup="$SCRIPT_DIR/$BACKEND_DIR/${STANDALONE_BIN}.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
|
cp "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" "$backup"
|
||||||
|
info "已备份旧二进制: $backup"
|
||||||
|
fi
|
||||||
|
build_standalone_binary || exit 1
|
||||||
|
start_binary_daemon "${args[@]}" || exit 1
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
local args=()
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
args=("$@")
|
||||||
|
else
|
||||||
|
load_binary_args args
|
||||||
|
fi
|
||||||
|
if [ ! -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" ]; then
|
||||||
|
info "二进制不存在,先编译..."
|
||||||
|
build_standalone_binary || exit 1
|
||||||
|
fi
|
||||||
|
start_binary_daemon "${args[@]}" || exit 1
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
local port=${1:-8080}
|
||||||
|
stop_binary_process "$port"
|
||||||
|
success "二进制已停止"
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
local args=()
|
||||||
|
load_binary_args args
|
||||||
|
local port
|
||||||
|
port=$(binary_listen_port "${args[@]}")
|
||||||
|
info "监听端口: $port"
|
||||||
|
if [ -f "$BINARY_PID_FILE" ]; then
|
||||||
|
local pid
|
||||||
|
pid=$(cat "$BINARY_PID_FILE" 2>/dev/null || echo "")
|
||||||
|
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||||
|
success "进程运行中 (PID: $pid)"
|
||||||
|
else
|
||||||
|
warning "PID 文件存在但进程未运行"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warning "未找到 PID 文件"
|
||||||
|
fi
|
||||||
|
if curl -sf "http://localhost:${port}/version" > /dev/null 2>&1; then
|
||||||
|
success "HTTP 就绪: http://localhost:${port}/version"
|
||||||
|
else
|
||||||
|
warning "HTTP 未响应"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
echo "独立二进制命令:"
|
||||||
|
echo " ./run.sh binary upgrade [--port 8080 --db-driver sqlite] 停服→清旧→重编译→启动"
|
||||||
|
echo " ./run.sh binary start [args...] 后台启动(参数会持久化)"
|
||||||
|
echo " ./run.sh binary stop [port] 停止进程"
|
||||||
|
echo " ./run.sh binary status 查看状态"
|
||||||
|
echo " ./run.sh binary [--port 8080 ...] 前台运行(开发调试)"
|
||||||
|
;;
|
||||||
|
run|*)
|
||||||
|
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
||||||
|
if [ ! -f "$STANDALONE_BIN" ]; then
|
||||||
|
info "二进制不存在,先编译..."
|
||||||
|
build_standalone_binary || exit 1
|
||||||
|
fi
|
||||||
|
banner "========== 前台启动独立二进制 =========="
|
||||||
|
mkdir -p data logs uploads
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
save_binary_args "$@"
|
||||||
|
fi
|
||||||
|
./"$STANDALONE_BIN" "$@"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -553,6 +774,7 @@ KUBERNETES_NAMESPACE="devops"
|
|||||||
KUBERNETES_MANIFEST_DIR="$SCRIPT_DIR/deploy/kubernetes"
|
KUBERNETES_MANIFEST_DIR="$SCRIPT_DIR/deploy/kubernetes"
|
||||||
KUBERNETES_IMAGE="aiops:embed"
|
KUBERNETES_IMAGE="aiops:embed"
|
||||||
KUBERNETES_NODE_PORT=30080
|
KUBERNETES_NODE_PORT=30080
|
||||||
|
KUBERNETES_BUILT_IMAGE=""
|
||||||
|
|
||||||
# 检测 kubectl 和 Docker
|
# 检测 kubectl 和 Docker
|
||||||
check_kubernetes_deps() {
|
check_kubernetes_deps() {
|
||||||
@@ -592,34 +814,52 @@ load_image_to_cluster() {
|
|||||||
warning "如集群无法拉取镜像,请手动推送: docker push <registry>/$image"
|
warning "如集群无法拉取镜像,请手动推送: docker push <registry>/$image"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Kubernetes 构建镜像
|
# Kubernetes 构建镜像(upgrade 时无缓存 + 时间戳 tag 避免旧层残留)// Logan.Li
|
||||||
cmd_kubernetes_build() {
|
cmd_kubernetes_build() {
|
||||||
|
local no_cache=${1:-0}
|
||||||
if ! command -v docker &>/dev/null; then
|
if ! command -v docker &>/dev/null; then
|
||||||
error "Docker 未安装"
|
error "Docker 未安装"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
banner "========== 构建 embed 镜像 =========="
|
banner "========== 构建 embed 镜像 =========="
|
||||||
info "镜像: $KUBERNETES_IMAGE"
|
clean_frontend_artifacts
|
||||||
|
clean_embed_static
|
||||||
|
|
||||||
# 版本信息
|
local version
|
||||||
local version=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")
|
version=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")
|
||||||
local build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
local build_time
|
||||||
|
build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||||
|
local image_tag
|
||||||
|
image_tag="${KUBERNETES_IMAGE}-$(date +%Y%m%d%H%M%S)"
|
||||||
|
info "镜像: $KUBERNETES_IMAGE (+ $image_tag)"
|
||||||
|
|
||||||
|
local build_args=(
|
||||||
|
-f Dockerfile.embed
|
||||||
|
--build-arg VERSION="$version"
|
||||||
|
--build-arg BUILD_TIME="$build_time"
|
||||||
|
-t "$KUBERNETES_IMAGE"
|
||||||
|
-t "$image_tag"
|
||||||
|
)
|
||||||
|
if [ "$no_cache" -eq 1 ]; then
|
||||||
|
build_args+=(--no-cache --pull)
|
||||||
|
info "无缓存全量构建 (--no-cache --pull)"
|
||||||
|
fi
|
||||||
|
build_args+=(.)
|
||||||
|
|
||||||
info "构建中(前端 + 后端 embed)..."
|
info "构建中(前端 + 后端 embed)..."
|
||||||
docker build -f Dockerfile.embed \
|
docker build "${build_args[@]}" || {
|
||||||
--build-arg VERSION="$version" \
|
|
||||||
--build-arg BUILD_TIME="$build_time" \
|
|
||||||
-t "$KUBERNETES_IMAGE" . || {
|
|
||||||
error "镜像构建失败"
|
error "镜像构建失败"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
local img_size=$(docker images "$KUBERNETES_IMAGE" --format "{{.Size}}" | head -1)
|
local img_size
|
||||||
|
img_size=$(docker images "$KUBERNETES_IMAGE" --format "{{.Size}}" | head -1)
|
||||||
success "镜像构建完成: $KUBERNETES_IMAGE ($img_size)"
|
success "镜像构建完成: $KUBERNETES_IMAGE ($img_size)"
|
||||||
|
|
||||||
# 加载到本地集群
|
|
||||||
load_image_to_cluster "$KUBERNETES_IMAGE"
|
load_image_to_cluster "$KUBERNETES_IMAGE"
|
||||||
|
load_image_to_cluster "$image_tag"
|
||||||
|
KUBERNETES_BUILT_IMAGE="$image_tag"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Kubernetes 部署
|
# Kubernetes 部署
|
||||||
@@ -678,10 +918,12 @@ cmd_kubernetes() {
|
|||||||
upgrade|update)
|
upgrade|update)
|
||||||
check_kubernetes_deps
|
check_kubernetes_deps
|
||||||
banner "========== Kubernetes 滚动更新 =========="
|
banner "========== Kubernetes 滚动更新 =========="
|
||||||
cmd_kubernetes_build
|
cmd_kubernetes_build 1
|
||||||
info "触发滚动更新..."
|
info "更新 Deployment 镜像: $KUBERNETES_BUILT_IMAGE"
|
||||||
kubectl -n "$KUBERNETES_NAMESPACE" rollout restart deployment/aiops
|
kubectl -n "$KUBERNETES_NAMESPACE" set image deployment/aiops aiops="$KUBERNETES_BUILT_IMAGE" --record
|
||||||
kubectl -n "$KUBERNETES_NAMESPACE" rollout status deployment/aiops --timeout=180s
|
kubectl -n "$KUBERNETES_NAMESPACE" rollout status deployment/aiops --timeout=180s
|
||||||
|
info "清理悬空镜像..."
|
||||||
|
docker image prune -f 2>/dev/null || true
|
||||||
success "滚动更新完成"
|
success "滚动更新完成"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -812,7 +1054,9 @@ cmd_help() {
|
|||||||
echo " ./run.sh logs 查看实时日志"
|
echo " ./run.sh logs 查看实时日志"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}Docker 容器部署${NC}"
|
echo -e " ${CYAN}Docker 容器部署${NC}"
|
||||||
echo " ./run.sh docker up 构建并启动所有容器 (MySQL+Redis+Nginx)"
|
echo " ./run.sh docker up 增量构建 + 强制重建容器"
|
||||||
|
echo " ./run.sh docker up fresh 无缓存全量重建并启动"
|
||||||
|
echo " ./run.sh docker upgrade 同 up(升级别名)"
|
||||||
echo " ./run.sh docker down 停止并移除容器"
|
echo " ./run.sh docker down 停止并移除容器"
|
||||||
echo " ./run.sh docker build 仅构建镜像"
|
echo " ./run.sh docker build 仅构建镜像"
|
||||||
echo " ./run.sh docker restart 重启容器"
|
echo " ./run.sh docker restart 重启容器"
|
||||||
@@ -834,7 +1078,9 @@ cmd_help() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e " ${YELLOW}编译 & 独立二进制${NC}"
|
echo -e " ${YELLOW}编译 & 独立二进制${NC}"
|
||||||
echo " ./run.sh build 编译独立二进制 ./$BACKEND_DIR/$STANDALONE_BIN"
|
echo " ./run.sh build 编译独立二进制 ./$BACKEND_DIR/$STANDALONE_BIN"
|
||||||
echo " ./run.sh binary 编译+启动独立二进制"
|
echo " ./run.sh binary upgrade 停服→清旧→重编译→后台启动(生产升级)"
|
||||||
|
echo " ./run.sh binary start|stop|status 进程管理"
|
||||||
|
echo " ./run.sh binary [args] 前台启动(调试)"
|
||||||
echo " ./run.sh build agent 交叉编译 Agent (amd64+arm64)"
|
echo " ./run.sh build agent 交叉编译 Agent (amd64+arm64)"
|
||||||
echo " ./run.sh build frontend 构建前端生产包"
|
echo " ./run.sh build frontend 构建前端生产包"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in new issue
Block a user