diff --git a/src/backend/src/docker.js b/src/backend/src/docker.js index d556a03..3245e27 100644 --- a/src/backend/src/docker.js +++ b/src/backend/src/docker.js @@ -183,9 +183,26 @@ function getServerMetrics() { const { execSync } = require("child_process"); const fs = require("fs"); try { - // Lecture directe de /proc (monté depuis l'hôte via le namespace PID du conteneur) - // Pas besoin de nsenter : /proc dans le conteneur reflète l'hôte + // ===== Méthode 1 : Lire host-metrics.json généré par le script système de l'hôte ===== + // Ce fichier est mis à jour toutes les 30s par /opt/manus-deploy/host-metrics.sh + // Il contient les vraies métriques de l'hôte LXC (pas du nœud physique) + const hostMetricsPath = '/opt/manus-deploy/host-metrics.json'; + if (fs.existsSync(hostMetricsPath)) { + try { + const raw = fs.readFileSync(hostMetricsPath, 'utf8'); + const hostMetrics = JSON.parse(raw); + // Vérifier que le fichier est récent (moins de 2 minutes) + const fileAge = Date.now() - new Date(hostMetrics.timestamp).getTime(); + if (fileAge < 120000 && hostMetrics.memory && hostMetrics.memory.total > 0) { + return resolve({ + ...hostMetrics, + timestamp: new Date().toISOString() + }); + } + } catch(e) {} + } + // ===== Méthode 2 : Lecture directe de /proc (fallback) ===== // CPU : mesure via cgroup v2 usage_usec (méthode précise pour conteneur LXC) // Fallback sur /proc/stat normalisé par nproc si cgroup v2 non disponible let cpuUsage = 0; @@ -303,8 +320,10 @@ function getServerMetrics() { const sReclaimable = memMap['SReclaimable'] || 0; const shmem = memMap['Shmem'] || 0; memCached = (memMap['Cached'] || 0) + (sReclaimable > shmem ? sReclaimable - shmem : 0); - memUsed = memTotal - memFree - memBuffers - memCached; - if (memUsed < 0) memUsed = memTotal - memAvailable; + // Utiliser MemAvailable comme référence car elle inclut les caches libérables + // Identique à ce que `free` affiche dans la colonne 'disponible' + memUsed = memTotal - memAvailable; + if (memUsed < 0) memUsed = 0; } } catch(e) { // Fallback ultime @@ -317,19 +336,22 @@ function getServerMetrics() { } catch(e2) {} } - // Disque : df sur la racine du conteneur (= hôte car pas de volume overlay) + // Disque : df sur /opt/manus-deploy (point de montage de l'hôte LXC) + // Utiliser /opt/manus-deploy car c'est un volume monté depuis l'hôte let diskTotal = 0, diskUsed = 0, diskFree = 0; try { - const diskRaw = execSync('df -B1 / | tail -1').toString().trim().split(/\s+/); + const diskPath = fs.existsSync('/opt/manus-deploy') ? '/opt/manus-deploy' : '/'; + const diskRaw = execSync('df -B1 ' + diskPath + ' | tail -1').toString().trim().split(/\s+/); diskTotal = parseInt(diskRaw[1]); diskUsed = parseInt(diskRaw[2]); diskFree = parseInt(diskRaw[3]); } catch(e) {} - // Uptime et load - const uptimeRaw = fs.readFileSync('/proc/uptime', 'utf8').trim().split(' '); + // Uptime et load : utiliser /host/proc si disponible (hôte LXC réel) + const procBase = fs.existsSync('/host/proc/uptime') ? '/host/proc' : '/proc'; + const uptimeRaw = fs.readFileSync(procBase + '/uptime', 'utf8').trim().split(' '); const uptimeSeconds = parseFloat(uptimeRaw[0]); - const loadRaw = fs.readFileSync('/proc/loadavg', 'utf8').trim().split(' '); + const loadRaw = fs.readFileSync(procBase + '/loadavg', 'utf8').trim().split(' '); const load1 = parseFloat(loadRaw[0]); const load5 = parseFloat(loadRaw[1]); const load15 = parseFloat(loadRaw[2]); @@ -337,7 +359,8 @@ function getServerMetrics() { // Réseau let netRx = 0, netTx = 0; try { - const netDev = fs.readFileSync('/proc/net/dev', 'utf8'); + const netDevPath = fs.existsSync('/host/proc/net/dev') ? '/host/proc/net/dev' : '/proc/net/dev'; + const netDev = fs.readFileSync(netDevPath, 'utf8'); const netLine = netDev.split('\n').find(l => /eth0|ens|enp/.test(l)); if (netLine) { const parts = netLine.trim().split(/\s+/);