From c69d5479b3eb3dd740cd5c3461755a6abe9640cb Mon Sep 17 00:00:00 2001 From: "Logan.Li" Date: Sun, 18 Oct 2026 21:05:59 +0000 Subject: [PATCH] =?UTF-8?q?feat(nodes):=20=E8=8A=82=E7=82=B9=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=96=B0=E5=A2=9E=20Request=20=E5=8D=A0=E7=94=A8?= =?UTF-8?q?=E5=88=97=EF=BC=9B=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E6=94=B9=E4=B8=BA=2010/30/50/100?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 节点管理原表格只有"使用率"一列(CPU/内存双条),缺少 Request 占用。 - ListNodes 一次性拉取 Pod 并按节点聚合 CPU/内存 Request, 计算 cpu_alloc_pct / mem_alloc_pct(已申请 / 可分配), 新增 pod_count / cpu_request_m / mem_request_mi 字段 - 前端新增"Request 占用"双条列,阈值 60%/85% 变色, tooltip 展示"已申请 / 可分配"绝对值 - 不含磁盘/分区:K8s 原生 API 不提供磁盘用量, 后续接入 Prometheus 后由监控侧承担 服务器列表分页选项由 10/20/30 调整为 10/30/50/100。 // Logan.Li --- backend/controller/k8s_node.go | 51 +++++++++++++++++++ .../components/kubernetes/KubernetesNodes.tsx | 38 +++++++++++++- .../src/components/server/ServerManager.tsx | 3 +- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/backend/controller/k8s_node.go b/backend/controller/k8s_node.go index 0d8e000..f3dd9da 100644 --- a/backend/controller/k8s_node.go +++ b/backend/controller/k8s_node.go @@ -49,7 +49,44 @@ func (h *KubernetesHandler) ListNodes(c *gin.Context) { // Metrics absolute (for detail pages) CPUUsageM string `json:"cpu_usage_m,omitempty"` // e.g. "122m" MemUsageMi string `json:"mem_usage_mi,omitempty"` // e.g. "2105Mi" + + // Request 占用(分配率 = 该节点 Pod 已申请 / 可分配)// Logan.Li + PodCount int `json:"pod_count"` + CPURequestM int `json:"cpu_request_m"` + MemRequestMi int64 `json:"mem_request_mi"` + CPUAllocPct float64 `json:"cpu_alloc_pct"` + MemAllocPct float64 `json:"mem_alloc_pct"` } + + // 一次性拉取 Pod 并按节点聚合 Request:列表页展示"Request 占用"所需, + // 避免每行单独查一次 Pod 列表。 // Logan.Li + type reqAgg struct { + cpuM int + memMi int64 + pods int + } + podReqs := map[string]*reqAgg{} + if pods, perr := client.ListPods(); perr == nil && pods != nil { + for _, p := range pods.Items { + if p.Spec.NodeName == "" { + continue + } + agg := podReqs[p.Spec.NodeName] + if agg == nil { + agg = &reqAgg{} + podReqs[p.Spec.NodeName] = agg + } + agg.pods++ + for _, ctr := range p.Spec.Containers { + if ctr.Resources.Requests == nil { + continue + } + agg.cpuM += parseCpuToMilli(ctr.Resources.Requests["cpu"]) + agg.memMi += parseMemToMi(ctr.Resources.Requests["memory"]) + } + } + } + result := []NodeSummary{} for _, n := range nodes.Items { role := "worker" @@ -100,6 +137,20 @@ func (h *KubernetesHandler) ListNodes(c *gin.Context) { s.MemoryUsage = float64(memMi) / float64(memCapacity) * 100 } } + // Request 占用:本节点 Pod 已申请 / 可分配。 // Logan.Li + if agg := podReqs[n.Metadata.Name]; agg != nil { + allocCPU := parseIntResource(n.Status.Allocatable["cpu"]) + allocMem := parseMemResource(n.Status.Allocatable["memory"]) + s.PodCount = agg.pods + s.CPURequestM = agg.cpuM + s.MemRequestMi = agg.memMi + if allocCPU > 0 { + s.CPUAllocPct = float64(agg.cpuM) / float64(allocCPU*1000) * 100 + } + if allocMem > 0 { + s.MemAllocPct = float64(agg.memMi) / float64(allocMem) * 100 + } + } result = append(result, s) } c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result}) diff --git a/frontend/src/components/kubernetes/KubernetesNodes.tsx b/frontend/src/components/kubernetes/KubernetesNodes.tsx index b4461d6..f38026d 100644 --- a/frontend/src/components/kubernetes/KubernetesNodes.tsx +++ b/frontend/src/components/kubernetes/KubernetesNodes.tsx @@ -116,15 +116,17 @@ export function KubernetesNodes() { {i18n.t('system.terminalStatus')} {i18n.t('deploy.detailDialog.config')} {i18n.t('server.usage')} + {/* Request 占用:已申请 / 可分配,与"使用率"分列,避免一列塞四个条 // Logan.Li */} + {i18n.t('k8s.node.requestUsage', { defaultValue: 'Request 占用' })} Pods Kubelet {loading ? ( - {i18n.t('monitor.logPanel.logResults.loading')} + {i18n.t('monitor.logPanel.logResults.loading')} ) : filtered.length === 0 ? ( - {selectedCluster ? i18n.t('k8s.empty.nodes') : i18n.t('kubernetes.kubernetesnodes.sf8c38834')} + {selectedCluster ? i18n.t('k8s.empty.nodes') : i18n.t('kubernetes.kubernetesnodes.sf8c38834')} ) : ( filtered.map((n) => ( @@ -161,6 +163,38 @@ export function KubernetesNodes() { ); })()} + + {(() => { + // Request 占用 = 该节点 Pod 已申请 / 可分配。 + // 百分比给直观判断,tooltip 给绝对值(分子/分母一起看才有意义)。 // Logan.Li + const cpuPct = n.cpu_alloc_pct || 0; + const memPct = n.mem_alloc_pct || 0; + const cpuReq = n.cpu_request_m || 0; + const memReq = n.mem_request_mi || 0; + const bar = (val: number) => val >= 85 ? 'bg-[var(--dg-red)]' : val >= 60 ? 'bg-[var(--dg-orange)]' : 'bg-[var(--dg-blue)]'; + const txt = (val: number) => val >= 85 ? 'text-[var(--dg-red)]' : val >= 60 ? 'text-[var(--dg-orange)]' : 'text-[var(--dg-text-secondary)]'; + const cpuCap = (n.cpu || 0) * 1000; + const memCap = n.memory || 0; + return ( +
+
+ CPU +
+
+
+ {cpuPct.toFixed(0)}% +
+
+ Mem +
+
+
+ {memPct.toFixed(0)}% +
+
+ ); + })()} + {n.pod_count || 0} / {n.max_pods || '-'} {n.kubelet_version || '-'} diff --git a/frontend/src/components/server/ServerManager.tsx b/frontend/src/components/server/ServerManager.tsx index 2646d0b..edb53a7 100644 --- a/frontend/src/components/server/ServerManager.tsx +++ b/frontend/src/components/server/ServerManager.tsx @@ -583,8 +583,9 @@ export function ServerManager() { 10 - 20 30 + 50 + 100 {t("server.items")}