harkaq: granja con volumen + auto-shutdown VALIDADA end-to-end

Test real (worker + volumen + helper), no en el papel. Las 3 piezas:
  1. worker midió lz4+bzip2 → volumen
  2. tras 1 ciclo idle SE AUTO-ELIMINÓ (verificado: el server desapareció solo)
  3. volumen SOBREVIVIÓ con los 2 verdicts; helper efímero ccx13 los recogió al
     hub y se auto-destruyó

3 bugs cazados EN EL TEST (no adivinados):
  - falta --exclude /work: work/=69GB colgó el rsync del up horas
  - cpx11 'unsupported' en hel1 (AMD sin stock) → ccx13
  - collect llamaba harvest con 'local' (rsync a un worker inexistente): ahora
    sólo recoge; clasificar es paso aparte (worker mide, hub clasifica)

+ auto-delete por API REST (curl+metadata), NO hcloud CLI (la golden no lo trae).
El ID propio sale del metadata service, no del nombre.

Cierra los 2 huecos de la 1ª campaña: idle cobrando (ahora auto-delete) y
dependencia de que yo esté viva (ahora el volumen persiste). Runbook:
docs/runbooks/harkaq-granja-volumen.md. Volumen clausurado tras el test (€0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 07:47:36 -04:00
co-authored by Claude Opus 4.8
parent f7916b5ee9
commit 35f58aa75b
2 changed files with 55 additions and 6 deletions
+42
View File
@@ -0,0 +1,42 @@
# Granja harkaq con volumen persistente + auto-shutdown
La arquitectura que la 1ª campaña pidió (quedó idle ~4h cobrando). El worker no depende de que yo
esté viva y no se queda idle. **Validado end-to-end 2026-07-16** (worker se auto-eliminó, volumen
sobrevivió con las cosechas, helper efímero las recogió).
## Ciclo
```sh
scripts/farm/harkaq-vol.sh up 2 # 2 workers midiendo → volumen; se auto-eliminan al agotar cola
# ... (muelen la noche, se destruyen solos) ...
scripts/farm/harkaq-vol.sh collect # (cuando esté viva) helper efímero baja las cosechas al hub
scripts/farm/harkaq-vol.sh close # clausura el volumen (deja de cobrar)
scripts/farm/harkaq-vol.sh status
```
## Cómo funciona (y por qué así)
- **Volumen `harkaq-cosecha`**: los workers escriben verdicts a `/mnt/cosecha`, que **sobrevive a
su destrucción**. No dependo de estar viva para no perder el trabajo.
- **Auto-shutdown**: tras `IDLE_CICLOS` ciclos con 0 mediciones nuevas, el worker **se auto-elimina
vía API REST** (`curl` + metadata service; NO hcloud CLI, que la golden no trae). En Hetzner un
server apagado **sigue cobrando** ⇒ apagar de verdad es `delete`.
- **collect**: monta el volumen en un helper efímero barato (o un worker vivo), rsync al hub, lo
destruye. Clasificar los verdicts es un paso aparte (el worker MIDE, el hub CLASIFICA con el
store completo).
## Rastrillos que ya se pagaron (todos verificados en vivo)
- **`--exclude /work` es crítico**: `work/` son 69GB; sin excluirlo el rsync del `up` cuelga horas.
- **Tipo de server**: en hel1 los AMD `cpx*` dan "unsupported/stock"; `ccx13`/`ccx23` sí van.
- **El token va al worker** (para auto-delete): riesgo acotado — efímero, sin servicios entrantes.
- **`${1:?...}` sin llaves** en el mensaje: una `}` interna cierra la expansión.
- El bucle **borra el artefacto `-hkm` tras medir** (envenenaba la caché en la 1ª campaña) y gatea
por ABI≥7 + binario-con-jaula (no medir ciego).
## Parar de emergencia
```sh
hcloud server list | grep hkw # ver workers
hcloud server delete hkw-N # matar uno
scripts/farm/harkaq-vol.sh close # y el volumen
```
+13 -6
View File
@@ -26,7 +26,7 @@ cd "$HUB"
VOL="${VOL:-harkaq-cosecha}"
IMAGE="${IMAGE:-408909310}" # golden con kernel 6.17 (Landlock ABI 7)
TYPE="${TYPE:-ccx23}"
HELPER_TYPE="${HELPER_TYPE:-cx22}" # para collect: el más barato que monte el volumen
HELPER_TYPE="${HELPER_TYPE:-ccx13}" # para collect: el más barato que monte el volumen
LOCATION="${LOCATION:-hel1}"
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
@@ -75,8 +75,11 @@ EOF
ip=$(hcloud server ip "$name")
wait_ssh "$ip" || { echo " $name no responde"; continue; }
# Provisión: código + harkaq + montaje del volumen + servicio con auto-shutdown.
rsync -az -e "$SSH" --exclude /store --exclude '/store-*' --exclude /target --exclude /dist \
--exclude /.dev-fs --exclude /.git --exclude /.scratch --exclude '*.png' --exclude '/content*' \
# --exclude /work es CRÍTICO: work/ son 69GB (sources, tandas). Sin él el rsync manda
# decenas de GB y el up cuelga horas — pasó. farm-up.sh ya lo excluye; se perdió al copiar.
rsync -az -e "$SSH" --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
--exclude /dist --exclude /tandas --exclude /.dev-fs --exclude /.git --exclude /.scratch \
--exclude '*.png' --exclude '/content*' \
./ "root@$ip:$REMOTE/" >/dev/null
$SSH "root@$ip" "MNT=/mnt/cosecha; mkdir -p \$MNT; mount $DEV \$MNT 2>/dev/null || (mkfs.ext4 -q -F $DEV && mount $DEV \$MNT); mkdir -p \$MNT/verdicts \$MNT/logs"
scripts/farm/harkaq-farm-setup.sh "$ip" >/dev/null 2>&1 || echo " (setup con avisos, sigo)"
@@ -120,9 +123,13 @@ collect)
mkdir -p work/harkaq-campana
rsync -az -e "$SSH" "root@$ip:/mnt/cosecha/verdicts/" work/harkaq-campana/ 2>/dev/null || true
[ -n "$helper" ] && { echo "== destruyo el helper"; hcloud server delete "$helper" >/dev/null; }
echo "== corriendo el harvest sobre lo recogido"
scripts/farm/harvest-harkaq.sh "local" 2>/dev/null || \
echo " (harvest necesita un worker; recogido a work/harkaq-campana/, clasificá con harvest-harkaq)"
# NO se corre el harvest acá: harvest-harkaq.sh rsync-ea de un WORKER, y en este punto los
# verdicts ya están locales (los trajo el rsync del volumen). Clasificarlos es un paso aparte
# —lo hace el hub con el store COMPLETO (el worker mide, el hub clasifica)— y su edición de
# recetas + commit no debe colgar de un `collect`. Se reporta y se deja al operador/harvest.
n=$(ls work/harkaq-campana/*.verdicts 2>/dev/null | wc -l)
echo "== recogidos $n verdicts a work/harkaq-campana/. Clasificar/declarar:"
echo " scripts/farm/harkaq-suggest.py <v> ~/.cache/harkaq/base.policy \$PWD/store \$PWD/recipes"
;;
close)