feat: add monitoring agent (Go) + agent report API + cloud billing UI
- Go agent: CPU/mem/disk/port collectors, HTTP report, static binary - Cross-compile: linux/amd64 + linux/arm64 (statically linked) - One-line install script for all Linux distros - Backend: POST /api/v1/agent/report (no JWT) + GET /agent/latest,history (JWT) - CloudBilling.tsx: fixed syntax errors, stats/trends/records/alerts - K8s menu renamed to Kubernetes管理 Signed-off-by: Logan.Li
This commit is contained in:
1 parent
77d13b07d5
commit
ac0b8e75c0
17 files changed
+1267
No files matched your search
+136
@@ -0,0 +1,136 @@
|
||||
// Package main 是 AIOps Agent 的入口。
|
||||
// Agent 定时采集 CPU、内存、磁盘及监听端口指标,并通过 HTTP 上报至 AIOps 后端。
|
||||
//
|
||||
// 以下变量可在编译时通过 -ldflags "-X main.version=... -X main.buildTime=..." 注入。
|
||||
//
|
||||
// 署名 Logan.Li
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"ezops/agent/collector"
|
||||
"ezops/agent/model"
|
||||
"ezops/agent/reporter"
|
||||
)
|
||||
|
||||
// 编译时可注入的版本信息
|
||||
var (
|
||||
version = "dev"
|
||||
buildTime = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server := flag.String("server", "http://localhost:8080", "AIOps 后端上报地址(含 scheme 与端口)")
|
||||
interval := flag.Int("interval", 10, "采集与上报间隔,单位:秒")
|
||||
token := flag.String("token", "", "Bearer 鉴权令牌")
|
||||
hostnameFlag := flag.String("hostname", "", "上报主机名,默认使用系统主机名")
|
||||
flag.Parse()
|
||||
|
||||
// 统一日志格式:带微秒时间戳,便于排查问题
|
||||
logger := log.New(os.Stdout, "[aiops-agent] ", log.LstdFlags|log.Lmicroseconds)
|
||||
|
||||
// 解析主机名:优先使用 -hostname,否则回退到 os.Hostname()
|
||||
hostname := *hostnameFlag
|
||||
if hostname == "" {
|
||||
if h, err := os.Hostname(); err == nil {
|
||||
hostname = h
|
||||
} else {
|
||||
hostname = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// 参数校验
|
||||
if *server == "" {
|
||||
logger.Fatal("参数 -server 不能为空")
|
||||
}
|
||||
if *interval < 1 {
|
||||
logger.Fatalf("参数 -interval 必须 >= 1,当前为 %d", *interval)
|
||||
}
|
||||
|
||||
logger.Printf("AIOps Agent 启动: version=%s buildTime=%s", version, buildTime)
|
||||
logger.Printf("配置: server=%s interval=%ds hostname=%q token=%s",
|
||||
*server, *interval, hostname, maskToken(*token))
|
||||
|
||||
// 初始化各采集器与上报器
|
||||
cpuCollector := collector.NewCPUCollector()
|
||||
memCollector := collector.NewMemCollector()
|
||||
diskCollector := collector.NewDiskCollector()
|
||||
portCollector := collector.NewPortCollector()
|
||||
rep := reporter.New(*server, *token, logger)
|
||||
|
||||
// 监听 SIGINT / SIGTERM,支持优雅退出
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
ticker := time.NewTicker(time.Duration(*interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
// collectAndReport 执行单次采集并上报;各项采集独立进行,单项失败不影响整体上报
|
||||
collectAndReport := func() {
|
||||
report := &model.Report{
|
||||
Hostname: hostname,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
if stat, err := cpuCollector.Collect(); err != nil {
|
||||
logger.Printf("CPU 采集异常: %v", err)
|
||||
} else {
|
||||
report.CPU = stat
|
||||
}
|
||||
|
||||
if stat, err := memCollector.Collect(); err != nil {
|
||||
logger.Printf("内存采集异常: %v", err)
|
||||
} else {
|
||||
report.Memory = stat
|
||||
}
|
||||
|
||||
if disks, err := diskCollector.Collect(); err != nil {
|
||||
logger.Printf("磁盘采集异常: %v", err)
|
||||
} else {
|
||||
report.Disks = disks
|
||||
}
|
||||
|
||||
if ports, err := portCollector.Collect(); err != nil {
|
||||
logger.Printf("端口采集异常: %v", err)
|
||||
} else {
|
||||
report.Ports = ports
|
||||
}
|
||||
|
||||
if err := rep.Send(ctx, report); err != nil {
|
||||
logger.Printf("上报失败: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Printf("上报成功: cpu=%.2f%% mem=%.2f%% disks=%d ports=%d",
|
||||
report.CPU.UsagePercent, report.Memory.UsagePercent, len(report.Disks), len(report.Ports))
|
||||
}
|
||||
|
||||
// 启动后立即执行一次,再按 ticker 周期执行
|
||||
collectAndReport()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
collectAndReport()
|
||||
case <-ctx.Done():
|
||||
logger.Println("收到退出信号,Agent 正在关闭...")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maskToken 对令牌做脱敏处理,仅用于日志输出
|
||||
func maskToken(token string) string {
|
||||
if token == "" {
|
||||
return "(empty)"
|
||||
}
|
||||
if len(token) <= 4 {
|
||||
return "****"
|
||||
}
|
||||
return token[:4] + "****"
|
||||
}
|
||||
Reference in new issue
Block a user