feat: card RAM par conteneur — live, avec seuil configurable
Nouvel endpoint /api/dockermem (Docker API stats en parallèle sur tous les conteneurs, ~2s pour 68 conteneurs). Carte affichant jusqu'à 20 conteneurs triés par RAM utilisée décroissante, filtrés par un seuil en Mo réglable dans l'interface (persisté en localStorage).
This commit is contained in:
+42
@@ -1,5 +1,6 @@
|
||||
import subprocess, json, time, re, os, http.client, socket as _socket
|
||||
from typing import Optional
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import psutil
|
||||
import cpuinfo
|
||||
@@ -723,4 +724,45 @@ def api_dockernet():
|
||||
return {"networks": result, "ts": int(now)}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────── DOCKERMEM ──
|
||||
|
||||
def _container_mem(c: dict) -> Optional[dict]:
|
||||
"""RAM réellement utilisée par un conteneur (usage - cache, comme `docker stats`)."""
|
||||
cid = c.get("Id")
|
||||
name = c.get("Names", [""])[0].lstrip("/")
|
||||
try:
|
||||
stats = _docker_api(f"/containers/{cid}/stats?stream=false")
|
||||
mem = stats.get("memory_stats", {})
|
||||
usage = mem.get("usage", 0)
|
||||
sub = mem.get("stats", {}).get("inactive_file", mem.get("stats", {}).get("cache", 0))
|
||||
used = max(0, usage - sub)
|
||||
limit = mem.get("limit", 0)
|
||||
return {
|
||||
"name": name,
|
||||
"mem_bytes": used,
|
||||
"mem_limit": limit,
|
||||
"mem_pct": (used / limit * 100) if limit else 0,
|
||||
}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
@app.get("/api/dockermem")
|
||||
def api_dockermem():
|
||||
try:
|
||||
containers = _docker_api("/containers/json")
|
||||
except Exception:
|
||||
return {"containers": [], "ts": int(time.time())}
|
||||
|
||||
# Docker échantillonne cpu/precpu à ~1s d'intervalle même avec stream=false
|
||||
# (le endpoint stats renvoie toujours les deux) : avec 60+ conteneurs il faut
|
||||
# tout lancer en parallèle (pas de petit pool) pour rester sous quelques secondes.
|
||||
with ThreadPoolExecutor(max_workers=max(1, len(containers))) as ex:
|
||||
results = list(ex.map(_container_mem, containers))
|
||||
|
||||
result = [r for r in results if r]
|
||||
result.sort(key=lambda x: x["mem_bytes"], reverse=True)
|
||||
return {"containers": result, "ts": int(time.time())}
|
||||
|
||||
|
||||
app.mount("/", StaticFiles(directory="/app/static", html=True), name="static")
|
||||
|
||||
Reference in New Issue
Block a user