Compare commits

..
Author SHA1 Message Date
notplants eb675e6e10 fix: reclaim unused images during critical disk pressure
continuous-integration/drone/push Build is passing
2026-11-02 17:56:11 +00:00
autonomic-bot a8b836ed1d Merge pull request 'enroll(rustdesk-server): test suite + bridge poll' (#48) from test/rustdesk-server-enroll-20261030 into main
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Merge pull request enroll(rustdesk-server): test suite + bridge poll

rustdesk-server enrolled: status-key health overlay (sidecar serving the real hbbs public key), rendezvous ports + ws handshake + identity-key + punch-hole-amplification tests. Cold full-suite green on recipe main (punch-hole correctly skipped on 1.1.12) and on the upgrade head upgrade-2e65135 (passed actively on 1.1.16). Bridge polls recipe-maintainers/rustdesk-server.
2026-10-30 21:15:58 +00:00
2 changed files with 19 additions and 13 deletions
+4 -3
View File
@@ -86,9 +86,10 @@ back cleanly to a full cold run (the PR is still tested).
skips if a `run_recipe_ci.py` is active. At most MAX_TESTS apps are ever live at once. skips if a `run_recipe_ci.py` is active. At most MAX_TESTS apps are ever live at once.
- **Warm keycloak shared safely** via per-run namespaced realms (above); orphan realms reaped. - **Warm keycloak shared safely** via per-run namespaced realms (above); orphan realms reaped.
- **Disk** (warm is the budget, not RAM): the `ci-docker-prune` unit (`nix/modules/docker-prune.nix`, - **Disk** (warm is the budget, not RAM): the `ci-docker-prune` unit (`nix/modules/docker-prune.nix`,
Phase-2pc) prunes only **dangling** images/containers/build-cache (`until=24h`), and only under Phase-2pc) prunes dangling images/containers/build-cache (`until=24h`) under
genuine disk pressure (`/` ≥ 80%) with nothing in flight — **never `--all`** (keeps cached base/ disk pressure (`/` ≥ 80%) when no service is converging. At `/` ≥ 90%, it also removes unused
in-use images warm; the local store IS the cache on this single host) and **never `--volumes`** (so images older than seven days, even if a warm service is crash-looping. It skips while a run-app
stack is live and never uses `--volumes` (so
data-warm canonical volumes survive). Each canonical = one data volume + one snapshot (small; the data-warm canonical volumes survive). Each canonical = one data volume + one snapshot (small; the
keycloak DB snapshot ~300M dominates). `canonical.prune_stale()` (run nightly) drops warm data for keycloak DB snapshot ~300M dominates). `canonical.prune_stale()` (run nightly) drops warm data for
**de-enrolled** canonicals. Monitor with `df -h /` (the nightly logs it). **de-enrolled** canonicals. Monitor with `df -h /` (the nightly logs it).
+15 -10
View File
@@ -5,24 +5,21 @@
# between CI runs no test apps run, so it evicted the cached recipe base images and forced a cold # between CI runs no test apps run, so it evicted the cached recipe base images and forced a cold
# re-pull on the next run → the prune->re-pull->Docker-Hub-rate-limit churn documented in JOURNAL-2. # re-pull on the next run → the prune->re-pull->Docker-Hub-rate-limit churn documented in JOURNAL-2.
# #
# On this SINGLE host, Docker's own local image store IS the cache (re-deploys reuse local layers, # On this single host, Docker's local image store is the CI cache. Keep tagged images during
# no re-download; the daemon is PAT-authenticated). So we keep that store warm and only reclaim disk # routine pressure, but evict unused old images at 90% usage. This recovers from a warm service
# under GENUINE pressure, and even then SURGICALLY: # crash loop: an unmet replica must not prevent the emergency cleanup that lets it restart.
# - dangling images + stopped containers + dangling build cache, age-gated (until=24h) — NEVER # Never prune volumes, and skip while a run-app stack is live.
# `--all` (would evict tagged base/in-use images), NEVER `--volumes` (warm canonical data — see
# swarm.nix's existing comment; warm volumes are reaped only by the warm reconcilers).
# and only when nothing is in flight:
# - skip if any run-app stack is live (mid-pull layers can look prunable — "never prune mid-run");
# - skip if any swarm service has unmet replicas (a deploy/pull is converging, incl. warm redeploys).
{ pkgs, ... }: { pkgs, ... }:
let let
# `/` usage % at/above which a surgical prune is permitted. Below this: keep the cache, no-op. # `/` usage % at/above which a surgical prune is permitted. Below this: keep the cache, no-op.
threshold = 80; threshold = 80;
emergencyThreshold = 90;
prune = pkgs.writeShellApplication { prune = pkgs.writeShellApplication {
name = "cc-ci-docker-prune"; name = "cc-ci-docker-prune";
runtimeInputs = with pkgs; [ docker coreutils gnugrep gawk ]; runtimeInputs = with pkgs; [ docker coreutils gnugrep gawk ];
text = '' text = ''
THRESH=${toString threshold} THRESH=${toString threshold}
EMERGENCY_THRESH=${toString emergencyThreshold}
used="$(df --output=pcent / | tail -1 | tr -dc '0-9')" used="$(df --output=pcent / | tail -1 | tr -dc '0-9')"
: "''${used:=0}" : "''${used:=0}"
if [ "$used" -lt "$THRESH" ]; then if [ "$used" -lt "$THRESH" ]; then
@@ -38,6 +35,14 @@ let
echo "docker-prune: a run-app stack is live — skipping (never prune mid-run)" echo "docker-prune: a run-app stack is live — skipping (never prune mid-run)"
exit 0 exit 0
fi fi
if [ "$used" -ge "$EMERGENCY_THRESH" ]; then
echo "docker-prune: / at ''${used}% (>= ''${EMERGENCY_THRESH}%) — emergency prune of unused images older than 7d"
docker container prune -f --filter until=24h || true
docker image prune -af --filter until=168h || true
docker builder prune -f --filter until=24h || true
df -h /
exit 0
fi
# NEVER prune while ANY swarm service is converging (unmet replicas => a pull/deploy in flight, # NEVER prune while ANY swarm service is converging (unmet replicas => a pull/deploy in flight,
# including infra warm redeploys). Replicas field is "running/desired" e.g. 1/1. # including infra warm redeploys). Replicas field is "running/desired" e.g. 1/1.
converging="$(docker service ls --format '{{.Replicas}}' \ converging="$(docker service ls --format '{{.Replicas}}' \
@@ -56,7 +61,7 @@ let
in in
{ {
systemd.services.ci-docker-prune = { systemd.services.ci-docker-prune = {
description = "Surgical disk-pressure-gated Docker prune (dangling+old only; never --all/--volumes; never mid-run)"; description = "Disk-pressure-gated Docker prune (old unused images at 90%; never volumes or mid-run)";
after = [ "docker.service" ]; after = [ "docker.service" ];
requires = [ "docker.service" ]; requires = [ "docker.service" ];
path = [ pkgs.docker ]; path = [ pkgs.docker ];