From cd1072779518f1c527fc0549b680cea8106d130c Mon Sep 17 00:00:00 2001 From: Henry Dollman Date: Fri, 8 Nov 2024 18:00:30 -0500 Subject: [PATCH] gpu usage and vram charts --- beszel/internal/agent/agent.go | 10 +++-- beszel/internal/agent/gpu.go | 4 +- .../site/src/components/charts/area-chart.tsx | 22 +++++++--- beszel/site/src/components/routes/system.tsx | 42 ++++++++++++++++++- beszel/site/src/components/spinner.tsx | 2 +- beszel/site/src/types.d.ts | 15 +++++++ supplemental/docker/agent/docker-compose.yml | 3 +- 7 files changed, 82 insertions(+), 16 deletions(-) diff --git a/beszel/internal/agent/agent.go b/beszel/internal/agent/agent.go index deb4c1a..7440bd1 100644 --- a/beszel/internal/agent/agent.go +++ b/beszel/internal/agent/agent.go @@ -76,10 +76,12 @@ func (a *Agent) Run(pubKey []byte, addr string) { a.dockerManager = newDockerManager() // initialize GPU manager - if gm, err := NewGPUManager(); err != nil { - slog.Error("GPU manager", "err", err) - } else { - a.gpuManager = gm + if os.Getenv("GPU") == "true" { + if gm, err := NewGPUManager(); err != nil { + slog.Error("GPU manager", "err", err) + } else { + a.gpuManager = gm + } } // if debugging, print stats diff --git a/beszel/internal/agent/gpu.go b/beszel/internal/agent/gpu.go index ae59272..de064fb 100644 --- a/beszel/internal/agent/gpu.go +++ b/beszel/internal/agent/gpu.go @@ -94,8 +94,8 @@ func (gm *GPUManager) parseNvidiaData(output []byte) { // update gpu data gpu := gm.GpuDataMap[id] gpu.Temperature += temp - gpu.MemoryUsed += memoryUsage - gpu.MemoryTotal += totalMemory + gpu.MemoryUsed += memoryUsage / 1.024 + gpu.MemoryTotal += totalMemory / 1.024 gpu.Usage += usage gpu.Power += power gpu.Count++ diff --git a/beszel/site/src/components/charts/area-chart.tsx b/beszel/site/src/components/charts/area-chart.tsx index 129cba4..b321ee2 100644 --- a/beszel/site/src/components/charts/area-chart.tsx +++ b/beszel/site/src/components/charts/area-chart.tsx @@ -33,11 +33,15 @@ export default memo(function AreaChartDefault({ unit = " MB/s", chartName, chartData, + max, + tickFormatter, }: { maxToggled?: boolean unit?: string chartName: string chartData: ChartData + max?: number + tickFormatter?: (value: number) => string }) { const { yAxisWidth, updateYAxisWidth } = useYAxisWidth() const { i18n } = useLingui() @@ -52,19 +56,21 @@ export default memo(function AreaChartDefault({ return [[t`CPU Usage`, "cpu", 1, 0.4]] } else if (chartName === "dio") { return [ - [t({ message: "Write", comment: "Context is disk write" }), "dw", 3, 0.3], - [t({ message: "Read", comment: "Context is disk read" }), "dr", 1, 0.3], + [t({ message: "Write", comment: "Disk write" }), "dw", 3, 0.3], + [t({ message: "Read", comment: "Disk read" }), "dr", 1, 0.3], ] } else if (chartName === "bw") { return [ - [t({ message: "Sent", comment: "Context is network bytes sent (upload)" }), "ns", 5, 0.2], - [t({ message: "Received", comment: "Context is network bytes received (download)" }), "nr", 2, 0.2], + [t({ message: "Sent", comment: "Network bytes sent (upload)" }), "ns", 5, 0.2], + [t({ message: "Received", comment: "Network bytes received (download)" }), "nr", 2, 0.2], ] } else if (chartName.startsWith("efs")) { return [ [t`Write`, `${chartName}.w`, 3, 0.3], [t`Read`, `${chartName}.r`, 1, 0.3], ] + } else if (chartName.startsWith("g.")) { + return [chartName.includes("mu") ? [t`Used`, chartName, 2, 0.25] : [t`Usage`, chartName, 1, 0.4]] } return [] }, [chartName, i18n.locale]) @@ -89,8 +95,14 @@ export default memo(function AreaChartDefault({ orientation={chartData.orientation} className="tracking-tighter" width={yAxisWidth} + domain={[0, max ?? "auto"]} tickFormatter={(value) => { - const val = toFixedWithoutTrailingZeros(value, 2) + unit + let val: string + if (tickFormatter) { + val = tickFormatter(value) + } else { + val = toFixedWithoutTrailingZeros(value, 2) + unit + } return updateYAxisWidth(val) }} tickLine={false} diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index c7dfb9b..923e291 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -1,12 +1,12 @@ import { $systems, pb, $chartTime, $containerFilter, $userSettings, $direction } from "@/lib/stores" -import { ChartData, ChartTimes, ContainerStatsRecord, SystemRecord, SystemStatsRecord } from "@/types" +import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types" import React, { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react" import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card" import { useStore } from "@nanostores/react" import Spinner from "../spinner" import { ClockArrowUp, CpuIcon, GlobeIcon, LayoutGridIcon, MonitorIcon, XIcon } from "lucide-react" import ChartTimeSelect from "../charts/chart-time-select" -import { chartTimeData, cn, getPbTimestamp, useLocalStorage } from "@/lib/utils" +import { chartTimeData, cn, getPbTimestamp, getSizeAndUnit, toFixedFloat, useLocalStorage } from "@/lib/utils" import { Separator } from "../ui/separator" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip" import { Button } from "../ui/button" @@ -478,6 +478,44 @@ export default function SystemDetail({ name }: { name: string }) { )} + {/* GPU charts */} + {Object.keys(systemStats.at(-1)?.stats.g ?? {}).length > 0 && ( +
+ {Object.keys(systemStats.at(-1)?.stats.g ?? {}).map((id) => { + const gpu = systemStats.at(-1)?.stats.g?.[id] as GPUData + return ( +
+ + + + + { + const { v, u } = getSizeAndUnit(value, false) + return toFixedFloat(v, 1) + u + }} + /> + +
+ ) + })} +
+ )} + {/* extra filesystem charts */} {Object.keys(systemStats.at(-1)?.stats.efs ?? {}).length > 0 && (
diff --git a/beszel/site/src/components/spinner.tsx b/beszel/site/src/components/spinner.tsx index 81b3c3b..b19ec66 100644 --- a/beszel/site/src/components/spinner.tsx +++ b/beszel/site/src/components/spinner.tsx @@ -4,7 +4,7 @@ export default function ({ msg }: { msg?: string }) { return (
{msg ? ( -

{msg}

+

{msg}

) : ( )} diff --git a/beszel/site/src/types.d.ts b/beszel/site/src/types.d.ts index 1c063b8..90a7e78 100644 --- a/beszel/site/src/types.d.ts +++ b/beszel/site/src/types.d.ts @@ -81,6 +81,21 @@ export interface SystemStats { t?: Record /** extra filesystems */ efs?: Record + /** GPU data */ + g?: Record +} + +export interface GPUData { + /** name */ + n: string + /** memory used (mb) */ + mu?: number + /** memory total (mb) */ + mt?: number + /** usage (%) */ + u: number + /** power (w) */ + p?: number } export interface ExtraFsStats { diff --git a/supplemental/docker/agent/docker-compose.yml b/supplemental/docker/agent/docker-compose.yml index b933856..e9574d2 100644 --- a/supplemental/docker/agent/docker-compose.yml +++ b/supplemental/docker/agent/docker-compose.yml @@ -7,8 +7,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # monitor other disks / partitions by mounting a folder in /extra-filesystems - # - /mnt/disk1/.beszel:/extra-filesystems/disk1:ro + # - /mnt/disk/.beszel:/extra-filesystems/sda1:ro environment: PORT: 45876 KEY: 'ssh-ed25519 YOUR_PUBLIC_KEY' - # FILESYSTEM: /dev/sda1 # override the root partition / device for disk I/O stats