fix(docker): report current CPU usage instead of the lifetime average (#6249)
This commit is contained in:
1 parent
9864fed775
commit
af89423f66
2 files changed
+37
-4
No files matched your search
@@ -300,7 +300,10 @@ async function getContainersWithStatsAsync() {
|
||||
|
||||
const stats = await instance
|
||||
.getContainer(container.Id)
|
||||
.stats({ stream: false, "one-shot": true })
|
||||
// Not in one-shot mode: the daemon then includes precpu_stats (the
|
||||
// previous reading), which is required to compute the current CPU usage
|
||||
// as a delta. One-shot omits it and would only expose lifetime totals.
|
||||
.stats({ stream: false })
|
||||
.catch(
|
||||
() =>
|
||||
({
|
||||
@@ -336,12 +339,22 @@ export function calculateCpuUsage(stats: ContainerStats): number {
|
||||
}
|
||||
|
||||
const numberOfCpus = stats.cpu_stats.online_cpus;
|
||||
const usage = stats.cpu_stats.system_cpu_usage;
|
||||
if (!usage || usage === 0) {
|
||||
|
||||
// Docker's own formula for the current (instantaneous) CPU usage: it compares
|
||||
// the latest reading against the previous one (precpu_stats). Dividing the
|
||||
// cumulative total_usage by the cumulative system_cpu_usage instead would
|
||||
// report the container's lifetime average, which stays high long after a load
|
||||
// spike has ended. precpu_stats is only present when the stats are NOT fetched
|
||||
// in one-shot mode; when it is absent (e.g. Podman) the deltas fall back to the
|
||||
// cumulative totals, matching the previous behaviour.
|
||||
const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - (stats.precpu_stats?.cpu_usage?.total_usage ?? 0);
|
||||
const systemDelta = (stats.cpu_stats.system_cpu_usage ?? 0) - (stats.precpu_stats?.system_cpu_usage ?? 0);
|
||||
|
||||
if (systemDelta <= 0 || cpuDelta < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (stats.cpu_stats.cpu_usage.total_usage / usage) * numberOfCpus * 100;
|
||||
return (cpuDelta / systemDelta) * numberOfCpus * 100;
|
||||
}
|
||||
|
||||
export function calculateMemoryUsage(stats: ContainerStats): number {
|
||||
|
||||
@@ -75,6 +75,26 @@ describe("calculateCpuUsage", () => {
|
||||
// (500 / 100000) * 2 * 100 = 1
|
||||
expect(calculateCpuUsage(stats)).toBe(1);
|
||||
});
|
||||
|
||||
test("should use the delta against precpu_stats for the current usage", () => {
|
||||
const stats = createStats({
|
||||
cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 1_002_000 }, system_cpu_usage: 20_000 },
|
||||
precpu_stats: { cpu_usage: { total_usage: 1_000_000 }, system_cpu_usage: 10_000 },
|
||||
});
|
||||
// Despite a large lifetime total_usage, only the recent delta counts:
|
||||
// (2000 / 10000) * 4 * 100 = 80
|
||||
expect(calculateCpuUsage(stats)).toBe(80);
|
||||
});
|
||||
|
||||
test("should report 0 for a container that was busy over its lifetime but is idle now", () => {
|
||||
const stats = createStats({
|
||||
cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 1_000_000 }, system_cpu_usage: 20_000 },
|
||||
precpu_stats: { cpu_usage: { total_usage: 1_000_000 }, system_cpu_usage: 10_000 },
|
||||
});
|
||||
// No CPU delta since the last reading -> current usage is 0, even though the
|
||||
// lifetime average would be high. This is the regression the fix addresses.
|
||||
expect(calculateCpuUsage(stats)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("calculateMemoryUsage", () => {
|
||||
|
||||
Reference in new issue
Block a user