Files
projektuebersicht/Docker/pi-einrichten.sh
T
erikandClaude Opus 5.5 b7e0bd0d92 Termin aus Mail, Rollen & Rechte, Abwesenheiten, Glas-Darstellung und Pi-Betrieb
- Termin aus Mail: Mailtext oder .eml per Drag & Drop/Strg+V auf Terminplan
  oder Aufgaben, KI schlägt Aufgaben vor, Prüfung im Aufgaben-Panel; neues
  Notizfeld je Aufgabe (plan_todos.notiz)
- Terminplan: Meilensteine ohne Aufgabenbezug je in eigener Zeile,
  "+ Neu"-Menü klappt neben schmaler Seitenleiste nach rechts, Leistungsphasen
- Rollen & Rechte, Abwesenheiten-Seite, Benachrichtigungen, Rundgang
- Glas-Darstellung, Logo als Favicon
- Pi-Betrieb: PDF-Arbeit im Kindprozess, Wächter, Einrichtungs- und
  Prüfskript inkl. WLAN-Wächter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:16:52 +01:00

237 lines
9.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# pi-einrichten.sh — macht Ubuntu Server auf dem Raspberry Pi Zero 2 (512 MB)
# stabil für die Projektübersicht. Mehrfach ausführbar (prüft jeden Schritt).
#
# sudo bash Docker/pi-einrichten.sh # Standard
# sudo bash Docker/pi-einrichten.sh --snapd-aus # zusätzlich snapd abschalten
#
# Was es tut (Einzelheiten in CLAUDE.md, "Betrieb auf dem Pi"):
# 1. Hardware-Watchdog: hängt das System, startet der Pi selbst neu
# 2. zram: komprimierter Auslagerungsspeicher im RAM (statt SD-Karte)
# 3. Speichergrenzen für Container erlauben (cgroup memory)
# 4. gpu_mem=16 — kein Bildschirm, mehr RAM für Linux
# 5. Unnötige Dienste aus (multipathd, ModemManager, udisks2 …)
# 6. earlyoom: greift ein, BEVOR das System im Speichermangel festfriert
# 7. Systemprotokoll auf 50 MB begrenzen
# 8. Nächtlicher Neustart des Containers (3:30 Uhr)
# 9. Prüfung der Stromversorgung (Unterspannung)
# 10. WLAN: Energiesparen aus, Wächter verbindet neu und startet den Pi
# neu, wenn das Netz 10 Minuten lang weg bleibt
set -u
if [ "$(id -u)" -ne 0 ]; then echo "Bitte mit sudo ausführen."; exit 1; fi
SNAPD_AUS=0
[ "${1:-}" = "--snapd-aus" ] && SNAPD_AUS=1
NEUSTART_NOETIG=0
FIRMWARE=/boot/firmware
[ -d "$FIRMWARE" ] || FIRMWARE=/boot
ok() { echo " ✓ $*"; }
info() { echo " • $*"; }
warn() { echo " ! $*"; }
echo "== Projektübersicht: Pi einrichten =="
echo "System: $(. /etc/os-release; echo "$PRETTY_NAME"), $(uname -m), $(free -m | awk '/Mem:/{print $2}') MB RAM"
# 1. Hardware-Watchdog ------------------------------------------------------
echo "[1] Hardware-Watchdog"
mkdir -p /etc/systemd/system.conf.d
cat > /etc/systemd/system.conf.d/10-watchdog.conf <<'EOF'
# Hängt das System, löst der Watchdog des Pi nach 14 s einen Neustart aus
# (der bcm2835-Watchdog kann höchstens ~15 s).
[Manager]
RuntimeWatchdogSec=14s
RebootWatchdogSec=2min
EOF
if [ -e /dev/watchdog ]; then ok "Watchdog aktiv nach Neustart (/dev/watchdog vorhanden)"
else warn "/dev/watchdog fehlt — Kernelmodul bcm2835_wdt wird beim Start geladen"; echo bcm2835_wdt > /etc/modules-load.d/watchdog.conf; NEUSTART_NOETIG=1; fi
systemctl daemon-reexec
# 2. zram -------------------------------------------------------------------
echo "[2] zram (komprimierter Auslagerungsspeicher)"
apt-get update -qq
if apt-cache show systemd-zram-generator >/dev/null 2>&1; then
apt-get install -y -qq systemd-zram-generator >/dev/null
cat > /etc/systemd/zram-generator.conf <<'EOF'
[zram0]
zram-size = ram / 2
compression-algorithm = zstd
swap-priority = 100
EOF
systemctl daemon-reload
systemctl restart systemd-zram-setup@zram0.service 2>/dev/null || true
ok "systemd-zram-generator eingerichtet (halber RAM, zstd)"
else
apt-get install -y -qq zram-tools >/dev/null
printf 'ALGO=zstd\nPERCENT=50\nPRIORITY=100\n' > /etc/default/zramswap
systemctl restart zramswap 2>/dev/null || true
ok "zram-tools eingerichtet (halber RAM, zstd)"
fi
cat > /etc/sysctl.d/90-zram.conf <<'EOF'
# zram ist schnell: früh auslagern, Seiten einzeln holen
vm.swappiness=100
vm.page-cluster=0
vm.vfs_cache_pressure=200
EOF
sysctl -q --system
swapon --show | sed 's/^/ /'
# 3. Speichergrenzen für Container -------------------------------------------
echo "[3] Speichergrenzen für Container (cgroup memory)"
if grep -qw memory /sys/fs/cgroup/cgroup.controllers 2>/dev/null; then
ok "bereits verfügbar"
else
CMDLINE="$FIRMWARE/cmdline.txt"
if [ -f "$CMDLINE" ] && ! grep -q "cgroup_enable=memory" "$CMDLINE"; then
cp "$CMDLINE" "$CMDLINE.vor-projektuebersicht"
sed -i '1 s/$/ cgroup_enable=memory cgroup_memory=1/' "$CMDLINE"
ok "in $CMDLINE ergänzt (Sicherung: cmdline.txt.vor-projektuebersicht)"
NEUSTART_NOETIG=1
else
warn "nicht verfügbar — $CMDLINE prüfen"
fi
fi
# 4. GPU-Speicher -----------------------------------------------------------
echo "[4] GPU-Speicher"
CONFIG="$FIRMWARE/config.txt"
if [ -f "$CONFIG" ]; then
if grep -q "^gpu_mem=" "$CONFIG"; then sed -i 's/^gpu_mem=.*/gpu_mem=16/' "$CONFIG"
else printf '\n# Projektübersicht: kein Bildschirm, mehr RAM für Linux\ngpu_mem=16\n' >> "$CONFIG"; fi
ok "gpu_mem=16"
NEUSTART_NOETIG=1
else warn "$CONFIG nicht gefunden"; fi
# 5. Unnötige Dienste -------------------------------------------------------
echo "[5] Unnötige Dienste abschalten"
for d in multipathd multipathd.socket ModemManager udisks2 packagekit bluetooth avahi-daemon avahi-daemon.socket; do
if systemctl list-unit-files "$d.service" "$d" 2>/dev/null | grep -q "$d"; then
systemctl disable --now "$d" >/dev/null 2>&1 && ok "$d aus"
fi
done
if [ "$SNAPD_AUS" = 1 ]; then
if command -v snap >/dev/null && snap list docker >/dev/null 2>&1; then
warn "Docker ist als Snap installiert — snapd bleibt an"
else
systemctl disable --now snapd.service snapd.socket snapd.seeded.service >/dev/null 2>&1 && ok "snapd aus"
fi
else
info "snapd bleibt an (spart ~40 MB mit --snapd-aus)"
fi
# 6. earlyoom ---------------------------------------------------------------
echo "[6] earlyoom"
apt-get install -y -qq earlyoom >/dev/null
cat > /etc/default/earlyoom <<'EOF'
# Bei unter 5 % freiem RAM UND unter 10 % freiem Swap den größten Prozess
# beenden — nie SSH, Docker oder systemd. Der App-Container startet dann neu.
# Ohne innere Anführungszeichen: systemd reicht sie sonst wörtlich weiter.
EARLYOOM_ARGS="-m 5 -s 10 -r 3600 --avoid ^(sshd|dockerd|containerd|systemd|systemd-journal)$ --prefer ^node$"
EOF
systemctl enable --now earlyoom >/dev/null 2>&1
systemctl restart earlyoom
ok "aktiv"
# 7. Systemprotokoll --------------------------------------------------------
echo "[7] Systemprotokoll begrenzen"
mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=50M\n' > /etc/systemd/journald.conf.d/10-groesse.conf
systemctl restart systemd-journald
ok "höchstens 50 MB"
# 8. Nächtlicher Neustart ---------------------------------------------------
echo "[8] Nächtlicher Neustart des Containers"
cat > /etc/cron.d/projektuebersicht <<'EOF'
# Projektübersicht: frischer Speicher jede Nacht (gegen schleichende Lecks)
30 3 * * * root docker restart projektuebersicht >/dev/null 2>&1
EOF
systemctl enable docker >/dev/null 2>&1
ok "täglich 3:30 Uhr; Docker startet beim Booten"
# 9. Stromversorgung --------------------------------------------------------
echo "[9] Stromversorgung"
T=""
[ -r /sys/devices/platform/soc/soc:firmware/get_throttled ] && T=$(cat /sys/devices/platform/soc/soc:firmware/get_throttled)
command -v vcgencmd >/dev/null && T=$(vcgencmd get_throttled | cut -d= -f2)
if [ -z "$T" ]; then info "nicht auslesbar (Paket libraspberrypi-bin installieren)"
elif [ "$((T))" -eq 0 ]; then ok "keine Unterspannung seit dem Start ($T)"
else warn "Unterspannung/Drosselung erkannt ($T) — besseres Netzteil (5 V / 2,5 A) verwenden!"; fi
# 10. WLAN ------------------------------------------------------------------
# Befund 29.10.: Der Pi "stürzte" nicht ab, sondern verlor das WLAN
# (wlan0: Lost carrier) und kam danach nie wieder in das Netz
# (ASSOC-REJECT in Schleife) — erreichbar war er erst nach Stecker ziehen.
echo "[10] WLAN stabil halten"
apt-get install -y -qq iw >/dev/null 2>&1
cat > /etc/systemd/system/wlan-energiesparen-aus.service <<'EOF'
[Unit]
Description=WLAN-Energiesparen aus (Pi Zero 2 verliert sonst die Verbindung)
After=sys-subsystem-net-devices-wlan0.device
Wants=sys-subsystem-net-devices-wlan0.device
[Service]
Type=oneshot
ExecStart=/usr/sbin/iw dev wlan0 set power_save off
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
cat > /usr/local/sbin/wlan-waechter <<'EOF'
#!/bin/sh
# Prüft jede Minute, ob das Gateway erreichbar ist. Ohne Netz: alle 2
# Minuten WLAN neu verbinden, nach 10 Minuten den Pi neu starten.
ZAEHLER=/run/wlan-waechter.zaehler
GW=$(ip route | awk '/^default/{print $3; exit}')
if [ -n "$GW" ] && ping -c 2 -W 3 "$GW" >/dev/null 2>&1; then
rm -f "$ZAEHLER"; exit 0
fi
N=$(( $(cat "$ZAEHLER" 2>/dev/null || echo 0) + 1 ))
echo "$N" > "$ZAEHLER"
logger -t wlan-waechter "kein Netz seit $N Minute(n)"
if [ "$N" -ge 10 ]; then
logger -t wlan-waechter "Neustart des Pi"; systemctl reboot
elif [ $((N % 2)) -eq 0 ]; then
logger -t wlan-waechter "WLAN neu verbinden"
wpa_cli -i wlan0 reassociate >/dev/null 2>&1
if [ "$N" -ge 4 ]; then
ip link set wlan0 down; sleep 2; ip link set wlan0 up
networkctl reconfigure wlan0 >/dev/null 2>&1
fi
fi
EOF
chmod 755 /usr/local/sbin/wlan-waechter
cat > /etc/systemd/system/wlan-waechter.service <<'EOF'
[Unit]
Description=WLAN-Wächter der Projektübersicht
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/wlan-waechter
EOF
cat > /etc/systemd/system/wlan-waechter.timer <<'EOF'
[Unit]
Description=WLAN-Wächter jede Minute
[Timer]
OnBootSec=3min
OnUnitActiveSec=1min
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now wlan-energiesparen-aus.service >/dev/null 2>&1
systemctl enable --now wlan-waechter.timer >/dev/null 2>&1
if iw dev wlan0 get power_save 2>/dev/null | grep -q off; then ok "Energiesparen aus"; else warn "Energiesparen ließ sich nicht abschalten"; fi
ok "Wächter aktiv (neu verbinden ab 2 min, Neustart nach 10 min ohne Netz)"
echo
if [ "$NEUSTART_NOETIG" = 1 ]; then
echo "Fertig. Bitte den Pi jetzt neu starten: sudo reboot"
else
echo "Fertig."
fi
echo "Danach prüfen: bash Docker/pi-pruefen.sh"