apiVersion: v1 kind: ConfigMap metadata: name: image-prune-script namespace: backup-system data: image-prune.sh: | #!/bin/sh # Barrido semanal de imágenes de containerd huérfanas, un nodo por job. # Motivo: cada build de CI publica un tag nuevo y el nodo se queda con todos # los viejos. El 2026-08-05 el worker llegó al 80,9% de disco con 223 imágenes # para ~20 pods (97 tags de researchowl, 105 de polymarket-bot, app decomisada # en julio). El prune dejó el nodo en 23%: 54 GB en imágenes muertas. # # El recolector de kubelet no basta: solo entra al 85% de imagefs, o sea que # solo actúa cuando ya casi no queda sitio, y en ráfaga. # # chroot al host porque crictl no está en la imagen: vive en el binario de k3s # del nodo, y el socket de containerd solo se ve desde el namespace del host. set -u K3S=/usr/local/bin/k3s PASADAS=4 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } disco() { chroot /host df -h / | tail -1 | awk '{print $3" usados, "$4" libres ("$5")"}'; } imagenes() { chroot /host $K3S crictl images 2>/dev/null | tail -n +2 | wc -l; } if [ ! -x "/host$K3S" ]; then log "ERROR: no encuentro $K3S en el host" exit 1 fi log "antes: $(disco), $(imagenes) imágenes" # Varias pasadas a propósito: borrar snapshots satura a containerd y las # llamadas siguientes se caen con DeadlineExceeded. No es un fallo real, es # cola — reintentando termina. Pasó en la limpieza manual del 2026-08-05: # la primera pasada soltó 11 GB de los 54 y el resto salió en las siguientes. # No fiamos el reintento del código de salida (crictl devuelve 0 aunque haya # borrados fallidos): miramos si escupió errores de RemoveImage. limpio=0 for i in $(seq 1 $PASADAS); do salida=$(chroot /host $K3S crictl rmi --prune 2>&1) if echo "$salida" | grep -q "RemoveImage.*failed"; then fallos=$(echo "$salida" | grep -c "RemoveImage.*failed") log "pasada $i: $fallos borrados en cola, reintento en 30s" sleep 30 else log "pasada $i: limpia" limpio=1 break fi done log "después: $(disco), $(imagenes) imágenes" if [ "$limpio" = 0 ]; then # Sale 1 para que se vea en Grafana/Telegram: liberó algo, pero quedaron # borrados sin cerrar tras $PASADAS intentos y conviene mirar el nodo. log "ERROR: quedaron borrados sin completar tras $PASADAS pasadas" exit 1 fi log "OK"