diff --git a/backend/services/collector_deployer.go b/backend/services/collector_deployer.go index 582d247..959c96f 100644 --- a/backend/services/collector_deployer.go +++ b/backend/services/collector_deployer.go @@ -136,7 +136,7 @@ After=network-online.target Type=simple User=root Group=root -ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml -positions.enabled=true +ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml ExecReload=/bin/kill -HUP $MAINPID Restart=always RestartSec=10 @@ -155,12 +155,52 @@ WantedBy=multi-user.target return fmt.Errorf("启动 Promtail 失败: %w", err) } + // 必须校验服务真的起来了: + // 原来只判断 systemctl 命令有没有执行错误,结果 Promtail 因配置/参数错误 + // 崩溃重启 40+ 次,平台仍然显示"采集器已部署",用户以为采集是好的。 + if err := waitServiceActive(ctx, client, "promtail", 20*time.Second); err != nil { + out, _, _ := client.Execute(ctx, "journalctl -u promtail -n 15 --no-pager 2>/dev/null | tail -15") + tail := strings.TrimSpace(out) + if tail == "" { + out, _, _ = client.Execute(ctx, "tail -10 /var/log/promtail.log 2>/dev/null") + tail = strings.TrimSpace(out) + } + return fmt.Errorf("Promtail 启动后未处于运行状态:%v\n最近日志:\n%s", err, truncateForMessage(tail, 900)) + } + // 保存配置快照 d.collector.Config = conf d.log("success", "Promtail 部署完成", "") return nil } +// waitServiceActive 轮询 systemctl is-active,直到 active 或超时。 +// Promtail 起不来时 systemctl restart 依然返回 0(只是排队拉起), +// 所以必须自己查状态,否则会把"崩溃重启中"误报成部署成功。 +func waitServiceActive(ctx context.Context, client *SSHClient, service string, timeout time.Duration) error { + deadline := time.Now().Add(timeout) + var last string + for time.Now().Before(deadline) { + out, _, err := client.Execute(ctx, fmt.Sprintf("systemctl is-active %s 2>&1", service)) + last = strings.TrimSpace(out) + if err == nil && last == "active" { + return nil + } + time.Sleep(2 * time.Second) + } + if last == "" { + last = "unknown" + } + return fmt.Errorf("%s 状态=%s", service, last) +} + +func truncateForMessage(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max] + "…" +} + func (d *CollectorDeployer) uninstallHost(ctx context.Context) error { host := d.collector.Host if host == nil {