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
@@ -0,0 +1,133 @@
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现对所有已挂载的真实文件系统执行 statfs,采集磁盘使用情况。
|
||||
// 使用 golang.org/x/sys/unix 的 Statfs,兼容 Linux 与 macOS。
|
||||
//
|
||||
// 署名 Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
// DiskCollector 磁盘使用率采集器
|
||||
type DiskCollector struct{}
|
||||
|
||||
// NewDiskCollector 创建磁盘采集器实例
|
||||
func NewDiskCollector() *DiskCollector {
|
||||
return &DiskCollector{}
|
||||
}
|
||||
|
||||
// mount 描述一个挂载点信息
|
||||
type mount struct {
|
||||
device string
|
||||
mountpoint string
|
||||
fstype string
|
||||
}
|
||||
|
||||
// realFilesystems 允许采集的"真实"文件系统类型白名单,
|
||||
// 用于排除 tmpfs/proc/sysfs 等伪文件系统,避免产生噪音数据。
|
||||
var realFilesystems = map[string]bool{
|
||||
"ext2": true, "ext3": true, "ext4": true,
|
||||
"xfs": true, "btrfs": true, "zfs": true,
|
||||
"apfs": true, "hfs": true, "exfat": true,
|
||||
"ntfs": true, "vfat": true, "fat32": true,
|
||||
"f2fs": true, "jfs": true, "reiserfs": true,
|
||||
"ufs": true,
|
||||
}
|
||||
|
||||
// Collect 采集所有真实文件系统挂载点的磁盘使用情况
|
||||
func (d *DiskCollector) Collect() ([]model.DiskStat, error) {
|
||||
mounts, err := d.getMounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取挂载点失败: %w", err)
|
||||
}
|
||||
|
||||
disks := make([]model.DiskStat, 0, len(mounts))
|
||||
seen := make(map[string]bool) // 按挂载点去重
|
||||
|
||||
for _, m := range mounts {
|
||||
if !realFilesystems[m.fstype] {
|
||||
continue
|
||||
}
|
||||
if seen[m.mountpoint] {
|
||||
continue
|
||||
}
|
||||
seen[m.mountpoint] = true
|
||||
|
||||
var fs unix.Statfs_t
|
||||
if err := unix.Statfs(m.mountpoint, &fs); err != nil {
|
||||
// 单个挂载点采集失败不影响其余挂载点
|
||||
continue
|
||||
}
|
||||
|
||||
bsize := uint64(fs.Bsize)
|
||||
total := fs.Blocks * bsize
|
||||
free := fs.Bfree * bsize
|
||||
if total == 0 {
|
||||
continue
|
||||
}
|
||||
used := total - free
|
||||
|
||||
disks = append(disks, model.DiskStat{
|
||||
Mount: m.mountpoint,
|
||||
Device: m.device,
|
||||
TotalGB: bytesToGB(total),
|
||||
UsedGB: bytesToGB(used),
|
||||
UsagePercent: float64(used) / float64(total) * 100.0,
|
||||
})
|
||||
}
|
||||
return disks, nil
|
||||
}
|
||||
|
||||
// getMounts 获取挂载点列表。
|
||||
// - Linux: 解析 /proc/mounts
|
||||
// - macOS(开发环境): 返回根分区
|
||||
func (d *DiskCollector) getMounts() ([]mount, error) {
|
||||
if runtime.GOOS != "linux" {
|
||||
// 开发环境(macOS)下提供根分区以便本地调试
|
||||
return []mount{{device: "/dev/disk1", mountpoint: "/", fstype: "apfs"}}, nil
|
||||
}
|
||||
|
||||
f, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var mounts []mount
|
||||
scanner := bufio.NewScanner(f)
|
||||
// 挂载点路径可能较长,扩大缓冲区
|
||||
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
||||
for scanner.Scan() {
|
||||
// 形如: /dev/sda1 / ext4 rw,relatime 0 0
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < 3 {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, mount{
|
||||
device: fields[0],
|
||||
mountpoint: unescapeMount(fields[1]),
|
||||
fstype: fields[2],
|
||||
})
|
||||
}
|
||||
return mounts, scanner.Err()
|
||||
}
|
||||
|
||||
// unescapeMount 还原 /proc/mounts 中被转义的挂载点路径(如 \040 -> 空格)
|
||||
func unescapeMount(s string) string {
|
||||
return strings.ReplaceAll(s, "\\040", " ")
|
||||
}
|
||||
|
||||
// bytesToGB 字节数转 GB
|
||||
func bytesToGB(b uint64) float64 {
|
||||
const gb = 1024.0 * 1024.0 * 1024.0
|
||||
return float64(b) / gb
|
||||
}
|
||||
Reference in new issue
Block a user