From 3eacfa2a7599ecade11e2469a7e272b061011ba8 Mon Sep 17 00:00:00 2001 From: Alexander Gabriel Date: Sun, 18 Oct 2026 15:16:43 +0200 Subject: [PATCH] added stop-skript --- README.md | 13 +++++++-- scripts/stop.sh | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100755 scripts/stop.sh diff --git a/README.md b/README.md index 1f4fd78..b7961cd 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,16 @@ sudo ./scripts/firewall-ufw.sh # einmalig: Ports 8800 + 8801 für LAN + wt0 in **Nach einem Reboot** startet der Stack von selbst: Der Docker-Dienst ist aktiviert, und die Container haben `restart: unless-stopped`. Autocomplete und Embeddings lädt llama-swap per `hooks.on_startup.preload` ebenfalls von selbst, das Chat-Modell kommt beim ersten Request dazu. +Gestoppt bzw. der VRAM freigegeben wird mit `scripts/stop.sh`. Laufen Modelle, entlädt das Skript sie vorher über llama-swap, damit die Server sauber beendet werden. Danach zeigt es Container-Status und VRAM-Belegung. + +| Aufruf | Wirkung | +|---|---| +| `./scripts/stop.sh` | Modelle entladen, Container stoppen (bleiben auch nach einem Reboot aus) | +| `./scripts/stop.sh --unload` | nur alle Modelle entladen, Container laufen weiter | +| `./scripts/stop.sh --down` | Container stoppen und entfernen, Modelle und Daten bleiben erhalten | + +Nach dem Stoppen (mit oder ohne `--down`) startet `./scripts/start.sh` den Stack wieder. Nach `--unload` lädt llama-swap Autocomplete und Embeddings beim nächsten Request von selbst neu. + Der erste Aufruf eines Modells lädt es von Hugging Face herunter (Hauptmodell ~22 GB). Danach läuft alles offline. Whisper und das Bildmodell lädt llama-swap **nicht** selbst herunter (~12 GB): @@ -140,6 +150,5 @@ GPU-Layer und MoE-Auslagerung in den RAM verteilt llama.cpp automatisch (`--fit` | `client/continue/` | Vorlage für Continue-Clients | | `docs/laravel.md` | Anbindung einer Laravel-App (laravel/ai) | | `scripts/start.sh` | Start mit Prüfungen, Preload und Statusausgabe | +| `scripts/stop.sh` | Stoppen bzw. Modelle entladen, mit Statusausgabe | | `scripts/firewall-ufw.sh` | ufw-Freigaben | -| `PLAN.md` | ursprünglicher Umsetzungsplan | -| `PLAN-multimodal.md` | Plan + Messwerte für Transkription/Bilder | diff --git a/scripts/stop.sh b/scripts/stop.sh new file mode 100755 index 0000000..31399cd --- /dev/null +++ b/scripts/stop.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Stoppt den LocalAI-Stack bzw. gibt den VRAM frei. +# +# ./scripts/stop.sh Container stoppen (bleiben auch nach Reboot aus) +# ./scripts/stop.sh --unload nur alle Modelle entladen, Container laufen weiter +# ./scripts/stop.sh --down Container stoppen und entfernen (Modelle/Daten bleiben) +set -euo pipefail + +cd "$(dirname "$(readlink -f "$0")")/.." + +MODE=stop +while [[ $# -gt 0 ]]; do + case "$1" in + --unload) MODE=unload ;; + --down) MODE=down ;; + -h|--help) sed -n '2,6p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "Unbekannte Option: $1" >&2; exit 2 ;; + esac + shift +done + +if [[ -t 1 ]]; then B=$'\e[1m'; G=$'\e[32m'; Y=$'\e[33m'; R=$'\e[31m'; N=$'\e[0m'; else B= G= Y= R= N=; fi +ok() { echo "${G}✔${N} $*"; } +warn() { echo "${Y}!${N} $*"; } +die() { echo "${R}✘${N} $*" >&2; exit 1; } +step() { echo; echo "${B}== $*${N}"; } + +command -v docker >/dev/null || die "docker nicht installiert" +docker info >/dev/null 2>&1 || die "Docker-Daemon läuft nicht oder keine Rechte (Gruppe 'docker'?)" + +BACKEND_PORT=8080 +[[ -f .env ]] && BACKEND_PORT=$(set -a; . ./.env; echo "${BACKEND_PORT:-8080}") + +running=$(docker compose ps --status running -q 2>/dev/null | wc -l) + +# ------------------------------------------------------------- Entladen +# vor dem Stoppen entladen, damit llama-swap die Server sauber beendet +if [[ "$running" -gt 0 ]] && curl -fsS -m 2 "http://127.0.0.1:$BACKEND_PORT/health" >/dev/null 2>&1; then + step "Entlade Modelle" + loaded=$(curl -s -m 5 "http://127.0.0.1:$BACKEND_PORT/running" \ + | python3 -c 'import sys,json; print(", ".join(m["model"] for m in json.load(sys.stdin)["running"]) or "-")' 2>/dev/null || echo "?") + echo " geladen: $loaded" + if curl -fsS -m 60 -o /dev/null "http://127.0.0.1:$BACKEND_PORT/unload"; then + ok "Modelle entladen" + else + warn "Entladen über llama-swap fehlgeschlagen" + fi +elif [[ "$MODE" == unload ]]; then + die "llama-swap läuft nicht – nichts zu entladen" +fi + +# --------------------------------------------------------------- Stoppen +case "$MODE" in + stop) + step "Stoppe Container" + docker compose stop + ok "Stack gestoppt – Neustart mit ./scripts/start.sh" + ;; + down) + step "Stoppe und entferne Container" + docker compose down --remove-orphans + ok "Stack entfernt – Neustart mit ./scripts/start.sh" + ;; + unload) + warn "Container laufen weiter – Autocomplete/Embeddings werden beim nächsten Request neu geladen" + ;; +esac + +# ------------------------------------------------------------------ Status +step "Status" +docker compose ps -a --format '{{.Service}}: {{.Status}}' | sed 's/^/ /' +[[ "$(docker compose ps -a -q | wc -l)" -eq 0 ]] && echo " keine Container" +command -v rocm-smi >/dev/null && rocm-smi --showmeminfo vram 2>/dev/null \ + | awk '/GPU\[0\].*Total Memory/{t=$NF} /GPU\[0\].*Used Memory/{u=$NF} END{if(t) printf " VRAM: %.1f / %.1f GB\n", u/1e9, t/1e9}' +true