mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
new approach using cgroup
This commit is contained in:
parent
b3d65e0a1e
commit
bb4ef54db9
3 changed files with 148 additions and 19 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { Server, Socket } from "socket.io";
|
import { Server, Socket } from "socket.io";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
const MAX_USERS_PER_ROOM = 60;
|
const MAX_USERS_PER_ROOM = 60;
|
||||||
const MAX_TOTAL_CONNECTIONS = 2000;
|
const MAX_TOTAL_CONNECTIONS = 2000;
|
||||||
|
|
@ -114,30 +115,159 @@ export const setupWebsocket = (io: Server): void => {
|
||||||
|
|
||||||
class ContainerMetrics {
|
class ContainerMetrics {
|
||||||
private totalSystemMemory = os.totalmem();
|
private totalSystemMemory = os.totalmem();
|
||||||
|
private cgroupv2 = this.isCgroupV2();
|
||||||
|
private lastCPUUsage = 0;
|
||||||
|
private lastCPUTime = Date.now();
|
||||||
|
|
||||||
getMetrics() {
|
private isCgroupV2(): boolean {
|
||||||
|
return fs.existsSync('/sys/fs/cgroup/cgroup.controllers');
|
||||||
|
}
|
||||||
|
|
||||||
|
private readCgroupFile(filepath: string): string {
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(filepath, 'utf-8').trim();
|
||||||
|
} catch (error) {
|
||||||
|
console.debug(`Could not read ${filepath}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCgroupCPUUsage(): number {
|
||||||
|
try {
|
||||||
|
if (this.cgroupv2) {
|
||||||
|
const usage = this.readCgroupFile('/sys/fs/cgroup/cpu.stat');
|
||||||
|
const usageMatch = usage.match(/usage_usec\s+(\d+)/);
|
||||||
|
if (usageMatch) {
|
||||||
|
const currentUsage = Number(usageMatch[1]) / 1000000;
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const cpuDelta = currentUsage - this.lastCPUUsage;
|
||||||
|
const timeDelta = (currentTime - this.lastCPUTime) / 1000;
|
||||||
|
|
||||||
|
this.lastCPUUsage = currentUsage;
|
||||||
|
this.lastCPUTime = currentTime;
|
||||||
|
|
||||||
|
return (cpuDelta / timeDelta) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cgroupV1Paths = [
|
||||||
|
'/sys/fs/cgroup/cpu/cpuacct.usage',
|
||||||
|
'/sys/fs/cgroup/cpuacct/cpuacct.usage',
|
||||||
|
'/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const path of cgroupV1Paths) {
|
||||||
|
const usage = this.readCgroupFile(path);
|
||||||
|
if (usage) {
|
||||||
|
const currentUsage = Number(usage) / 1000000000;
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const cpuDelta = currentUsage - this.lastCPUUsage;
|
||||||
|
const timeDelta = (currentTime - this.lastCPUTime) / 1000;
|
||||||
|
|
||||||
|
this.lastCPUUsage = currentUsage;
|
||||||
|
this.lastCPUTime = currentTime;
|
||||||
|
|
||||||
|
return (cpuDelta / timeDelta) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getFallbackCPUUsage();
|
||||||
|
} catch (error) {
|
||||||
|
return this.getFallbackCPUUsage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFallbackCPUUsage(): number {
|
||||||
try {
|
try {
|
||||||
// Get CPU usage percentage directly from system
|
|
||||||
const cpus = os.cpus();
|
const cpus = os.cpus();
|
||||||
const cpuUsage = cpus.reduce((acc, cpu) => {
|
return cpus.reduce((acc, cpu) => {
|
||||||
const total = Object.values(cpu.times).reduce((a, b) => a + b);
|
const total = Object.values(cpu.times).reduce((a, b) => a + b);
|
||||||
const idle = cpu.times.idle;
|
const idle = cpu.times.idle;
|
||||||
return acc + ((total - idle) / total) * 100;
|
return acc + ((total - idle) / total) * 100;
|
||||||
}, 0) / cpus.length;
|
}, 0) / cpus.length;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting fallback CPU usage:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const memoryUsage = process.memoryUsage();
|
private getCgroupMemoryUsage(): { used: number; limit: number } | null {
|
||||||
|
try {
|
||||||
|
// First get process memory as baseline
|
||||||
|
const processMemory = process.memoryUsage();
|
||||||
|
const baselineMemory = processMemory.rss;
|
||||||
|
|
||||||
|
if (this.cgroupv2) {
|
||||||
|
const memUsage = Number(this.readCgroupFile('/sys/fs/cgroup/memory.current'));
|
||||||
|
if (!isNaN(memUsage) && memUsage > 0) {
|
||||||
|
return {
|
||||||
|
used: Math.max(baselineMemory, memUsage),
|
||||||
|
limit: this.totalSystemMemory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try cgroup v1
|
||||||
|
const v1Paths = {
|
||||||
|
usage: '/sys/fs/cgroup/memory/memory.usage_in_bytes',
|
||||||
|
limit: '/sys/fs/cgroup/memory/memory.limit_in_bytes'
|
||||||
|
};
|
||||||
|
|
||||||
|
const memoryUsage = Number(this.readCgroupFile(v1Paths.usage));
|
||||||
|
if (!isNaN(memoryUsage) && memoryUsage > 0) {
|
||||||
|
return {
|
||||||
|
used: Math.max(baselineMemory, memoryUsage),
|
||||||
|
limit: this.totalSystemMemory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to process memory
|
||||||
|
return {
|
||||||
|
used: baselineMemory,
|
||||||
|
limit: this.totalSystemMemory
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.debug('Error reading cgroup memory:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMetrics() {
|
||||||
|
try {
|
||||||
const mbFactor = 1024 * 1024;
|
const mbFactor = 1024 * 1024;
|
||||||
|
let memoryData = this.getCgroupMemoryUsage();
|
||||||
|
|
||||||
|
if (!memoryData) {
|
||||||
|
const processMemory = process.memoryUsage();
|
||||||
|
memoryData = {
|
||||||
|
used: processMemory.rss,
|
||||||
|
limit: this.totalSystemMemory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoryUsedMB = memoryData.used / mbFactor;
|
||||||
|
const memoryTotalMB = memoryData.limit / mbFactor;
|
||||||
|
const memoryPercentage = (memoryData.used / memoryData.limit) * 100;
|
||||||
|
|
||||||
|
console.debug(`
|
||||||
|
Memory Usage: ${memoryUsedMB.toFixed(2)} MB
|
||||||
|
Memory Total: ${memoryTotalMB.toFixed(2)} MB
|
||||||
|
Memory %: ${memoryPercentage.toFixed(2)}%
|
||||||
|
`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
memoryUsedMB: (memoryUsage.rss / mbFactor).toFixed(2),
|
memoryUsedMB: memoryUsedMB.toFixed(2),
|
||||||
memoryUsedPercentage: (
|
memoryUsedPercentage: memoryPercentage.toFixed(2),
|
||||||
(memoryUsage.rss / this.totalSystemMemory) * 100
|
cpuUsedPercentage: this.getCgroupCPUUsage().toFixed(2)
|
||||||
).toFixed(2),
|
|
||||||
cpuUsedPercentage: cpuUsage.toFixed(2)
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting container metrics:", error);
|
console.error("Error getting metrics:", error);
|
||||||
throw error;
|
return {
|
||||||
|
memoryUsedMB: "0",
|
||||||
|
memoryUsedPercentage: "0",
|
||||||
|
cpuUsedPercentage: "0"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export class Watcher extends RoomParticipant {
|
||||||
try {
|
try {
|
||||||
this.socket.emit("get-usage");
|
this.socket.emit("get-usage");
|
||||||
this.socket.once("usage-data", (data) => {
|
this.socket.once("usage-data", (data) => {
|
||||||
|
//console.log(`Watcher ${this.username} received data:`, data);
|
||||||
this.roomRessourcesData.push({ timestamp: Date.now(), ...data });
|
this.roomRessourcesData.push({ timestamp: Date.now(), ...data });
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ import generateMetricsReport from './utility/metrics_generator.js';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
baseUrl: process.env.BASE_URL || 'http://msevignyl.duckdns.org',
|
baseUrl: process.env.BASE_URL || 'http://localhost',
|
||||||
auth: {
|
auth: {
|
||||||
username: process.env.USER_EMAIL || 'admin@admin.com',
|
username: process.env.USER_EMAIL || 'admin@admin.com',
|
||||||
password: process.env.USER_PASSWORD || 'admin'
|
password: process.env.USER_PASSWORD || 'admin'
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
count: parseInt(process.env.NUMBER_ROOMS || '5'),
|
count: parseInt(process.env.NUMBER_ROOMS || '2'),
|
||||||
usersPerRoom: parseInt(process.env.USERS_PER_ROOM || '60'),
|
usersPerRoom: parseInt(process.env.USERS_PER_ROOM || '60'),
|
||||||
batchSize: 5,
|
batchSize: 5,
|
||||||
batchDelay: 250
|
batchDelay: 250
|
||||||
|
|
@ -178,8 +178,6 @@ async function main() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
metrics.logError('main', error);
|
metrics.logError('main', error);
|
||||||
console.error('Error:', error.message);
|
console.error('Error:', error.message);
|
||||||
} finally {
|
|
||||||
cleanup();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue