Files
k8s-manifests/backup-system/configmap-image-prune-script.yaml
chemavxandClaude Opus 5 87a4ece6f4 backup-system: barrido semanal de imágenes containerd por nodo
El worker llegó al 80,9% de disco: 223 imágenes para ~20 pods, 62 GB en
containerd. Cada build de CI publica un tag y nadie recoge los viejos —
97 tags de researchowl y 105 de polymarket-bot, decomisada en julio.

El GC de kubelet no basta: solo entra al 85% de imagefs, o sea cuando ya
no queda sitio. Un prune manual dejó el worker en 23% (54 GB) y el master
en 15% (58 GB).

Un CronJob por nodo porque un CronJob programa un solo pod; el nodo se
fija con nodeSelector. Domingos 06:30 y 07:15, el hueco entre backups.

El script hace varias pasadas a propósito: borrar snapshots satura a
containerd y las llamadas siguientes dan DeadlineExceeded. No es fallo,
es cola. Se reintenta mirando la salida, no el código de retorno, porque
crictl devuelve 0 aunque haya borrados fallidos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:14:40 +00:00

66 lines
2.5 KiB
YAML

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"