This commit is contained in:
RGJorge
2026-05-10 23:39:12 +00:00
parent 713504dc65
commit 3962e6c319
19 changed files with 1382 additions and 275 deletions
+39 -4
View File
@@ -1,6 +1,34 @@
import { docker } from "./docker";
import type { Service, Stats, DockerEvent } from "../shared/types";
/** Compute real memory usage by subtracting reclaimable page cache.
* Mirrors `docker stats` CLI logic. Works for cgroup v1 and v2.
*
* Why: memory_stats.usage includes the kernel page cache (file-backed pages
* the kernel keeps in RAM "just in case"). That cache is INSTANTLY reclaimable
* under memory pressure and is NOT real usage. Containers with heavy I/O
* (DBs, collectors) appear at 90-100% when actually using 10-15%.
*
* Returns: { real, cache, anon, total, limit } all in bytes. */
export function computeMemoryBreakdown(memoryStats: any): {
real: number;
cache: number;
anon: number;
total: number;
limit: number;
} {
const total = memoryStats?.usage ?? 0;
const limit = memoryStats?.limit ?? 0;
const s = memoryStats?.stats ?? {};
// cgroup v2: 'inactive_file'
// cgroup v1: 'total_inactive_file' (recursive) or 'cache' (legacy)
const cache = s.inactive_file ?? s.total_inactive_file ?? s.cache ?? 0;
// anon = process memory (heap, stack). cgroup v2: 'anon'. cgroup v1: 'rss' or 'total_rss'.
const anon = s.anon ?? s.total_rss ?? s.rss ?? 0;
const real = Math.max(0, total - cache);
return { real, cache, anon, total, limit };
}
export async function pollStats(services: Service[]): Promise<Stats[]> {
const running = services.filter((s) => s.state === "running");
const results: Stats[] = [];
@@ -30,14 +58,21 @@ export async function pollStats(services: Service[]): Promise<Stats[]> {
? (cpuHost * 100000 / svc.cpu_quota)
: cpuHost;
const memUsage = raw.memory_stats.usage || 0;
const memLimit = raw.memory_stats.limit || 1;
const mb = computeMemoryBreakdown(raw.memory_stats);
const memLimit = mb.limit || 1;
const TO_MB = 1024 * 1024;
results.push({
service: svc.uid,
cpu: parseFloat(cpu.toFixed(2)),
mem_mb: parseFloat((memUsage / 1024 / 1024).toFixed(1)),
mem_percent: parseFloat(((memUsage / memLimit) * 100).toFixed(1)),
mem_mb: parseFloat((mb.real / TO_MB).toFixed(1)),
mem_percent: parseFloat(((mb.real / memLimit) * 100).toFixed(1)),
mem_breakdown: {
anon_mb: parseFloat((mb.anon / TO_MB).toFixed(1)),
cache_mb: parseFloat((mb.cache / TO_MB).toFixed(1)),
total_mb: parseFloat((mb.total / TO_MB).toFixed(1)),
limit_mb: parseFloat((mb.limit / TO_MB).toFixed(1)),
},
});
} catch {
// Container may have stopped between discovery and stats