From a631a4ab6c0ca27b14ac46d4dcfe145d4952811e Mon Sep 17 00:00:00 2001 From: alkatrazz Date: Wed, 13 May 2026 22:20:14 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20funk-cluster=20=E2=80=94=20arr=C3=AAt?= =?UTF-8?q?=20k8s=20propre=20avec=20drain=20+=20v=C3=A9rification=20NFS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- admin/cluster.md | 12 +++- ansible/roles/litellm/files/funk-cluster | 89 ++++++++++++++++++++---- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/admin/cluster.md b/admin/cluster.md index 787739d..dc34c7e 100644 --- a/admin/cluster.md +++ b/admin/cluster.md @@ -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 | diff --git a/ansible/roles/litellm/files/funk-cluster b/ansible/roles/litellm/files/funk-cluster index 82d870b..87b6606 100644 --- a/ansible/roles/litellm/files/funk-cluster +++ b/ansible/roles/litellm/files/funk-cluster @@ -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 ;;