diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md b/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md index b10e08e..681e82d 100644 --- a/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md +++ b/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md @@ -170,15 +170,16 @@ Ersetzt PLAN §8 „0 Fundament“ (Teil Inventar) und IMPLEMENTATION „Gemeins **Interfaces:** - Produces: `bootstrap-node.sh `, idempotent. Ausgabe endet mit `BOOTSTRAP OK ` oder Exit ≠ 0. -- [ ] **Step 1:** Das Skript (läuft auf dem Laptop, Git-Bash) erledigt Folgendes: +- [x] **Step 1:** Das Skript (läuft auf dem Laptop, Git-Bash) erledigt Folgendes (12.10.: `ssh-keygen -R` nur mit `--reinstall`, Update abgekoppelt per `systemd-run`): - alten Hostschlüssel mit `ssh-keygen -R ` entfernen - Schlüssel per Passwort aus `.env` eintragen, mit dem SSH_ASKPASS-Verfahren vom 08.10. (nur anhängen, `grep -qxF`) - Enterprise-Quellen auf `Enabled: no` setzen, `proxmox.sources` mit `pve-no-subscription` anlegen, vorher sichern - `apt-get update && apt-get -y full-upgrade` - Pakete `ethtool lm-sensors` installieren - Prüfen: `timedatectl show -p NTPSynchronized` = `yes` -- [ ] **Step 2:** Zweiter Lauf gegen denselben Knoten → keine Änderung, wieder `BOOTSTRAP OK`. -- [ ] **Step 3:** Commit. +- [x] **Step 2:** Zweiter Lauf gegen denselben Knoten → keine Änderung, wieder `BOOTSTRAP OK` (pve44, 12.10., 17 s). +- [x] **Step 3:** Commit. +- [ ] **Step 4 (Ausführung 12.10.):** Alle Knoten auf PVE 9.2.21 / Kernel 7.0.14-23 gebracht; nach jedem Neustart Kernel, `systemctl --failed`, vmbr0, NTP sowie GPU-Adressen und IOMMU-Gruppen geprüft. pve61 wird erst neu gestartet, wenn der Nutzer vor Ort ist (USB-NIC im Dock). ### Task 1.2: Speicher (D1) @@ -248,7 +249,7 @@ Ersetzt GPU-Knoten 3 (Teil Cluster). Mit 5 Stimmen ist **kein QDevice** nötig. - [ ] LXC `ops` (Debian 13, vmid 100, auf pve46 laut RAM-Budget, IP aus D5) mit `ansible-core`, `opentofu`, `git` und dem Repo-Clone. Ansible läuft nicht auf dem Windows-Laptop. - [ ] `site.yml`: `pve-host` nur für `pve_gpu_hosts` (pve44, pve46, pve48, pve52). pve61 steht in `pve_kiosk` ohne VFIO und Blacklist. -- [ ] Rolle `pve-host` ergänzen: `/etc/modprobe.d/vfio.conf` mit `options vfio-pci ids=` und `softdep nvidia pre: vfio-pci`. Die Liste enthält die Grafik- **und** die Audio-Funktionen der Karten. Die IDs aus `lspci -nn -s 01:00` und `lspci -nn -s 02:00` auf dem Knoten lesen, nicht aus dem Gedächtnis eintragen; bekannt sind bisher nur die VGA-IDs `10de:13f1` und `10de:1c30`. +- [ ] Rolle `pve-host` ergänzen: `/etc/modprobe.d/vfio.conf` mit `options vfio-pci ids=` und `softdep nvidia pre: vfio-pci`. Die Liste enthält die Grafik- **und** die Audio-Funktionen der Karten. Die IDs aus `lspci -nn -s 01:00` und `lspci -nn -s 02:00` auf dem Knoten lesen, nicht aus dem Gedächtnis eintragen; auf allen GPU-Knoten gelesen (12.10.): M4000 `10de:13f1` + Audio `10de:0fbb`, P2000 `10de:1c30` + Audio `10de:10f1`. - [ ] `ansible-playbook playbooks/site.yml --limit pve_gpu_hosts --check`, danach der echte Lauf. Knoten **einzeln** neu starten. Prüfung: `lspci -nnk -s 01:00` und `lspci -nnk -s 02:00` auf jedem GPU-Knoten → bei allen Funktionen `Kernel driver in use: vfio-pci`. Auf pve61: `lspci -k -s 00:02.0` → `i915`. diff --git a/platform/proxmox/scripts/README.md b/platform/proxmox/scripts/README.md new file mode 100644 index 0000000..4664ed6 --- /dev/null +++ b/platform/proxmox/scripts/README.md @@ -0,0 +1,23 @@ +# Skripte für die Proxmox-Knoten + +## bootstrap-node.sh + +Bringt einen Knoten auf den Grundstand: Laptop-Schlüssel, Paketquellen `pve-no-subscription`, `apt full-upgrade`, `ethtool` und `lm-sensors`. Läuft auf dem Admin-Laptop in Git-Bash und ist idempotent. + +```bash +platform/proxmox/scripts/bootstrap-node.sh pve44 # bestehender Knoten +platform/proxmox/scripts/bootstrap-node.sh --reinstall pve46 # nach Neuinstallation +``` + +- Voraussetzungen: SSH-Alias in `~/.ssh/config`, `.env` mit `_PASS` (nur nötig, solange der Schlüssel fehlt). +- `--reinstall` verwirft den alten Hostschlüssel. Ohne den Schalter muss der bekannte Hostschlüssel passen. +- Das Update läuft per `systemd-run` auf dem Knoten weiter, auch wenn die SSH-Sitzung abreißt. Protokoll: `/root/bootstrap-upgrade.log`. +- Das Skript startet nicht neu. Es meldet `Neustart nötig: ja (…)`, wenn ein neuer Kernel installiert wurde. + +Nach dem Neustart prüfen: + +```bash +ssh pve44 'uname -r; pveversion; systemctl --failed --no-legend; ip -br a show vmbr0' +``` + +Dazu Adressen, IDs und IOMMU-Gruppen der Karten mit dem Stand vor dem Neustart vergleichen. Die Site-Dateien verwenden `0000:01:00` und `0000:02:00`. diff --git a/platform/proxmox/scripts/bootstrap-node.sh b/platform/proxmox/scripts/bootstrap-node.sh new file mode 100644 index 0000000..7fed2bc --- /dev/null +++ b/platform/proxmox/scripts/bootstrap-node.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# Bootstrap eines Proxmox-Knotens vom Admin-Laptop (Git-Bash). Idempotent. +# +# bootstrap-node.sh [--reinstall] +# +# SSH-Alias aus ~/.ssh/config (z. B. pve44) +# --reinstall nur nach einer Neuinstallation: alten Hostschlüssel verwerfen. +# Ohne den Schalter bleibt der bekannte Hostschlüssel Pflicht. +# +# Umgebung (optional): +# PVE_ENV .env mit _PASS (Standard: C:/Users/r.rapp/it/proxmox/.env) +# PVE_PUBKEY öffentlicher Schlüssel (Standard: C:/Users/r.rapp/it/proxmox/keys/id_ed25519_pve.pub) +# +# Ablauf: Schlüssel eintragen (nur falls nötig) → Paketquellen auf pve-no-subscription +# → apt full-upgrade + ethtool/lm-sensors (abgekoppelt per systemd-run) → Prüfungen. +# Ausgabe endet mit "BOOTSTRAP OK " oder Exit ≠ 0. Neu gestartet wird nicht. +set -euo pipefail + +reinstall=0 +if [ "${1:-}" = "--reinstall" ]; then reinstall=1; shift; fi +node=${1:?Aufruf: bootstrap-node.sh [--reinstall] } +env_file=${PVE_ENV:-C:/Users/r.rapp/it/proxmox/.env} +pubkey=${PVE_PUBKEY:-C:/Users/r.rapp/it/proxmox/keys/id_ed25519_pve.pub} +ip=$(ssh -G "$node" | awk '$1 == "hostname" {print $2}') + +ssh_key() { ssh -T -o BatchMode=yes -o ConnectTimeout=10 "$node" "$@"; } + +# 1. Hostschlüssel nur nach einer Neuinstallation verwerfen +if [ "$reinstall" = 1 ]; then + ssh-keygen -R "$ip" >/dev/null 2>&1 || true +fi + +# 2. Laptop-Schlüssel per Passwort aus .env eintragen, falls der Schlüssel-Login noch nicht geht +if ! ssh_key true 2>/dev/null; then + var="$(printf '%s' "$node" | tr '[:lower:]' '[:upper:]')_PASS" + pw=$(grep -E "^${var}=" "$env_file" | head -n1 | cut -d= -f2- | tr -d '\r') + [ -n "$pw" ] || { echo "FEHLER: ${var} fehlt in ${env_file}" >&2; exit 1; } + askpass=$(mktemp) + trap 'rm -f "$askpass"' EXIT + printf '#!/bin/sh\nprintf "%%s\\n" "$PVE_ASKPASS_PW"\n' > "$askpass" + chmod +x "$askpass" + PVE_ASKPASS_PW=$pw SSH_ASKPASS=$askpass SSH_ASKPASS_REQUIRE=force DISPLAY=:0 \ + ssh -o StrictHostKeyChecking=accept-new -o PubkeyAuthentication=no \ + -o NumberOfPasswordPrompts=1 -o ConnectTimeout=10 "root@$ip" \ + 'umask 077; mkdir -p ~/.ssh; read -r k; grep -qxF "$k" ~/.ssh/authorized_keys 2>/dev/null || echo "$k" >> ~/.ssh/authorized_keys' \ + < "$pubkey" + unset pw + ssh_key true || { echo "FEHLER: Schlüssel-Login auf $node geht nicht" >&2; exit 1; } +fi + +# 3. Paketquellen umstellen und das Update abgekoppelt starten (übersteht Neustarts von sshd/pveproxy) +ssh_key 'bash -s' <<'REMOTE' +set -euo pipefail +d=/etc/apt/sources.list.d +if ! ls -d /root/apt-backup-* >/dev/null 2>&1; then + b=/root/apt-backup-$(date +%F); mkdir -p "$b"; cp -a "$d/." "$b/" +fi +for f in pve-enterprise.sources ceph.sources; do + if [ -f "$d/$f" ] && ! grep -q '^Enabled:' "$d/$f"; then sed -i '/^Types:/a Enabled: no' "$d/$f"; fi +done +test -f /usr/share/keyrings/proxmox-archive-keyring.gpg +[ -f "$d/proxmox.sources" ] || cat > "$d/proxmox.sources" <<'SRC' +Types: deb +URIs: http://download.proxmox.com/debian/pve +Suites: trixie +Components: pve-no-subscription +Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg +SRC +rm -f /root/bootstrap-upgrade.rc +systemd-run --unit="bootstrap-upgrade-$(date +%s)" --collect --quiet \ + --setenv=DEBIAN_FRONTEND=noninteractive \ + bash -c 'o="-o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold" + { apt-get update && apt-get -y $o full-upgrade && apt-get -y $o install ethtool lm-sensors; } \ + > /root/bootstrap-upgrade.log 2>&1 + echo $? > /root/bootstrap-upgrade.rc' +REMOTE + +echo "Update läuft auf $node (Protokoll dort: /root/bootstrap-upgrade.log) ..." +rc="" +for _ in $(seq 1 240); do # höchstens 60 Minuten + rc=$(ssh_key 'cat /root/bootstrap-upgrade.rc 2>/dev/null' 2>/dev/null || true) + [ -n "$rc" ] && break + sleep 15 +done +if [ "$rc" != 0 ]; then + echo "FEHLER: Update auf $node (rc=${rc:-Zeitüberschreitung})" >&2 + ssh_key 'tail -n 20 /root/bootstrap-upgrade.log' >&2 || true + exit 1 +fi + +# 4. Prüfungen +ssh_key 'bash -s' <<'REMOTE' +set -euo pipefail +[ "$(timedatectl show -p NTPSynchronized --value)" = yes ] || { echo "FEHLER: Zeit nicht synchronisiert" >&2; exit 1; } +command -v ethtool >/dev/null && command -v sensors >/dev/null +newest=$(ls /lib/modules | sort -V | tail -n1) +if [ "$newest" = "$(uname -r)" ]; then restart=nein; else restart="ja ($(uname -r) -> $newest)"; fi +echo "$(pveversion | cut -d/ -f2) | offene Updates: $(apt-get -s full-upgrade | grep -c '^Inst' || true) | Neustart nötig: $restart" +echo "BOOTSTRAP OK $(hostname)" +REMOTE