refactor: 前后端目录结构模块化 + v2 优化移植与缺陷修复
前端: - src/srv/api → src/api,按域分文件夹(auth/cluster/host/storage/deployment/collector) - 新增 http.ts 引擎(http.get/post/put/delete),域模块改用语义化调用 - index.html 增加 favicon(匹配应用 logo 的 emerald + Activity 脉冲) 后端分层增强(保留包名): - 新增 repository/:从 models 拆出 JsonDB 持久化层(InitDB/GetDB/Shutdown/RequestSave) - 新增 dto/:从 controllers 抽出各域请求契约(带 binding 校验) - 新增 pkg/crypto、pkg/jwt:从 utils 拆出安全原语(外部 jwt 别名 jwtv5 避免冲突) - models 只留领域实体 + sanitizer;utils 只留 gin/应用相关(response/errors) v2 优化移植与缺陷修复: - 移植密钥分离/SSH TOFU/并发写安全/Loki 生产级配置/Docker 加固 - 修复 deployment.go 并发丢更新、Helm values 重复键、EncryptField 哨兵、loki-gateway 端点等审计缺陷 - 实现真正的优雅关停(main.go http.Server + signal + repository.Shutdown) - 修复登录契约:后端返回 accessToken,前端 auth.ts/Login.tsx 对齐 - 补 crypto/jwt/repository 单元测试(含并发写回归) 验证: go build/vet/test 全绿;npm run build 通过;登录→鉴权→优雅关停冒烟通过 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
fb8d10231a
commit
327e282356
93 files changed
+8433
-3089
No files matched your search
@@ -35,10 +35,15 @@ func kubeconfigEnv(cluster *models.Cluster) (env []string, cleanup func(), err e
|
||||
}
|
||||
kubeFile, err := os.CreateTemp("", "kube-*.yaml")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("create kubeconfig temp file failed: %w", err)
|
||||
}
|
||||
if _, err := kubeFile.WriteString(kubeconfig); err != nil {
|
||||
kubeFile.Close()
|
||||
os.Remove(kubeFile.Name())
|
||||
return nil, nil, fmt.Errorf("write kubeconfig temp file failed: %w", err)
|
||||
}
|
||||
kubeFile.WriteString(kubeconfig)
|
||||
kubeFile.Close()
|
||||
os.Chmod(kubeFile.Name(), 0600)
|
||||
env = append(os.Environ(), fmt.Sprintf("KUBECONFIG=%s", kubeFile.Name()))
|
||||
cleanup = func() { os.Remove(kubeFile.Name()) }
|
||||
return env, cleanup, nil
|
||||
@@ -73,6 +78,7 @@ func (d *CollectorDeployer) Install(ctx context.Context) error {
|
||||
|
||||
// Uninstall 按采集器类型分发卸载
|
||||
func (d *CollectorDeployer) Uninstall(ctx context.Context) error {
|
||||
d.log("info", fmt.Sprintf("开始卸载采集器 %s(类型 %s)", d.collector.Name, d.collector.Type), "")
|
||||
switch d.collector.Type {
|
||||
case "host-promtail":
|
||||
return d.uninstallHost(ctx)
|
||||
@@ -85,6 +91,7 @@ func (d *CollectorDeployer) Uninstall(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// ===== host-promtail:SSH 安装 Promtail 二进制 + systemd =====
|
||||
|
||||
func (d *CollectorDeployer) installHost(ctx context.Context, endpoint string) error {
|
||||
host := d.collector.Host
|
||||
if host == nil {
|
||||
@@ -100,7 +107,7 @@ func (d *CollectorDeployer) installHost(ctx context.Context, endpoint string) er
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 架构
|
||||
// 检测架构
|
||||
archOut, _, _ := client.Execute(ctx, "uname -m")
|
||||
arch := strings.TrimSpace(archOut)
|
||||
switch arch {
|
||||
@@ -112,41 +119,70 @@ func (d *CollectorDeployer) installHost(ctx context.Context, endpoint string) er
|
||||
return fmt.Errorf("不支持的主机架构: %s", arch)
|
||||
}
|
||||
|
||||
// 确定 Promtail 版本(优先使用关联的 Loki 版本)
|
||||
version := "3.0.0"
|
||||
if d.loki != nil && d.loki.Version != "" {
|
||||
version = d.loki.Version
|
||||
}
|
||||
|
||||
// 创建必要目录
|
||||
d.log("info", "准备目录 /opt/promtail /etc/promtail /var/lib/promtail", "")
|
||||
if _, _, err := client.Execute(ctx, "mkdir -p /opt/promtail /etc/promtail /var/lib/promtail"); err != nil {
|
||||
return fmt.Errorf("创建 Promtail 目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 下载 Promtail
|
||||
d.log("info", fmt.Sprintf("下载 Promtail(%s)", arch), "")
|
||||
url := fmt.Sprintf("https://github.com/grafana/loki/releases/download/v3.0.0/promtail-linux-%s.zip", arch)
|
||||
dl := fmt.Sprintf("mkdir -p /opt/promtail /etc/promtail && cd /tmp && curl -fsSL -o p.zip '%s' && unzip -o p.zip -d /opt/promtail/ && chmod +x /opt/promtail/promtail-linux-%s", url, arch)
|
||||
d.log("info", fmt.Sprintf("下载 Promtail %s(%s)", version, arch), "")
|
||||
url := fmt.Sprintf("https://github.com/grafana/loki/releases/download/v%s/promtail-linux-%s.zip", version, arch)
|
||||
dl := fmt.Sprintf("cd /tmp && curl -fsSL -o p.zip '%s' && unzip -o p.zip -d /opt/promtail/ && chmod +x /opt/promtail/promtail-linux-%s", url, arch)
|
||||
if _, _, err := client.Execute(ctx, dl); err != nil {
|
||||
return fmt.Errorf("下载 Promtail 失败: %v", err)
|
||||
return fmt.Errorf("下载 Promtail 失败: %w", err)
|
||||
}
|
||||
client.Execute(ctx, "ln -sf /opt/promtail/promtail-linux-* /usr/local/bin/promtail 2>/dev/null || true")
|
||||
|
||||
// 配置
|
||||
// 生成并上传配置
|
||||
logPath := d.collector.LogPath
|
||||
if logPath == "" {
|
||||
logPath = "/var/log/*.log"
|
||||
}
|
||||
conf := PromtailConfig(endpoint, logPath)
|
||||
client.Execute(ctx, fmt.Sprintf("cat > /etc/promtail/promtail.yaml << 'EOF'\n%s\nEOF", conf))
|
||||
d.log("info", "上传 Promtail 配置", "")
|
||||
if _, _, err := client.Execute(ctx, fmt.Sprintf("cat > /etc/promtail/promtail.yaml << 'EOF'\n%s\nEOF", conf)); err != nil {
|
||||
return fmt.Errorf("上传配置失败: %w", err)
|
||||
}
|
||||
|
||||
// systemd 服务
|
||||
// systemd 服务(生产级)
|
||||
service := `[Unit]
|
||||
Description=Promtail Log Collector
|
||||
After=network.target
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml
|
||||
User=root
|
||||
Group=root
|
||||
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml -positions.enabled=true
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
LimitNOFILE=65536
|
||||
StandardOutput=append:/var/log/promtail.log
|
||||
StandardError=append:/var/log/promtail.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`
|
||||
client.Execute(ctx, fmt.Sprintf("cat > /etc/systemd/system/promtail.service << 'EOF'\n%s\nEOF", service))
|
||||
if _, _, err := client.Execute(ctx, "systemctl daemon-reload && systemctl enable promtail && systemctl restart promtail"); err != nil {
|
||||
return fmt.Errorf("启动 Promtail 失败: %v", err)
|
||||
d.log("info", "安装 systemd 服务", "")
|
||||
if _, _, err := client.Execute(ctx, fmt.Sprintf("cat > /etc/systemd/system/promtail.service << 'EOF'\n%s\nEOF", service)); err != nil {
|
||||
return fmt.Errorf("创建 systemd 服务失败: %w", err)
|
||||
}
|
||||
if _, _, err := client.Execute(ctx, "systemctl daemon-reload && systemctl enable promtail && systemctl restart promtail"); err != nil {
|
||||
return fmt.Errorf("启动 Promtail 失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存配置快照
|
||||
d.collector.Config = conf
|
||||
d.log("success", "Promtail 部署完成", "")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -161,11 +197,15 @@ func (d *CollectorDeployer) uninstallHost(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
client.Execute(ctx, "systemctl stop promtail 2>/dev/null || true; systemctl disable promtail 2>/dev/null || true; rm -f /etc/systemd/system/promtail.service /usr/local/bin/promtail; systemctl daemon-reload")
|
||||
client.Execute(ctx, "systemctl stop promtail 2>/dev/null || true")
|
||||
client.Execute(ctx, "systemctl disable promtail 2>/dev/null || true")
|
||||
client.Execute(ctx, "rm -rf /etc/systemd/system/promtail.service /usr/local/bin/promtail /etc/promtail /opt/promtail /var/lib/promtail")
|
||||
client.Execute(ctx, "systemctl daemon-reload")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ===== k8s-daemonset:helm 安装 Promtail DaemonSet =====
|
||||
|
||||
func (d *CollectorDeployer) installDaemonset(ctx context.Context, endpoint string) error {
|
||||
cluster := d.collector.Cluster
|
||||
if cluster == nil {
|
||||
@@ -183,27 +223,27 @@ func (d *CollectorDeployer) installDaemonset(ctx context.Context, endpoint strin
|
||||
}
|
||||
|
||||
values := PromtailHelmValues(endpoint)
|
||||
valuesFile, err := os.CreateTemp("", "promtail-values-*.yaml")
|
||||
valuesFile, valuesCleanup, err := writeTempFile("promtail-values-*.yaml", values, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(valuesFile.Name())
|
||||
valuesFile.WriteString(values)
|
||||
valuesFile.Close()
|
||||
defer valuesCleanup()
|
||||
|
||||
d.log("info", fmt.Sprintf("helm install promtail(命名空间 %s)", ns), "")
|
||||
// 先添加 grafana repo(幂等)
|
||||
// 添加 grafana repo(幂等)
|
||||
repo := exec.CommandContext(ctx, "helm", "repo", "add", "grafana", "https://grafana.github.io/helm-charts")
|
||||
repo.Env = env
|
||||
repo.CombinedOutput()
|
||||
exec.CommandContext(ctx, "helm", "repo", "update").CombinedOutput()
|
||||
upd := exec.CommandContext(ctx, "helm", "repo", "update")
|
||||
upd.Env = env
|
||||
upd.CombinedOutput()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "helm", "upgrade", "--install", "promtail", "grafana/promtail",
|
||||
"-f", valuesFile.Name(), "--namespace", ns, "--create-namespace", "--wait", "--timeout", "5m")
|
||||
"-f", valuesFile, "--namespace", ns, "--create-namespace", "--wait", "--timeout", "5m")
|
||||
cmd.Env = env
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("helm 安装 Promtail 失败: %v\n%s", err, string(out))
|
||||
return fmt.Errorf("helm 安装 Promtail 失败: %w\n%s", err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -229,6 +269,7 @@ func (d *CollectorDeployer) uninstallDaemonset(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// ===== k8s-fluentbit-sidecar:kubectl patch 注入 Fluent Bit sidecar =====
|
||||
|
||||
func (d *CollectorDeployer) installSidecar(ctx context.Context, endpoint string) error {
|
||||
cluster := d.collector.Cluster
|
||||
if cluster == nil {
|
||||
@@ -275,27 +316,31 @@ func (d *CollectorDeployer) installSidecar(ctx context.Context, endpoint string)
|
||||
lokiHost, lokiPort := parseLokiURL(endpoint)
|
||||
conf := FluentbitConfig(lokiHost, lokiPort, logPath, app, ns)
|
||||
cm := FluentbitConfigMapYAML(conf, ns)
|
||||
cmFile, _ := os.CreateTemp("", "fluent-bit-cm-*.yaml")
|
||||
defer os.Remove(cmFile.Name())
|
||||
cmFile.WriteString(cm)
|
||||
cmFile.Close()
|
||||
cmFile, cmCleanup, err := writeTempFile("fluent-bit-cm-*.yaml", cm, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cmCleanup()
|
||||
|
||||
d.log("info", "部署 Fluent Bit ConfigMap", "")
|
||||
applyCm := exec.CommandContext(ctx, "kubectl", "apply", "-f", cmFile.Name())
|
||||
applyCm := exec.CommandContext(ctx, "kubectl", "apply", "-f", cmFile)
|
||||
applyCm.Env = env
|
||||
if out, err := applyCm.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("ConfigMap 部署失败: %v\n%s", err, string(out))
|
||||
return fmt.Errorf("ConfigMap 部署失败: %w\n%s", err, string(out))
|
||||
}
|
||||
|
||||
// 4. patch 注入 sidecar
|
||||
d.log("info", fmt.Sprintf("为 %s 注入 Fluent Bit Sidecar", target), "")
|
||||
patchFile, _ := os.CreateTemp("", "sidecar-patch-*.yaml")
|
||||
defer os.Remove(patchFile.Name())
|
||||
patchFile.WriteString(SidecarPatchYAML)
|
||||
patchFile.Close()
|
||||
patch := exec.CommandContext(ctx, "kubectl", "patch", "deployment", target, "-n", ns, "--type=strategic", "--patch-file", patchFile.Name())
|
||||
patchFile, patchCleanup, err := writeTempFile("sidecar-patch-*.yaml", SidecarPatchYAML, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer patchCleanup()
|
||||
|
||||
patch := exec.CommandContext(ctx, "kubectl", "patch", "deployment", target, "-n", ns, "--type=strategic", "--patch-file", patchFile)
|
||||
patch.Env = env
|
||||
if out, err := patch.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Sidecar 注入失败: %v\n%s", err, string(out))
|
||||
return fmt.Errorf("Sidecar 注入失败: %w\n%s", err, string(out))
|
||||
}
|
||||
|
||||
// 5. 等待 rollout
|
||||
@@ -320,11 +365,13 @@ func (d *CollectorDeployer) uninstallSidecar(ctx context.Context) error {
|
||||
if ns == "" {
|
||||
ns = "default"
|
||||
}
|
||||
patchFile, _ := os.CreateTemp("", "sidecar-rm-*.yaml")
|
||||
defer os.Remove(patchFile.Name())
|
||||
patchFile.WriteString(SidecarRemovePatchYAML)
|
||||
patchFile.Close()
|
||||
patch := exec.CommandContext(ctx, "kubectl", "patch", "deployment", d.collector.TargetDeployment, "-n", ns, "--type=strategic", "--patch-file", patchFile.Name())
|
||||
patchFile, patchCleanup, err := writeTempFile("sidecar-rm-*.yaml", SidecarRemovePatchYAML, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer patchCleanup()
|
||||
|
||||
patch := exec.CommandContext(ctx, "kubectl", "patch", "deployment", d.collector.TargetDeployment, "-n", ns, "--type=strategic", "--patch-file", patchFile)
|
||||
patch.Env = env
|
||||
patch.CombinedOutput()
|
||||
return nil
|
||||
|
||||
Reference in new issue
Block a user