feat(k8s): M1 SyncCluster 增加 metrics 采集
后端: - SyncCluster 中调 GetNodeMetrics() 获取 metrics-server 数据 - 节点 INSERT 增加 cpu_usage/memory_usage 百分比字段 - 新增 parseCpuNanoToMilli / parseMemKiToMi 辅助函数 - 采集 Pod metrics(为 M2 准备) 前端: - KubernetesClusterDetail 删除硬编码 resourceData mock - 改为从节点 metrics 实时计算集群 CPU/内存使用率 - 图表 Y 轴固定 0-100 域,底部显示百分比数值 新增文档: - ROADMAP.md / TDD_PLAN.md / PROJECT_EVALUATION.md
This commit is contained in:
1 parent
3b112ee813
commit
b3d2011243
5 files changed
+289
-729
No files matched your search
@@ -647,6 +647,24 @@ func (h *KubernetesHandler) SyncCluster(c *gin.Context) {
|
||||
|
||||
result := k8sclient.SyncResult{}
|
||||
|
||||
// Fetch node metrics (best-effort — metrics-server may not be installed) // Logan.Li
|
||||
nodeMetrics := map[string]k8sclient.NodeMetricsItem{}
|
||||
if m, err := client.GetNodeMetrics(); err == nil {
|
||||
for _, item := range m.Items {
|
||||
nodeMetrics[item.Metadata.Name] = item
|
||||
}
|
||||
} else {
|
||||
log.Printf("[K8s] sync: GetNodeMetrics failed (metrics-server not installed?): %v", err)
|
||||
}
|
||||
|
||||
// Fetch pod metrics (best-effort) // Logan.Li
|
||||
podMetrics := map[string]k8sclient.PodMetricsItem{} // key: "namespace/podname"
|
||||
if pm, err := client.ListPodMetrics(""); err == nil {
|
||||
for _, item := range pm.Items {
|
||||
podMetrics[item.Metadata.Namespace+"/"+item.Metadata.Name] = item
|
||||
}
|
||||
}
|
||||
|
||||
// Sync nodes
|
||||
if nodes, err := client.ListNodes(); err == nil {
|
||||
// Use raw SQL for reliability (avoid GORM soft-delete / auto-timestamp issues)
|
||||
@@ -674,10 +692,24 @@ func (h *KubernetesHandler) SyncCluster(c *gin.Context) {
|
||||
}
|
||||
labelsJSON, _ := json.Marshal(n.Metadata.Labels)
|
||||
now := time.Now()
|
||||
err2 := h.db.Exec(`INSERT INTO kubernetes_nodes (cluster_id, name, ip, role, status, os, kubelet_version, cpu, memory, max_pods, labels, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
// Compute CPU/memory usage percentage from metrics-server data // Logan.Li
|
||||
cpuUsagePct, memUsagePct := 0.0, 0.0
|
||||
if m, ok := nodeMetrics[n.Metadata.Name]; ok {
|
||||
cpuCores := parseIntResource(n.Status.Capacity["cpu"])
|
||||
memMB := parseMemResource(n.Status.Capacity["memory"])
|
||||
if cpuCores > 0 {
|
||||
cpuMillicores := parseCpuNanoToMilli(m.Usage["cpu"])
|
||||
cpuUsagePct = float64(cpuMillicores) / float64(cpuCores*1000) * 100
|
||||
}
|
||||
if memMB > 0 {
|
||||
memMi := parseMemKiToMi(m.Usage["memory"])
|
||||
memUsagePct = float64(memMi) / float64(memMB) * 100
|
||||
}
|
||||
}
|
||||
err2 := h.db.Exec(`INSERT INTO kubernetes_nodes (cluster_id, name, ip, role, status, os, kubelet_version, cpu, memory, max_pods, cpu_usage, memory_usage, labels, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
cluster.ID, n.Metadata.Name, ip, role, nodeStatus, n.Status.NodeInfo.OSImage, n.Status.NodeInfo.KubeletVersion,
|
||||
parseIntResource(n.Status.Capacity["cpu"]), parseMemResource(n.Status.Capacity["memory"]), parseIntResource(n.Status.Capacity["pods"]),
|
||||
string(labelsJSON), now, now).Error
|
||||
cpuUsagePct, memUsagePct, string(labelsJSON), now, now).Error
|
||||
if err2 != nil {
|
||||
log.Printf("[K8s] insert node %s error: %v", n.Metadata.Name, err2)
|
||||
} else {
|
||||
@@ -1928,6 +1960,17 @@ func formatCpuFromNano(raw string) string {
|
||||
return fmt.Sprintf("%dm", milli)
|
||||
}
|
||||
|
||||
// parseCpuNanoToMilli converts K8s metrics CPU (nanocores "100354992n") to millicores (int) // Logan.Li
|
||||
func parseCpuNanoToMilli(raw string) int {
|
||||
if raw == "" { return 0 }
|
||||
val := strings.TrimSuffix(raw, "n")
|
||||
var nano int64
|
||||
fmt.Sscanf(val, "%d", &nano)
|
||||
milli := nano / 1000000
|
||||
if milli < 1 { milli = 1 }
|
||||
return int(milli)
|
||||
}
|
||||
|
||||
// formatMemFromKi converts K8s metrics memory (Ki "2154292Ki") to Mi "2105Mi"
|
||||
func formatMemFromKi(raw string) string {
|
||||
if raw == "" { return "" }
|
||||
@@ -1939,6 +1982,15 @@ func formatMemFromKi(raw string) string {
|
||||
return fmt.Sprintf("%dMi", mi)
|
||||
}
|
||||
|
||||
// parseMemKiToMi converts K8s metrics memory (Ki "2154292Ki") to Mi (int) // Logan.Li
|
||||
func parseMemKiToMi(raw string) int {
|
||||
if raw == "" { return 0 }
|
||||
val := strings.TrimSuffix(raw, "Ki")
|
||||
var ki int64
|
||||
fmt.Sscanf(val, "%d", &ki)
|
||||
return int(ki / 1024)
|
||||
}
|
||||
|
||||
|
||||
// GetResourceYAMLNS returns the YAML of a K8s resource
|
||||
func (h *KubernetesHandler) GetResourceYAMLNS(c *gin.Context) {
|
||||
|
||||
Reference in new issue
Block a user