feat(ops): U4 后端镜像瘦身 282MB→107MB (62%降幅)

- 移除 git/docker-cli/docker-cli-buildx (~100MB) 改为懒加载
- PipelineHandler 注入 ToolInstaller, runCheckout/runImageBuild 调 EnsureAvailable
- UpgradeTools 自动升级 DB 中 git/docker-cli preinstall→lazy
- 仅内置 linux-amd64 agent (~12MB节省), arm64/darwin 通过下载API
- kubectl 改用 install-kubectl.sh 下载 stripped 版本
- .dockerignore 排除 backend/kubectl 60MB 二进制
- Dockerfile.embed + backend/Dockerfile 同步更新
- 17/17 集成测试通过
This commit is contained in:
attacker committed 2026-06-27 14:28:09 +08:00
1 parent 8dd5a07b70
commit c624ca5b4a
11 files changed
+175 -47

No files matched your search

+3 -5
View File
@@ -81,10 +81,10 @@ FROM alpine:3.20
ARG TARGETARCH
ARG KUBECTL_VERSION=v1.32.2
# 安装运行时依赖(核心最小集,重型工具改为懒加载)// Logan.Li
# 安装运行时依赖(U4 镜像瘦身 — 核心最小集, git/docker-cli 改为懒加载)// Logan.Li
COPY scripts/setup-alpine-mirrors.sh scripts/install-kubectl.sh /tmp/
RUN chmod +x /tmp/setup-alpine-mirrors.sh && /tmp/setup-alpine-mirrors.sh && \
apk add --no-cache ca-certificates tzdata bash curl git docker-cli docker-cli-buildx && \
apk add --no-cache ca-certificates tzdata bash curl && \
chmod +x /tmp/install-kubectl.sh && \
TARGET_OS=linux TARGET_ARCH="${TARGETARCH}" KUBECTL_VERSION="${KUBECTL_VERSION}" \
/tmp/install-kubectl.sh /usr/local/bin/kubectl && \
@@ -106,10 +106,8 @@ COPY --from=builder /app/aiops .
# 创建运行时目录(agent 二进制 + 安装脚本)
RUN mkdir -p /app/agent/bin /app/agent/deploy
# 复制 agent 二进制(三个平台)
# 复制 agent 二进制(U4: 仅 linux-amd64 内置, arm64/darwin 通过下载 API 提供)// Logan.Li
COPY --from=agent-builder /agent/bin/aiops-agent-linux-amd64 /app/agent/bin/
COPY --from=agent-builder /agent/bin/aiops-agent-linux-arm64 /app/agent/bin/
COPY --from=agent-builder /agent/bin/aiops-agent-darwin-arm64 /app/agent/bin/
# 复制 agent 安装脚本和 systemd service 模板
COPY agent/deploy/install.sh /app/agent/deploy/
+6 -4
View File
@@ -9,6 +9,7 @@ import (
"ezops/model"
"ezops/middleware"
ezredis "ezops/pkg/redis"
"ezops/pkg/toolinstaller"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -53,13 +54,14 @@ func validateCode(db *gorm.DB, code string, excludeID uint) string {
// PipelineHandler 流水线管理器
type PipelineHandler struct {
db *gorm.DB
cache ezredis.Cache
db *gorm.DB
cache ezredis.Cache
toolInstaller *toolinstaller.ToolInstaller
}
// NewPipelineHandler 创建流水线处理器
func NewPipelineHandler(db *gorm.DB, cache ezredis.Cache) *PipelineHandler {
return &PipelineHandler{db: db, cache: cache}
func NewPipelineHandler(db *gorm.DB, cache ezredis.Cache, ti *toolinstaller.ToolInstaller) *PipelineHandler {
return &PipelineHandler{db: db, cache: cache, toolInstaller: ti}
}
// PipelineListItem 列表项(含最近一次运行摘要)// Logan.Li
+15
View File
@@ -222,6 +222,13 @@ func (h *PipelineHandler) runCheckout(url, branch, workspace string, logFn func(
}
logFn("info", "代码检出", "> Git 拉取 "+url+" (分支: "+branch+")")
// U4: 确保工具可用(slim 镜像模式下 git 为懒加载)// Logan.Li
if h.toolInstaller != nil {
if err := h.toolInstaller.EnsureAvailable("git"); err != nil {
return fmt.Errorf("git 不可用(安装失败): %v", err)
}
}
cmd := exec.Command("git", "clone", "--depth", "1", "-b", branch, url, workspace)
output, err := cmd.CombinedOutput()
// 流式输出到日志
@@ -429,6 +436,14 @@ func (h *PipelineHandler) runDockerBuild(workspace string, pipeline model.Pipeli
}
logFn("info", "镜像打包", fmt.Sprintf(">>> Docker 构建 (dockerfile: %s)", dfPath))
// U4: 确保 Docker CLI 可用(slim 镜像模式下 docker-cli 为懒加载)// Logan.Li
if h.toolInstaller != nil {
if err := h.toolInstaller.EnsureAvailable("docker-cli"); err != nil {
return fmt.Errorf("docker 不可用(安装失败): %v", err)
}
}
cmd := exec.Command("docker", "build", "-t", fullImage, "-f", dfPath, ".")
cmd.Dir = buildDir
out, err := cmd.CombinedOutput()
+22 -20
View File
@@ -191,29 +191,31 @@ echo "[Node.js] Installed: $(node --version) / npm $(npm --version)"
`,
SortOrder: 4,
},
// ──── Git — 版本控制(预装)// Logan.Li ────
// ──── Git — 版本控制(懒加载, U4 镜像瘦身)// Logan.Li ────
{
Name: "git",
DisplayName: "Git",
Description: "版本控制工具,流水线代码拉取使用",
Category: "build",
InstallMethod: ToolPreinstall,
BinaryName: "git",
InstallPath: "/usr/bin/git",
CheckCommand: "git --version 2>/dev/null",
SortOrder: 5,
Name: "git",
DisplayName: "Git",
Description: "版本控制工具,流水线代码拉取使用(首次使用自动安装)",
Category: "build",
InstallMethod: ToolLazy,
BinaryName: "git",
InstallPath: "/usr/bin/git",
CheckCommand: "git --version 2>/dev/null",
InstallScript: `apk add --no-cache git`,
SortOrder: 5,
},
// ──── Docker CLI — 容器构建(预装)// Logan.Li ────
// ──── Docker CLI — 容器构建(懒加载, U4 镜像瘦身)// Logan.Li ────
{
Name: "docker-cli",
DisplayName: "Docker CLI",
Description: "Docker 命令行工具,流水线镜像构建/推送使用",
Category: "deploy",
InstallMethod: ToolPreinstall,
BinaryName: "docker",
InstallPath: "/usr/bin/docker",
CheckCommand: "docker --version 2>/dev/null",
SortOrder: 6,
Name: "docker-cli",
DisplayName: "Docker CLI",
Description: "Docker 命令行工具,流水线镜像构建/推送使用(首次使用自动安装)",
Category: "deploy",
InstallMethod: ToolLazy,
BinaryName: "docker",
InstallPath: "/usr/bin/docker",
CheckCommand: "docker --version 2>/dev/null",
InstallScript: `apk add --no-cache docker-cli`,
SortOrder: 6,
},
}
}
+42
View File
@@ -327,6 +327,8 @@ func (ti *ToolInstaller) SeedTools() {
var count int64
ti.db.Model(&model.DeployTool{}).Count(&count)
if count > 0 {
// U4: 即使已有数据也升级工具配置(git/docker-cli 从 preinstall→lazy)
ti.UpgradeTools()
return
}
@@ -351,4 +353,44 @@ func (ti *ToolInstaller) SeedTools() {
} else {
log.Printf("[ToolInstaller] Seeded %d tools", len(tools))
}
// U4: 新种子也执行升级
ti.UpgradeTools()
}
// UpgradeTools updates existing tool configs to match latest predefined definitions.
// U4: git/docker-cli 从 preinstall 迁移到 lazy(镜像瘦身)。
func (ti *ToolInstaller) UpgradeTools() {
upgrades := map[string]struct {
installMethod model.ToolInstallMethod
installScript string
description string
}{
"git": {
installMethod: model.ToolLazy,
installScript: "apk add --no-cache git",
description: "版本控制工具,流水线代码拉取使用(首次使用自动安装)",
},
"docker-cli": {
installMethod: model.ToolLazy,
installScript: "apk add --no-cache docker-cli",
description: "Docker 命令行工具,流水线镜像构建/推送使用(首次使用自动安装)",
},
}
for name, up := range upgrades {
var tool model.DeployTool
if err := ti.db.Where("name = ?", name).First(&tool).Error; err != nil {
continue // tool not in DB, skip
}
if tool.InstallMethod == up.installMethod && tool.InstallScript == up.installScript {
continue // already up to date
}
log.Printf("[ToolInstaller] U4 upgrade: %s %s→%s", name, tool.InstallMethod, up.installMethod)
ti.db.Model(&model.DeployTool{}).Where("name = ?", name).Updates(map[string]interface{}{
"install_method": up.installMethod,
"install_script": up.installScript,
"description": up.description,
})
}
}
+1 -1
View File
@@ -150,7 +150,7 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
api.POST("/auth/register", authHandler.Register)
// Webhook 触发路由(无需 JWT,由 Webhook Token 签名验证)
pipelineHandler := controller.NewPipelineHandler(db, redisCache)
pipelineHandler := controller.NewPipelineHandler(db, redisCache, ti)
webhookGroup := api.Group("/webhook")
{
webhookGroup.POST("/job/:jobName", pipelineHandler.WebhookTrigger)