feat: funk-cluster — arrêt k8s propre avec drain + vérification NFS

Remplace le talosctl shutdown immédiat par une séquence en 6 étapes :
1. kubectl cordon tous les nœuds (empêche rescheduling)
2. kubectl drain compute-02/03 (SIGTERM pods, 30s grace, PVCs NFS libérés)
3. kubectl drain compute-01 (ArgoCD, system pods)
4. Vérification NFS : ss confirme plus de sessions :2049 actives
5. talosctl shutdown (halt propre, plus rien ne tourne)
6. Attente extinction ping

Prometheus et Grafana ont 30s pour flusher WAL/SQLite avant terminaison.
storage-01 (NFS server) est éteint en dernier une fois la vérification passée.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alkatrazz 2026-05-13 22:20:14 +02:00
parent 8b6f906a89
commit a631a4ab6c
2 changed files with 86 additions and 15 deletions

View file

@ -62,7 +62,7 @@ La connexion SSH se coupe à la fin (storage-01 s'éteint lui-même). C'est norm
funk-stop --k8s
```
Ordre d'arrêt automatique : hermes-agent + litellm → talosctl shutdown compute-01/02/03 → gpu-01 → qdrant + postgresql → storage-01.
Ordre d'arrêt automatique : hermes-agent + litellm → cordon+drain compute-02/03 → drain compute-01 → vérif NFS → talosctl shutdown → gpu-01 → qdrant + postgresql → storage-01.
---
@ -88,7 +88,13 @@ Vérifie dans l'ordre :
### `funk-stop [--k8s]`
1. Arrête hermes-agent et litellm sur storage-01 (timeout 10s + SIGKILL si besoin)
2. `--k8s` : `talosctl shutdown` sur compute-01/02/03, attend extinction
2. `--k8s` — arrêt propre du cluster en 6 étapes :
- **Cordon** des 3 nœuds → plus aucun pod ne peut être replanifié
- **Drain compute-02** → SIGTERM aux pods, 30s grace period, PVCs NFS démontés
- **Drain compute-03** → idem (Prometheus, Grafana flushent leur WAL/SQLite)
- **Drain compute-01** → ArgoCD, kube-state-metrics, etc.
- **Vérification NFS** → confirme que plus aucune session NFS active depuis k8s
- **talosctl shutdown** → halt propre (plus rien ne tourne, rien n'écrit)
3. Éteint gpu-01 (`shutdown -h now` via SSH)
4. Arrête qdrant et postgresql-16 sur storage-01
5. Éteint storage-01 (`shutdown -h now`) — connexion SSH coupée
@ -154,7 +160,7 @@ ssh s01 "sudo cp /tmp/funk-cluster /usr/local/bin/funk-cluster && sudo chmod +x
| Sujet | Détail |
|---|---|
| Ordre d'arrêt | Hermes/LiteLLM → k8s (optionnel) → gpu-01 → Qdrant/PG → storage-01 |
| Ordre d'arrêt | Hermes/LiteLLM → cordon+drain k8s (optionnel) → gpu-01 → Qdrant/PG → storage-01 |
| Ordre de démarrage | storage-01 (toujours déjà up) → gpu-01 → k8s (optionnel) |
| WOL compute nodes | BIOS non configuré → allumer manuellement pendant l'attente ping de `funk-start --k8s` |
| LiteLLM bloquant | `systemctl stop --timeout=10` + SIGKILL automatique si timeout |

View file

@ -357,31 +357,96 @@ cmd_stop() {
if [ -n "$with_k8s" ]; then
echo ""
echo "[ cluster k8s — arrêt compute-01/02/03 ]"
local alive=()
for ip in "${K8S_IPS[@]}"; do
ping -c1 -W2 "$ip" &>/dev/null && alive+=("$ip")
echo "[ cluster k8s — arrêt propre compute-01/02/03 ]"
# Quels nœuds sont en vie ?
local alive_ips=() alive_names=()
for i in 0 1 2; do
if ping -c1 -W2 "${K8S_IPS[$i]}" &>/dev/null; then
alive_ips+=("${K8S_IPS[$i]}")
alive_names+=("${K8S_NAMES[$i]}")
fi
done
if [ ${#alive[@]} -gt 0 ]; then
local nodes=$(IFS=,; echo "${alive[*]}")
info "talosctl shutdown → $nodes"
if [ ${#alive_ips[@]} -eq 0 ]; then
warn "Nœuds k8s déjà éteints"
else
# 1. Cordon de tous les nœuds — empêche tout rescheduling pendant le drain
info "Cordon (plus aucun pod ne peut être replanifié)..."
for name in "${alive_names[@]}"; do
$KUBECTL cordon "$name" 2>/dev/null \
&& ok "$name — cordonné" \
|| warn "$name — cordon échoué (nœud peut-être inaccessible)"
done
# 2. Drain workers en premier (compute-02, compute-03)
# Les pods reçoivent SIGTERM → flush WAL/SQLite → PVCs NFS démontés
echo ""
info "Drain workers (SIGTERM pods, 30s grace, 90s max)..."
for i in 1 2; do
if ping -c1 -W2 "${K8S_IPS[$i]}" &>/dev/null; then
info " Drain ${K8S_NAMES[$i]}..."
$KUBECTL drain "${K8S_NAMES[$i]}" \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=30 \
--timeout=90s \
--force 2>&1 | grep -vE "^(Warning|I[0-9])" | tail -3 || true
ok "${K8S_NAMES[$i]} — pods évacués, PVCs NFS libérés"
fi
done
# 3. Drain control-plane (compute-01) — ArgoCD, kube-state-metrics, etc.
if ping -c1 -W2 "${K8S_IPS[0]}" &>/dev/null; then
echo ""
info "Drain control-plane (compute-01)..."
$KUBECTL drain compute-01 \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=30 \
--timeout=90s \
--force 2>&1 | grep -vE "^(Warning|I[0-9])" | tail -3 || true
ok "compute-01 — drainé"
fi
# 4. Vérification NFS : plus aucune session active depuis les nœuds k8s
echo ""
info "Vérification montages NFS..."
local nfs_wait=0
while ss -tn 2>/dev/null | grep ':2049' | grep -qE '10\.1[123]\.'; do
sleep 3; nfs_wait=$((nfs_wait + 3))
[ $nfs_wait -ge 30 ] && break
echo -ne " → NFS encore actif depuis nœuds k8s... ${nfs_wait}s\r"
done; echo ""
if ss -tn 2>/dev/null | grep ':2049' | grep -qE '10\.1[123]\.'; then
warn "Sessions NFS toujours actives après 30s — extinction quand même"
else
ok "NFS libéré — aucune écriture en cours depuis le cluster"
fi
# 5. Extinction via talosctl (kernel halt propre — plus rien ne tourne)
echo ""
local nodes=$(IFS=,; echo "${alive_ips[*]}")
info "talosctl shutdown → ${alive_ips[*]}"
if $TALOSCTL shutdown --nodes "$nodes" 2>/dev/null; then
ok "Shutdown envoyé aux nœuds"
else
warn "Erreur talosctl shutdown — extinction manuelle requise"
return
fi
# 6. Attente extinction complète (ping)
local tries=0
while [ $tries -lt 20 ]; do
local still_up=0
for ip in "${alive[@]}"; do ping -c1 -W1 "$ip" &>/dev/null && still_up=$((still_up + 1)); done
for ip in "${alive_ips[@]}"; do
ping -c1 -W1 "$ip" &>/dev/null && still_up=$((still_up + 1))
done
[ $still_up -eq 0 ] && break
sleep 3; tries=$((tries + 1))
echo -ne " → Extinction k8s... $((tries * 3))s\r"
done; echo ""
ok "Cluster k8s éteint"
else
warn "Nœuds k8s déjà éteints"
ok "Cluster k8s éteint proprement"
fi
fi
@ -429,7 +494,7 @@ case "$CMD" in
echo " start Démarre gpu-01 (WOL) + vérifie tous les services"
echo " start --k8s Idem + démarre le cluster k8s (compute-01/02/03)"
echo " stop Arrêt propre gpu-01 + services storage-01"
echo " stop --k8s Idem + éteint le cluster k8s (talosctl shutdown)"
echo " stop --k8s Idem + arrêt propre k8s (drain → NFS check → talosctl shutdown)"
echo " status État complet (storage-01 + gpu-01 + k8s)"
exit 1
;;