feat(ops): self-heal disk pressure instead of only alerting
The 5-minute disk probe now reclaims storage automatically: from 85% it runs the gentle age-windowed Docker prune, from 90% it drops the age windows (docker-prune.sh --force: all unused build cache and unreferenced images, all stopped containers) so a mount can never silently max out. Alerts still fire at 85/90/95% and their hint now points at non-Docker growth when reclaiming is not enough. Force mode is reserved for the worker; deploys keep the gentle mode. Volumes are off-limits in every path.
This commit is contained in:
1 parent
fe26ca3ff3
commit
1caef76f82
4 files changed
+73
-18
No files matched your search
+36
-4
@@ -17,7 +17,11 @@ import {
|
||||
healthDegraded,
|
||||
} from "../src/lib/services/alert";
|
||||
import { runCatalogExport } from "../src/lib/services/catalog-git-export";
|
||||
import { diskLevel, parseDfOutput } from "../src/lib/services/disk-usage";
|
||||
import {
|
||||
DISK_THRESHOLDS,
|
||||
diskLevel,
|
||||
parseDfOutput,
|
||||
} from "../src/lib/services/disk-usage";
|
||||
import { invalidateNewsCache } from "../src/lib/services/news-cache";
|
||||
import { rcon } from "../src/lib/services/rcon";
|
||||
|
||||
@@ -133,6 +137,30 @@ async function checkDiskUsage(): Promise<void> {
|
||||
});
|
||||
await diskPressure(usage);
|
||||
}
|
||||
|
||||
// Self-healing: never let a mount max out. Safe prune from the warning
|
||||
// mark, forced prune (drop age windows) from the error mark. Cooldown is
|
||||
// keyed separately so a stuck fill level does not re-prune every run
|
||||
// while still escalating to the forced path on real pressure.
|
||||
if (usage.percent >= DISK_THRESHOLDS.error) {
|
||||
if (canAlert(`disk-prune-force-${usage.mount}`)) {
|
||||
logger.warn("Disk near full — forcing Docker cache reclaim", {
|
||||
module: "jobs",
|
||||
mount: usage.mount,
|
||||
percent: usage.percent,
|
||||
});
|
||||
await pruneDockerCache(true);
|
||||
}
|
||||
} else if (usage.percent >= DISK_THRESHOLDS.warning) {
|
||||
if (canAlert(`disk-prune-${usage.mount}`)) {
|
||||
logger.info("Disk at high water mark — running safe Docker prune", {
|
||||
module: "jobs",
|
||||
mount: usage.mount,
|
||||
percent: usage.percent,
|
||||
});
|
||||
await pruneDockerCache(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,8 +310,10 @@ async function cleanupOldSessions(): Promise<void> {
|
||||
|
||||
/** Host-side: reclaim Docker's unused cache (build cache, unreferenced images,
|
||||
* stopped containers). Volumes and in-use images are never touched. No-op when
|
||||
* docker or the prune script is unavailable. */
|
||||
async function pruneDockerCache(): Promise<void> {
|
||||
* docker or the prune script is unavailable. With force=true the age windows
|
||||
* are dropped (docker-prune.sh --force) so every unused byte is reclaimed —
|
||||
* the emergency path for a nearly-full disk. */
|
||||
async function pruneDockerCache(force = false): Promise<void> {
|
||||
const { access } = await import("node:fs/promises");
|
||||
const { resolve } = await import("node:path");
|
||||
const { spawn } = await import("node:child_process");
|
||||
@@ -294,7 +324,9 @@ async function pruneDockerCache(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolvePromise) => {
|
||||
const child = spawn("bash", [script], { stdio: "ignore" });
|
||||
const child = spawn("bash", [script, ...(force ? ["--force"] : [])], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
child.on("error", (err) =>
|
||||
captureWorkerError(
|
||||
err,
|
||||
|
||||
Reference in new issue
Block a user