Robot ValidationCatalogNew runDashboardTickets

← Catalog

SW-7 Fill image store with a fake large image, then watch GC

DomainsoftwareModule
Priorityhigh Automatedno
TagsstressRequires
Depends onSW-6 (pass)

Preconditions

  export CONTAINER_RUNTIME_ENDPOINT=unix:///run/k3s/containerd/containerd.sock
  du -sh /var/lib/containerd ; crictl images -q | wc -l ; df -h /var/lib/containerd

Steps

Step 1 — take the 5 GB fake image Build one image whose single layer is 5 GB of incompressible data, import it into the k8s containerd namespace.

dd if=/dev/urandom of=/tmp/blob-0 bs=1M count=5120        # 5 GB, incompressible
# assemble a minimal image (FROM scratch + COPY blob) and import it:
ctr -n k8s.io images import /tmp/fake-0.tar               # -> docker.io/library/fake:0
ctr -n k8s.io images ls | grep fake

> TODO(bench): confirm the image-build tool available on the unit (ctr/buildctl; > the robot has no docker). Wire this into scripts/fill_image_store.sh when automated.

Step 2 — loop-load until images total 100 GB (5 GB × 20) Each iteration must be a distinct 5 GB layer — fresh random content, not a retag (retagging shares layers and consumes no extra space).

for i in $(seq 1 19); do
  dd if=/dev/urandom of=/tmp/blob-$i bs=1M count=5120
  # build image fake:$i with blob-$i as its layer, then:
  ctr -n k8s.io images import /tmp/fake-$i.tar            # -> docker.io/library/fake:$i
done
du -sh /var/lib/containerd        # expect ~100 GB (20 × 5 GB distinct layers)
crictl images -q | wc -l          # ~20 fake images + baseline

Step 3 — seed 5 terminated/unknown-state pods Create pods that immediately terminate (Completed/Error) or land in an unknown state, referencing the fake images so those images are momentarily pinned then released.

for i in $(seq 1 5); do
  kubectl run dead-$i --image=docker.io/library/fake:$i --restart=Never \
    --command -- /bin/false || true
done
kubectl get pods -A | grep dead-      # expect Error/Completed (terminated)

> TODO(bench): kubectl needs the k3s server kubeconfig; this node is a k3s agent. > Run from the server/control plane, or seed equivalent dead containers via crictl runp/crictl create.

Step 4 — observe GC / assert reclamation Watch kubelet/k3s image garbage collection: once imagefs usage crosses the image-GC high threshold, unused images (the fake ones not backing a running pod) are reclaimed.

# in one shell: watch the store shrink
watch -n5 'du -sh /var/lib/containerd; crictl images -q | wc -l; df -h /var/lib/containerd | tail -1'
# in another: follow the GC log lines
journalctl -u k3s-agent -f | grep -iE 'image.?gc|garbage|freeing|evict'

> TODO(bench): confirm the k3s image-GC flags/defaults in effect > (--image-gc-high-threshold default 85%, --image-gc-low-threshold 80%, min-age).

Expected

runs automatically and reclaims space by deleting unused images down toward the low threshold — store size and image count drop measurably.

eligible for collection; the release-aru image and any running workload's images are retained (never GC'd out from under a running container).

Teardown

Remove everything created and confirm the store returns to the SW-6 baseline.

for i in $(seq 1 5); do kubectl delete pod dead-$i 2>/dev/null || true; done
for i in $(seq 0 19); do ctr -n k8s.io images rm docker.io/library/fake:$i 2>/dev/null || true; done
rm -f /tmp/blob-* /tmp/fake-*.tar
crictl rmi --prune 2>/dev/null || true
du -sh /var/lib/containerd ; crictl images -q | wc -l    # back to baseline (≤ 5 images)