diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a9426..34b5e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ ## Changelog +### v0.27.3 — Real System Memory Everywhere (2026-02-23) + +#### Changed +- **Deploy page uses real system memory** — Memory bar now shows actual `/proc/meminfo` usage instead of declared `mem_request` sums. Labels changed from "Jelenlegi foglalás" to "Jelenlegi használat". `system.GetMemoryMB()` provides real-time total and used memory. +- **Pre-start memory check uses real memory** — `actionStack("start")` in `router.go` and `DeployStack()` in `deploy.go` now check real used memory (`usedMB + newReqMB > usableMB`) instead of declared committed sums. `CommittedMemory()` kept only for soft overcommit warnings. + +#### Added +- **`system.GetMemoryMB()` helper** — Lightweight function in `internal/system/info_linux.go` that returns real total and used memory from `/proc/meminfo` without the overhead of full `GetInfo()` (no disk/CPU/temp). Stub in `info_other.go` for non-Linux. +- **Monitoring page memory distribution bar** — New stacked bar on `/monitoring` showing per-container memory usage (colored segments), OS/system overhead (gray), and free memory. Built dynamically from container summary data + real-time `/api/system/info`. Color-coded legend with per-app labels. + ### v0.27.2 — Comprehensive Fixes and New Labels (2026-02-23) #### Fixed diff --git a/controller/README.md b/controller/README.md index 52636f6..3aae6ee 100644 --- a/controller/README.md +++ b/controller/README.md @@ -136,11 +136,12 @@ The app catalog lives in a separate Git repository. The controller: - User-configurable inputs (admin password, language, storage path) remain editable - Section header prompts the user to note down any passwords they need 3. `checkBeforeDeploy()` JS guard fetches live state first (prevents double-deploy from another tab) -4. **Memory validation** checks `mem_request` against available RAM: +4. **Memory validation** uses real system memory from `/proc/meminfo`: - `usable_memory = total_ram - reserved_memory_mb` (default 384MB reserved) - - `CommittedMemory()` only counts running/starting/unhealthy apps — stopped/exited apps are excluded (they don't consume RAM) - - Hard block if requests exceed usable memory - - Soft warning if limits exceed total RAM (overcommit OK) + - `system.GetMemoryMB()` returns real-time total and used memory (not declared reservations) + - Hard block if `used_mb + new_request > usable_memory` + - `CommittedMemory()` (declared sum) still used for soft overcommit warning only + - Deploy page shows real memory usage bar (not declared reservations) 5. Pre-generated secret values are submitted as hidden form inputs so the **same values** the user saw are saved to `app.yaml` (no silent re-generation on submit). Controller saves `app.yaml`, sets in-memory `Deployed` flag **before** `docker compose up -d` (avoids stale UI during slow image pulls), reverts on failure 6. 3-step progress panel polls `GET /api/stacks/{name}` every 3s: config saved → containers starting → health check passed 7. Post-deploy: locked fields (DB_PASSWORD, etc.) become read-only; the "Automatikusan generált értékek" section continues to show the saved values on the settings page @@ -560,6 +561,7 @@ Legacy pinger (`internal/monitor/pinger.go`) still runs for backward compatibili Full-page system monitor at `/monitoring`: - **System Overview**: hostname, OS, kernel, CPU model/cores, uptime - **System Metrics Charts**: 4 line charts (CPU, Memory, Temperature, Load) in 2x2 grid +- **Memory Distribution Bar**: stacked bar showing per-container memory usage, OS/system overhead, and free memory (real-time from `/proc/meminfo` + container stats) - **Container Resources**: horizontal bar charts (CPU% and Memory per container) - **Per-container Detail**: click-to-expand historical charts - **Hub Connection Status**: shows Hub URL, customer ID, connection state (connected/unreachable), last successful push, last error diff --git a/controller/internal/api/router.go b/controller/internal/api/router.go index b7ce56a..41c391d 100644 --- a/controller/internal/api/router.go +++ b/controller/internal/api/router.go @@ -334,15 +334,14 @@ func (r *Router) actionStack(w http.ResponseWriter, action, name string) { if action == "start" { stackMemMB := r.stackMgr.StackMemoryMB(name) if stackMemMB > 0 { - if totalMB, memErr := system.GetTotalMemoryMB(); memErr == nil { + if totalMB, usedMB, memErr := system.GetMemoryMB(); memErr == nil { reservedMB := r.cfg.System.ReservedMemoryMB usableMB := totalMB - reservedMB - committedReqMB, _ := r.stackMgr.CommittedMemory() - afterMB := committedReqMB + stackMemMB + afterMB := usedMB + stackMemMB if afterMB > usableMB { writeJSON(w, http.StatusConflict, apiResponse{ OK: false, - Error: fmt.Sprintf("Nincs elég memória az indításhoz. Szükséges: %d MB, elérhető: %d MB (foglalt: %d MB / használható: %d MB)", stackMemMB, usableMB-committedReqMB, committedReqMB, usableMB), + Error: fmt.Sprintf("Nincs elég memória az indításhoz. Szükséges: %d MB, elérhető: %d MB (használt: %d MB / használható: %d MB)", stackMemMB, usableMB-usedMB, usedMB, usableMB), }) return } diff --git a/controller/internal/stacks/deploy.go b/controller/internal/stacks/deploy.go index 947b91a..8c33fcd 100644 --- a/controller/internal/stacks/deploy.go +++ b/controller/internal/stacks/deploy.go @@ -123,33 +123,33 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) { // --- Memory validation --- var deployWarning string reservedMB := m.cfg.System.ReservedMemoryMB - totalMB, memErr := system.GetTotalMemoryMB() + totalMB, usedMB, memErr := system.GetMemoryMB() if memErr != nil { m.logger.Printf("[WARN] Cannot read system memory: %v — skipping memory check", memErr) } else { usableMB := totalMB - reservedMB - currentReqMB, currentLimitMB := m.CommittedMemory() newReqMB := ParseMemoryMB(meta.Resources.MemRequest) - newLimitMB := ParseMemoryMB(meta.Resources.MemLimit) - m.logger.Printf("[INFO] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, committed_req=%dMB, new_req=%dMB, remaining=%dMB", - totalMB, reservedMB, usableMB, currentReqMB, newReqMB, usableMB-currentReqMB-newReqMB) + m.logger.Printf("[INFO] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB", + totalMB, reservedMB, usableMB, usedMB, newReqMB, usableMB-usedMB-newReqMB) - // Hard block: requests exceed usable memory - if newReqMB > 0 && currentReqMB+newReqMB > usableMB { + // Hard block: real used + new request exceeds usable memory + if newReqMB > 0 && usedMB+newReqMB > usableMB { return "", fmt.Errorf( "Nincs elég memória az alkalmazás telepítéséhez. "+ "Szükséges: %d MB, Elérhető: %d MB "+ - "(összesen: %d MB, ebből %d MB már foglalt, %d MB rendszer számára fenntartva)", + "(összesen: %d MB, ebből %d MB használt, %d MB rendszer számára fenntartva)", newReqMB, - usableMB-currentReqMB, + usableMB-usedMB, totalMB, - currentReqMB, + usedMB, reservedMB, ) } // Soft warning: limits exceed total (overcommit) + _, currentLimitMB := m.CommittedMemory() + newLimitMB := ParseMemoryMB(meta.Resources.MemLimit) if newLimitMB > 0 && currentLimitMB+newLimitMB > totalMB { deployWarning = "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. " + "Normál használat mellett ez nem okoz problémát." diff --git a/controller/internal/system/info_linux.go b/controller/internal/system/info_linux.go index 4de1045..00be761 100644 --- a/controller/internal/system/info_linux.go +++ b/controller/internal/system/info_linux.go @@ -54,6 +54,16 @@ func GetTotalMemoryMB() (int, error) { return int(info.TotalMemMB), nil } +// GetMemoryMB returns total and used system memory in MB from /proc/meminfo. +func GetMemoryMB() (totalMB, usedMB int, err error) { + info := SystemInfo{} + readMemInfo(&info) + if info.TotalMemMB == 0 { + return 0, 0, fmt.Errorf("could not read MemTotal from /proc/meminfo") + } + return int(info.TotalMemMB), int(info.UsedMemMB), nil +} + func readMemInfo(info *SystemInfo) { f, err := os.Open("/proc/meminfo") if err != nil { diff --git a/controller/internal/system/info_other.go b/controller/internal/system/info_other.go index c8151eb..feccb79 100644 --- a/controller/internal/system/info_other.go +++ b/controller/internal/system/info_other.go @@ -13,3 +13,8 @@ func GetInfo(_ string, _ *CPUCollector) SystemInfo { func GetTotalMemoryMB() (int, error) { return 0, fmt.Errorf("/proc/meminfo not available on this platform") } + +// GetMemoryMB is not available on non-Linux platforms. +func GetMemoryMB() (totalMB, usedMB int, err error) { + return 0, 0, fmt.Errorf("/proc/meminfo not available on this platform") +} diff --git a/controller/internal/web/handlers.go b/controller/internal/web/handlers.go index feeb407..2cf6f90 100644 --- a/controller/internal/web/handlers.go +++ b/controller/internal/web/handlers.go @@ -354,35 +354,36 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri // Memory info for deploy page (only for non-deployed apps) if !alreadyDeployed { memInfo := map[string]interface{}{"Available": false} - totalMB, memErr := system.GetTotalMemoryMB() + totalMB, usedMB, memErr := system.GetMemoryMB() if memErr == nil { reservedMB := s.cfg.System.ReservedMemoryMB usableMB := totalMB - reservedMB - committedReqMB, committedLimitMB := s.stackMgr.CommittedMemory() newReqMB := stacks.ParseMemoryMB(meta.Resources.MemRequest) - newLimitMB := stacks.ParseMemoryMB(meta.Resources.MemLimit) - afterReqMB := committedReqMB + newReqMB - afterLimitMB := committedLimitMB + newLimitMB + afterMB := usedMB + newReqMB percent := 0 if usableMB > 0 { - percent = afterReqMB * 100 / usableMB + percent = afterMB * 100 / usableMB + } + usedPercent := 0 + if usableMB > 0 { + usedPercent = usedMB * 100 / usableMB } - committedPercent := 0 - if usableMB > 0 { - committedPercent = committedReqMB * 100 / usableMB - } + // Overcommit warning still uses declared limits + _, committedLimitMB := s.stackMgr.CommittedMemory() + newLimitMB := stacks.ParseMemoryMB(meta.Resources.MemLimit) + afterLimitMB := committedLimitMB + newLimitMB memInfo["Available"] = true memInfo["TotalMB"] = totalMB memInfo["ReservedMB"] = reservedMB memInfo["UsableMB"] = usableMB - memInfo["CommittedMB"] = committedReqMB + memInfo["UsedMB"] = usedMB memInfo["NewRequestMB"] = newReqMB - memInfo["AfterMB"] = afterReqMB + memInfo["AfterMB"] = afterMB memInfo["Percent"] = percent - memInfo["CommittedPercent"] = committedPercent - memInfo["Blocked"] = newReqMB > 0 && afterReqMB > usableMB + memInfo["UsedPercent"] = usedPercent + memInfo["Blocked"] = newReqMB > 0 && afterMB > usableMB memInfo["OvercommitWarn"] = newLimitMB > 0 && afterLimitMB > totalMB } data["MemoryInfo"] = memInfo diff --git a/controller/internal/web/templates/deploy.html b/controller/internal/web/templates/deploy.html index 86b31db..8d9e729 100644 --- a/controller/internal/web/templates/deploy.html +++ b/controller/internal/web/templates/deploy.html @@ -204,15 +204,15 @@ {{else}}