fix(build): 独立二进制输出到根目录并修复 local 模式 SQLite 路径
- SQLite/redka 数据目录相对可执行文件解析,自动创建 data/ 父目录 - ./run.sh build 产物改为 ./aiops(与 run.sh 同级),数据/日志在 ./data ./logs - 解决从项目根执行时 data/ 不存在导致的 SQLite CANTOPEN 报错
This commit is contained in:
1 parent
fd7b04b9d6
commit
e7a479ccf5
5 files changed
+88
-31
No files matched your search
@@ -18,8 +18,10 @@ backend/vendor/
|
|||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Compiled binary
|
# Compiled binary
|
||||||
|
aiops
|
||||||
backend/aiops-server
|
backend/aiops-server
|
||||||
backend/aiops
|
backend/aiops
|
||||||
|
data/
|
||||||
|
|
||||||
# Config (keep backend/config.yaml as default)
|
# Config (keep backend/config.yaml as default)
|
||||||
config.local.yaml
|
config.local.yaml
|
||||||
|
|||||||
+9
-1
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
"ezops/router"
|
"ezops/router"
|
||||||
"ezops/config"
|
"ezops/config"
|
||||||
"ezops/seed"
|
"ezops/seed"
|
||||||
|
"ezops/pkg/appdir"
|
||||||
"ezops/pkg/database"
|
"ezops/pkg/database"
|
||||||
"ezops/pkg/logger"
|
"ezops/pkg/logger"
|
||||||
ezredis "ezops/pkg/redis"
|
ezredis "ezops/pkg/redis"
|
||||||
@@ -136,7 +138,7 @@ func main() {
|
|||||||
Port: cfg.Redis.Port,
|
Port: cfg.Redis.Port,
|
||||||
Password: cfg.Redis.Password,
|
Password: cfg.Redis.Password,
|
||||||
DB: cfg.Redis.DB,
|
DB: cfg.Redis.DB,
|
||||||
DataDir: "data/redka",
|
DataDir: appdir.Resolve("data/redka"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cfg.Mode == "local" {
|
if cfg.Mode == "local" {
|
||||||
@@ -237,6 +239,7 @@ func loadConfig() *config.Config {
|
|||||||
viper.SetConfigType("yaml")
|
viper.SetConfigType("yaml")
|
||||||
viper.AddConfigPath("./configs")
|
viper.AddConfigPath("./configs")
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath(appdir.Root())
|
||||||
|
|
||||||
// 如果指定了配置文件路径,使用它 // Logan.Li
|
// 如果指定了配置文件路径,使用它 // Logan.Li
|
||||||
if *flagConfig != "" {
|
if *flagConfig != "" {
|
||||||
@@ -295,6 +298,11 @@ func loadConfig() *config.Config {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQLite / redka 相对路径统一解析到可执行文件目录 // Logan.Li
|
||||||
|
if cfg.Database.Driver == "sqlite" && cfg.Database.DBName != "" && !filepath.IsAbs(cfg.Database.DBName) {
|
||||||
|
cfg.Database.DBName = appdir.Resolve(cfg.Database.DBName)
|
||||||
|
}
|
||||||
|
|
||||||
return &cfg
|
return &cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// appdir — 独立二进制运行目录(数据/配置相对可执行文件,不依赖 cwd)// Logan.Li
|
||||||
|
package appdir
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Root 可执行文件所在目录(解析软链)// Logan.Li
|
||||||
|
func Root() string {
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
if resolved, err := filepath.EvalSymlinks(exe); err == nil {
|
||||||
|
exe = resolved
|
||||||
|
}
|
||||||
|
return filepath.Dir(exe)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve 相对路径 → 基于可执行文件目录的绝对路径 // Logan.Li
|
||||||
|
func Resolve(path string) string {
|
||||||
|
if path == "" || filepath.IsAbs(path) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return filepath.Join(Root(), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureParentDir 确保文件父目录存在 // Logan.Li
|
||||||
|
func EnsureParentDir(filePath string) error {
|
||||||
|
dir := filepath.Dir(filePath)
|
||||||
|
if dir == "" || dir == "." {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.MkdirAll(dir, 0o755)
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"ezops/config"
|
"ezops/config"
|
||||||
"ezops/model"
|
"ezops/model"
|
||||||
|
"ezops/pkg/appdir"
|
||||||
|
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
@@ -29,7 +30,10 @@ func New(cfg config.DatabaseConfig) (*gorm.DB, error) {
|
|||||||
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName)
|
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName)
|
||||||
dialector = postgres.Open(dsn)
|
dialector = postgres.Open(dsn)
|
||||||
case "sqlite":
|
case "sqlite":
|
||||||
dsn := cfg.DBName
|
dsn := appdir.Resolve(cfg.DBName)
|
||||||
|
if err := appdir.EnsureParentDir(dsn); err != nil {
|
||||||
|
return nil, fmt.Errorf("创建数据库目录失败: %w", err)
|
||||||
|
}
|
||||||
if !strings.Contains(dsn, "?") {
|
if !strings.Contains(dsn, "?") {
|
||||||
// WAL 提升并发读,busy_timeout 降低锁冲突 // Logan.Li
|
// WAL 提升并发读,busy_timeout 降低锁冲突 // Logan.Li
|
||||||
dsn += "?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)"
|
dsn += "?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)"
|
||||||
|
|||||||
@@ -61,8 +61,11 @@ 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"
|
STANDALONE_BIN_PATH="$SCRIPT_DIR/$STANDALONE_BIN"
|
||||||
BINARY_ARGS_FILE="$SCRIPT_DIR/$BACKEND_DIR/.aiops.args"
|
BINARY_PID_FILE="$SCRIPT_DIR/.aiops.pid"
|
||||||
|
BINARY_ARGS_FILE="$SCRIPT_DIR/.aiops.args"
|
||||||
|
DATA_DIR="$SCRIPT_DIR/data"
|
||||||
|
LOGS_DIR="$SCRIPT_DIR/logs"
|
||||||
|
|
||||||
# 检测操作系统
|
# 检测操作系统
|
||||||
detect_os() {
|
detect_os() {
|
||||||
@@ -311,6 +314,7 @@ clean_embed_static() {
|
|||||||
|
|
||||||
clean_go_artifacts() {
|
clean_go_artifacts() {
|
||||||
info "清理 Go 旧编译产物..."
|
info "清理 Go 旧编译产物..."
|
||||||
|
rm -f "$STANDALONE_BIN_PATH"
|
||||||
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN"
|
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN"
|
||||||
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$BACKEND_BIN"
|
rm -f "$SCRIPT_DIR/$BACKEND_DIR/$BACKEND_BIN"
|
||||||
}
|
}
|
||||||
@@ -352,6 +356,7 @@ stop_binary_process() {
|
|||||||
[ -n "$pids" ] && echo "$pids" | xargs kill -9 2>/dev/null || true
|
[ -n "$pids" ] && echo "$pids" | xargs kill -9 2>/dev/null || true
|
||||||
else
|
else
|
||||||
fuser -k "${port}/tcp" 2>/dev/null || true
|
fuser -k "${port}/tcp" 2>/dev/null || true
|
||||||
|
pkill -f "${STANDALONE_BIN_PATH}" 2>/dev/null || true
|
||||||
pkill -f "${SCRIPT_DIR}/${BACKEND_DIR}/${STANDALONE_BIN}" 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
|
pkill -f "./${STANDALONE_BIN} --port" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
@@ -364,8 +369,8 @@ wait_binary_ready() {
|
|||||||
while ! curl -sf "http://localhost:${port}/version" > /dev/null 2>&1; do
|
while ! curl -sf "http://localhost:${port}/version" > /dev/null 2>&1; do
|
||||||
retries=$((retries + 1))
|
retries=$((retries + 1))
|
||||||
if [ $retries -ge 30 ]; then
|
if [ $retries -ge 30 ]; then
|
||||||
error "二进制启动超时,查看日志: $BACKEND_DIR/logs/aiops.log"
|
error "二进制启动超时,查看日志: $LOGS_DIR/aiops.log"
|
||||||
tail -20 "$SCRIPT_DIR/$BACKEND_DIR/logs/aiops.log" 2>/dev/null || true
|
tail -20 "$LOGS_DIR/aiops.log" 2>/dev/null || true
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -375,7 +380,7 @@ wait_binary_ready() {
|
|||||||
|
|
||||||
# 确保二进制默认 config.yaml 存在 // Logan.Li
|
# 确保二进制默认 config.yaml 存在 // Logan.Li
|
||||||
ensure_binary_config() {
|
ensure_binary_config() {
|
||||||
local cfg="$SCRIPT_DIR/$BACKEND_DIR/config.yaml"
|
local cfg="$SCRIPT_DIR/config.yaml"
|
||||||
if [ -f "$cfg" ]; then
|
if [ -f "$cfg" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -434,14 +439,14 @@ build_standalone_binary() {
|
|||||||
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
||||||
info "编译中..."
|
info "编译中..."
|
||||||
CGO_ENABLED=1 GOPROXY=https://goproxy.cn,direct GONOSUMCHECK='*' GOTOOLCHAIN=go1.24.0 \
|
CGO_ENABLED=1 GOPROXY=https://goproxy.cn,direct GONOSUMCHECK='*' GOTOOLCHAIN=go1.24.0 \
|
||||||
go build -ldflags="-w -s" -o "$STANDALONE_BIN" .
|
go build -ldflags="-w -s" -o "$STANDALONE_BIN_PATH" .
|
||||||
if [ ! -f "$STANDALONE_BIN" ]; then
|
if [ ! -f "$STANDALONE_BIN_PATH" ]; then
|
||||||
error "编译失败"
|
error "编译失败"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local bin_size
|
local bin_size
|
||||||
bin_size=$(du -h "$STANDALONE_BIN" | cut -f1)
|
bin_size=$(du -h "$STANDALONE_BIN_PATH" | cut -f1)
|
||||||
success "编译完成: $BACKEND_DIR/$STANDALONE_BIN ($bin_size)"
|
success "编译完成: ./$STANDALONE_BIN ($bin_size)"
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -528,16 +533,17 @@ start_binary_daemon() {
|
|||||||
local args=("$@")
|
local args=("$@")
|
||||||
local port
|
local port
|
||||||
port=$(binary_listen_port "${args[@]}")
|
port=$(binary_listen_port "${args[@]}")
|
||||||
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
mkdir -p data logs uploads
|
mkdir -p "$DATA_DIR" "$LOGS_DIR" uploads
|
||||||
rotate_log "logs/aiops.log"
|
rotate_log "$LOGS_DIR/aiops.log"
|
||||||
stop_binary_process "$port"
|
stop_binary_process "$port"
|
||||||
nohup ./"$STANDALONE_BIN" "${args[@]}" >> logs/aiops.log 2>&1 &
|
nohup ./"$STANDALONE_BIN" "${args[@]}" >> "$LOGS_DIR/aiops.log" 2>&1 &
|
||||||
echo $! > "$BINARY_PID_FILE"
|
echo $! > "$BINARY_PID_FILE"
|
||||||
save_binary_args "${args[@]}"
|
save_binary_args "${args[@]}"
|
||||||
if wait_binary_ready "$port"; then
|
if wait_binary_ready "$port"; then
|
||||||
success "二进制已启动 (PID: $(cat "$BINARY_PID_FILE"), 端口: $port)"
|
success "二进制已启动 (PID: $(cat "$BINARY_PID_FILE"), 端口: $port)"
|
||||||
info "日志: $BACKEND_DIR/logs/aiops.log"
|
info "日志: $LOGS_DIR/aiops.log"
|
||||||
|
info "数据: $DATA_DIR/"
|
||||||
verify_binary_version "$port" || true
|
verify_binary_version "$port" || true
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -691,9 +697,9 @@ cmd_build() {
|
|||||||
echo ""
|
echo ""
|
||||||
info "使用方法:"
|
info "使用方法:"
|
||||||
echo " ./run.sh binary upgrade # 停服重编译并后台启动"
|
echo " ./run.sh binary upgrade # 停服重编译并后台启动"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --port 8080 # 前台启动"
|
echo " ./$STANDALONE_BIN --port 8080 # 前台启动"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --mode local # 本地模式 (SQLite + redka)"
|
echo " ./$STANDALONE_BIN --mode local # 本地模式 (SQLite + redka)"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --config config.yaml # 使用配置文件"
|
echo " ./$STANDALONE_BIN --config config.yaml # 使用配置文件"
|
||||||
;;
|
;;
|
||||||
agent)
|
agent)
|
||||||
banner "========== 编译 Agent =========="
|
banner "========== 编译 Agent =========="
|
||||||
@@ -756,9 +762,9 @@ cmd_binary() {
|
|||||||
port=$(binary_listen_port "${args[@]}")
|
port=$(binary_listen_port "${args[@]}")
|
||||||
info "启动参数: ${args[*]}"
|
info "启动参数: ${args[*]}"
|
||||||
stop_binary_process "$port"
|
stop_binary_process "$port"
|
||||||
if [ -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" ]; then
|
if [ -f "$STANDALONE_BIN_PATH" ]; then
|
||||||
local backup="$SCRIPT_DIR/$BACKEND_DIR/${STANDALONE_BIN}.bak.$(date +%Y%m%d%H%M%S)"
|
local backup="$SCRIPT_DIR/${STANDALONE_BIN}.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
cp "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" "$backup"
|
cp "$STANDALONE_BIN_PATH" "$backup"
|
||||||
info "已备份旧二进制: $backup"
|
info "已备份旧二进制: $backup"
|
||||||
fi
|
fi
|
||||||
build_standalone_binary || exit 1
|
build_standalone_binary || exit 1
|
||||||
@@ -771,7 +777,7 @@ cmd_binary() {
|
|||||||
else
|
else
|
||||||
load_binary_args args
|
load_binary_args args
|
||||||
fi
|
fi
|
||||||
if [ ! -f "$SCRIPT_DIR/$BACKEND_DIR/$STANDALONE_BIN" ]; then
|
if [ ! -f "$STANDALONE_BIN_PATH" ]; then
|
||||||
info "二进制不存在,先编译..."
|
info "二进制不存在,先编译..."
|
||||||
build_standalone_binary || exit 1
|
build_standalone_binary || exit 1
|
||||||
fi
|
fi
|
||||||
@@ -814,16 +820,16 @@ cmd_binary() {
|
|||||||
echo " ./run.sh binary [--mode local --port 8080 ...] 前台运行 (SQLite + redka)"
|
echo " ./run.sh binary [--mode local --port 8080 ...] 前台运行 (SQLite + redka)"
|
||||||
;;
|
;;
|
||||||
run|*)
|
run|*)
|
||||||
cd "$SCRIPT_DIR/$BACKEND_DIR"
|
if [ ! -f "$STANDALONE_BIN_PATH" ]; then
|
||||||
if [ ! -f "$STANDALONE_BIN" ]; then
|
|
||||||
info "二进制不存在,先编译..."
|
info "二进制不存在,先编译..."
|
||||||
build_standalone_binary || exit 1
|
build_standalone_binary || exit 1
|
||||||
fi
|
fi
|
||||||
banner "========== 前台启动独立二进制 =========="
|
banner "========== 前台启动独立二进制 =========="
|
||||||
mkdir -p data logs uploads
|
mkdir -p "$DATA_DIR" "$LOGS_DIR" uploads
|
||||||
if [ $# -gt 0 ]; then
|
if [ $# -gt 0 ]; then
|
||||||
save_binary_args "$@"
|
save_binary_args "$@"
|
||||||
fi
|
fi
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
./"$STANDALONE_BIN" "$@"
|
./"$STANDALONE_BIN" "$@"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1200,7 +1206,7 @@ cmd_help() {
|
|||||||
echo " ./run.sh kubernetes build 仅构建 embed 镜像"
|
echo " ./run.sh kubernetes build 仅构建 embed 镜像"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${YELLOW}编译 & 独立二进制${NC}"
|
echo -e " ${YELLOW}编译 & 独立二进制${NC}"
|
||||||
echo " ./run.sh build 编译独立二进制 ./$BACKEND_DIR/$STANDALONE_BIN"
|
echo " ./run.sh build 编译独立二进制 ./$STANDALONE_BIN"
|
||||||
echo " ./run.sh binary upgrade 停服→清旧→重编译→后台启动(生产升级)"
|
echo " ./run.sh binary upgrade 停服→清旧→重编译→后台启动(生产升级)"
|
||||||
echo " ./run.sh binary start|stop|status 进程管理"
|
echo " ./run.sh binary start|stop|status 进程管理"
|
||||||
echo " ./run.sh binary [args] 前台启动(调试)"
|
echo " ./run.sh binary [args] 前台启动(调试)"
|
||||||
@@ -1208,10 +1214,11 @@ cmd_help() {
|
|||||||
echo " ./run.sh build frontend 构建前端生产包"
|
echo " ./run.sh build frontend 构建前端生产包"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BLUE}独立二进制用法${NC}"
|
echo -e " ${BLUE}独立二进制用法${NC}"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN 默认启动 (SQLite, 端口 8080)"
|
echo " ./$STANDALONE_BIN 默认启动 (SQLite, 端口 8080)"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --port 9090 自定义端口"
|
echo " ./$STANDALONE_BIN --port 9090 自定义端口"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --config config.yaml 使用配置文件"
|
echo " ./$STANDALONE_BIN --mode local 本地模式 (SQLite + redka)"
|
||||||
echo " ./$BACKEND_DIR/$STANDALONE_BIN --db-driver mysql \\"
|
echo " ./$STANDALONE_BIN --config config.yaml 使用配置文件"
|
||||||
|
echo " ./$STANDALONE_BIN --db-driver mysql \\"
|
||||||
echo " --db-host 127.0.0.1 --db-user root \\"
|
echo " --db-host 127.0.0.1 --db-user root \\"
|
||||||
echo " --db-password pass --db-name aiops MySQL 模式"
|
echo " --db-password pass --db-name aiops MySQL 模式"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in new issue
Block a user