F1 (rework): source guest RAM cap from docker info; deploy guard uses committed memory
The cgroup-only approach was a no-op on the demo: the controller container's OWN cgroup is unlimited (the 2GB cap is on the LXC ancestor, hidden), and /proc has no lxcfs, so it kept reporting the host's 16GB. The Docker daemon runs IN the LXC, so 'docker info' MemTotal reports the guest's real cap (2048MB) — now the authoritative source (cgroup limit preferred when present, e.g. non-nested). The deploy memory guard now uses the controller's committed-app memory (sum of running mem requests) for 'used' — accurate and cheap — instead of host /proc RSS (unobservable per guest; would make the guard never/always fire). /api/system/info reports the guest cap as total and committed memory as used. Tests: cgroup-limit path, docker-info fallback (nested case), GuestMemTotalMB fallback (dockerMemTotalFn stub).
This commit is contained in:
@@ -160,16 +160,28 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) {
|
||||
var deployWarning string
|
||||
reservedMB := m.cfg.System.ReservedMemoryMB
|
||||
totalMB, usedMB, memErr := system.GetMemoryMB()
|
||||
// F1: the controller container cannot read the guest's RAM cap from /proc (no lxcfs) or its own
|
||||
// cgroup (the cap is on the LXC ancestor). Prefer the guest cap from the Docker daemon (runs in the
|
||||
// LXC). And use the controller's OWN committed-memory accounting for "used" — accurate and cheap —
|
||||
// rather than host /proc RSS, which is unobservable-per-guest and would otherwise make this guard
|
||||
// either never fire (host total) or always fire (host used > guest cap).
|
||||
if gt, ok := system.GuestMemTotalMB(); ok && gt > 0 {
|
||||
totalMB = gt
|
||||
memErr = nil
|
||||
}
|
||||
if committedReqMB, _ := m.CommittedMemory(); committedReqMB > 0 || memErr == nil {
|
||||
usedMB = committedReqMB
|
||||
}
|
||||
if memErr != nil {
|
||||
m.logger.Printf("[WARN] [stacks] Cannot read system memory: %v — skipping memory check", memErr)
|
||||
} else {
|
||||
usableMB := totalMB - reservedMB
|
||||
newReqMB := ParseMemoryMB(meta.Resources.MemRequest)
|
||||
|
||||
m.logger.Printf("[INFO] [stacks] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB",
|
||||
m.logger.Printf("[INFO] [stacks] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, committed_used=%dMB, new_req=%dMB, remaining=%dMB",
|
||||
totalMB, reservedMB, usableMB, usedMB, newReqMB, usableMB-usedMB-newReqMB)
|
||||
|
||||
// Hard block: real used + new request exceeds usable memory
|
||||
// Hard block: committed + new request exceeds usable memory
|
||||
if newReqMB > 0 && usedMB+newReqMB > usableMB {
|
||||
clearDeploying()
|
||||
return "", fmt.Errorf(
|
||||
|
||||
Reference in New Issue
Block a user