Files
aiops/backend/cmd/embed.go
T
Logan 4631b02f13 feat: 三种部署方式 — embed单容器 + 二进制部署 + K8s
新增三种部署方式(原有 docker-compose 不受影响):

1. Docker embed 单容器 (docker-compose.embed.yml):
   - go:embed 前端嵌入后端二进制
   - 不需要 nginx 容器,3容器→2容器
   - 镜像仅 54.8MB (vs 原来 1.16GB)
   - 用法: make docker-embed

2. 二进制部署 (deploy/binary/):
   - make build-binary 生成单二进制
   - install.sh 自动创建 systemd 服务
   - 适合无 Docker 环境
   - 用法: scp aiops + install.sh, bash install.sh install

3. Kubernetes 部署 (deploy/k8s/):
   - devops 命名空间全套 yaml
   - MySQL StatefulSet + Redis StatefulSet + Backend Deployment
   - ConfigMap + Secret + Ingress
   - 用法: kubectl apply -f deploy/k8s/

核心改动:
- backend/cmd/embed.go: go:embed 前端 + SPA fallback 路由
- backend/Dockerfile.embed: 四阶段构建 (前端→后端embed→agent→runtime)
- Makefile: build-binary + docker-embed 目标
- deploy/README.md: 四种部署方式对比文档
2026-06-21 14:20:29 +08:00

69 lines
1.7 KiB
Go
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.
package main
import (
"embed"
"io/fs"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
//go:embed all:static
var frontendFS embed.FS
// setupFrontend 配置前端静态文件服务(go:embed 模式) // Logan.Li
// 前端构建产物放在 backend/cmd/static/ 目录,编译时嵌入二进制
func setupFrontend(r *gin.Engine) {
// 获取 static 子目录的 fs.FS
staticFS, err := fs.Sub(frontendFS, "static")
if err != nil {
// static 目录不存在(开发模式或未构建前端),跳过
return
}
httpFS := http.FS(staticFS)
// 静态资源文件(js/css/图片等)直接返回
r.Use(func(c *gin.Context) {
// API 和 WebSocket 路径不走静态文件
path := c.Request.URL.Path
if strings.HasPrefix(path, "/api/") ||
strings.HasPrefix(path, "/ws/") ||
strings.HasPrefix(path, "/agent/") ||
strings.HasPrefix(path, "/health") ||
strings.HasPrefix(path, "/version") {
c.Next()
return
}
c.Next()
})
// NoRoute: SPA fallback — 非 API 请求尝试静态文件,找不到则返回 index.html
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
// API/WS 路径返回 JSON 404
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/ws/") || strings.HasPrefix(path, "/agent/") {
c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "接口不存在"})
return
}
// 尝试打开静态文件
cleanPath := strings.TrimPrefix(path, "/")
if cleanPath == "" {
cleanPath = "index.html"
}
f, err := staticFS.Open(cleanPath)
if err == nil {
f.Close()
c.Header("Cache-Control", "public, max-age=31536000")
c.FileFromFS(path, httpFS)
return
}
// SPA fallback → index.html(React Router history 模式)
c.FileFromFS("/index.html", httpFS)
})
}