Spaces:
Running
Running
File size: 3,762 Bytes
24a2ddf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /**
* Monitors docker container CPU/memory during a stress test.
*
* Usage (run in parallel with run-stress-test.js):
* node stress/monitor-docker.js --containers pdf-test --interval 1000 --duration 300000 --out stress/results/monitor.json
*/
const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
function parseArgs(argv) {
const args = { containers: 'pdf-test,pdf-prod', interval: 1000, duration: 300000, out: '' };
for (let i = 0; i < argv.length; i++) {
const m = argv[i].match(/^--([^=]+)(?:=(.*))?$/);
if (!m) continue;
if (m[2] !== undefined) {
args[m[1]] = m[2];
} else if (i + 1 < argv.length && !argv[i + 1].startsWith('--')) {
args[m[1]] = argv[++i];
} else {
args[m[1]] = true;
}
}
return args;
}
function sample(containers) {
try {
// Use spawnSync with an args array (no shell) to avoid Windows quoting bugs.
const res = spawnSync('docker', ['stats', '--no-stream', '--format', '{{json .}}', '--', ...containers], { encoding: 'utf8', timeout: 10000 });
if (res.status !== 0 || !res.stdout) return {};
const rows = {};
for (const line of res.stdout.split('\n').filter(Boolean)) {
let obj;
try { obj = JSON.parse(line); } catch (e) { continue; }
const name = obj.Name;
if (!name) continue;
rows[name] = {
cpuPerc: parseFloat(obj.CPUPerc) || 0,
memUsedGiB: parseGiB(obj.MemUsage),
memTotalGiB: 0,
memPerc: parseFloat(obj.MemPerc) || 0,
};
}
return rows;
} catch (e) {
return {};
}
}
function parseGiB(memUsageStr) {
if (!memUsageStr) return 0;
const m = memUsageStr.match(/^([\d.]+)\s*(\w+)\s*\/\s*([\d.]+)\s*(\w+)$/);
if (!m) return 0;
const used = parseFloat(m[1]);
const unit = m[2];
if (unit === 'GiB') return used;
if (unit === 'MiB') return used / 1024;
if (unit === 'KiB') return used / (1024 * 1024);
return used;
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const containers = args.containers.split(',');
const interval = parseInt(args.interval, 10);
const duration = parseInt(args.duration, 10);
const outFile = args.out || path.join(__dirname, 'results', `monitor-${Date.now()}.json`);
const samples = [];
const started = Date.now();
console.log(`Monitoring ${containers.join(',')} for ${duration}ms...`);
while (Date.now() - started < duration) {
const rows = sample(containers);
samples.push({ t: Date.now() - started, rows });
process.stdout.write(`\rt=${Date.now() - started}ms ` + containers.map((c) => {
const r = rows[c];
return r ? `${c}: cpu=${r.cpuPerc.toFixed(1)}% mem=${r.memUsedGiB.toFixed(2)}GiB` : `${c}: n/a`;
}).join(' '));
await new Promise((r) => setTimeout(r, interval));
}
process.stdout.write('\n');
const summary = {};
for (const c of containers) {
const vals = samples.map((s) => s.rows[c]).filter(Boolean);
if (!vals.length) { summary[c] = null; continue; }
const cpu = vals.map((v) => v.cpuPerc).sort((a, b) => a - b);
const mem = vals.map((v) => v.memUsedGiB).sort((a, b) => a - b);
summary[c] = {
cpuPerc: { avg: +(cpu.reduce((a, b) => a + b, 0) / cpu.length).toFixed(1), peak: Math.round(cpu[cpu.length - 1]) },
memUsedGiB: { avg: +(mem.reduce((a, b) => a + b, 0) / mem.length).toFixed(2), peak: +mem[mem.length - 1].toFixed(2) },
};
}
const report = { started, duration, interval, containers, summary, samples };
fs.writeFileSync(outFile, JSON.stringify(report, null, 2));
console.log(`\nMonitor report saved: ${outFile}`);
console.log(JSON.stringify(summary, null, 2));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
|