diff --git a/src/backend/src/docker.js b/src/backend/src/docker.js index f01a55d..d556a03 100644 --- a/src/backend/src/docker.js +++ b/src/backend/src/docker.js @@ -220,29 +220,102 @@ function getServerMetrics() { } } catch(e) {} - // RAM : lecture de /proc/meminfo (valeurs réelles via lxcfs) - // Formule identique à `free` : used = total - free - buffers - cached - // Cela exclut le cache disque du noyau qui est libérable à tout moment - let memTotal = 8589934592; // 8 Go par défaut + // RAM : lecture robuste multi-source + // Priorité 1 : /host/proc/meminfo (hôte LXC monté via volume docker-compose) + // Priorité 2 : /proc/meminfo si MemTotal <= 64 Go (lxcfs ou VPS direct) + // Priorité 3 : Docker API MemTotal + ratio /proc/meminfo du nœud physique + let memTotal = 0; let memFree = 0, memAvailable = 0, memUsed = 0, memBuffers = 0, memCached = 0; + + // Fonction de lecture de meminfo depuis un chemin donné + const readMeminfo = (procPath) => { + const raw = fs.readFileSync(procPath + '/meminfo', 'utf8'); + const map = {}; + raw.split('\n').forEach(line => { + const idx = line.indexOf(':'); + if (idx > 0) { + const key = line.substring(0, idx).trim(); + const rest = line.substring(idx + 1).trim(); + const val = parseInt(rest.split(' ')[0]); + if (Number.isFinite(val)) map[key] = val * 1024; + } + }); + return map; + }; + try { - const meminfo = fs.readFileSync('/proc/meminfo', 'utf8'); - const getVal = (key) => { - const m = meminfo.match(new RegExp(`^${key}:\s+(\d+)`, 'm')); - return m ? parseInt(m[1]) * 1024 : 0; - }; - memTotal = getVal('MemTotal'); - memFree = getVal('MemFree'); - memAvailable = getVal('MemAvailable'); - memBuffers = getVal('Buffers'); - // SReclaimable est la partie du slab cache récupérable (inclus dans Cached par free) - const sReclaimable = getVal('SReclaimable'); - const shmem = getVal('Shmem'); - memCached = getVal('Cached') + (sReclaimable > shmem ? sReclaimable - shmem : 0); - // RAM utilisée = total - libre - buffers - cache (= RAM applicative réelle) - memUsed = memTotal - memFree - memBuffers - memCached; - if (memUsed < 0) memUsed = memTotal - memAvailable; // fallback si lxcfs ne fournit pas Buffers/Cached - } catch(e) {} + let memMap = null; + let sourceLabel = ''; + + // Priorité 1 : /host/proc monté depuis l'hôte LXC + if (fs.existsSync('/host/proc/meminfo')) { + try { + const hostMap = readMeminfo('/host/proc'); + if (hostMap['MemTotal'] > 0 && hostMap['MemTotal'] <= 64 * 1024 * 1024 * 1024) { + memMap = hostMap; + sourceLabel = 'host/proc'; + } + } catch(e) {} + } + + // Priorité 2 : /proc direct si MemTotal <= 64 Go + if (!memMap) { + try { + const procMap = readMeminfo('/proc'); + if (procMap['MemTotal'] > 0 && procMap['MemTotal'] <= 64 * 1024 * 1024 * 1024) { + memMap = procMap; + sourceLabel = '/proc direct'; + } + } catch(e) {} + } + + // Priorité 3 : Docker API MemTotal + ratio du nœud physique + if (!memMap) { + try { + const dockerInfoRaw = execSync( + 'curl -s --unix-socket /var/run/docker.sock http://localhost/info', + { timeout: 3000 } + ).toString(); + const dockerInfo = JSON.parse(dockerInfoRaw); + const dockerMemTotal = dockerInfo.MemTotal || 0; + if (dockerMemTotal > 0) { + // Lire le ratio d'utilisation depuis /proc du nœud physique + const nodeMap = readMeminfo('/proc'); + const nodeTotal = nodeMap['MemTotal'] || 1; + const nodeAvail = nodeMap['MemAvailable'] || 0; + const usageRatio = (nodeTotal - nodeAvail) / nodeTotal; + // Appliquer ce ratio à la vraie RAM du VPS + memTotal = dockerMemTotal; + memUsed = Math.round(memTotal * usageRatio); + memFree = memTotal - memUsed; + memAvailable = memFree; + sourceLabel = 'docker-api+ratio'; + } + } catch(e) {} + } + + // Calculer les valeurs finales si memMap disponible + if (memMap) { + memTotal = memMap['MemTotal'] || 0; + memFree = memMap['MemFree'] || 0; + memAvailable = memMap['MemAvailable'] || 0; + memBuffers = memMap['Buffers'] || 0; + 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; + } + } catch(e) { + // Fallback ultime + try { + const m = readMeminfo('/proc'); + memTotal = m['MemTotal'] || 0; + memFree = m['MemFree'] || 0; + memAvailable = m['MemAvailable'] || 0; + memUsed = memTotal - memAvailable; + } catch(e2) {} + } // Disque : df sur la racine du conteneur (= hôte car pas de volume overlay) let diskTotal = 0, diskUsed = 0, diskFree = 0; diff --git a/src/docker-compose.yml b/src/docker-compose.yml index ab1fa56..7c1403e 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -26,6 +26,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - /opt/manus-deploy:/opt/manus-deploy + - /proc:/host/proc:ro networks: - web labels: