From 5ff3d66d763f95dc2877931b1786bb10989c8a23 Mon Sep 17 00:00:00 2001 From: Adam Carr Date: Sat, 19 Sep 2026 09:35:04 -0700 Subject: [PATCH] Initial inventory and rebuild plan for planck cluster --- README.md | 20 ++++++++++++++++++ docs/INVENTORY.md | 26 +++++++++++++++++++++++ docs/MACS.txt | 20 ++++++++++++++++++ docs/PLAN.md | 50 ++++++++++++++++++++++++++++++++++++++++++++ scripts/inventory.sh | 36 +++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 README.md create mode 100644 docs/INVENTORY.md create mode 100644 docs/MACS.txt create mode 100644 docs/PLAN.md create mode 100755 scripts/inventory.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..89a0abc --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# planck — 20-node Raspberry Pi 4 home lab + +20× Raspberry Pi 4 Model B (8GB), each booting a 240GB USB SSD. Named +`planck001`–`planck020`. Network: Ubiquiti UDM Pro, 2 switches, one router. +Synology NAS on the same LAN for shared storage. + +## Docs + +- `docs/INVENTORY.md` — hardware inventory (regenerate: `./scripts/inventory.sh > docs/INVENTORY.md`) +- `docs/MACS.txt` — eth0 MAC addresses (for DHCP reservations) +- `docs/PLAN.md` — the rebuild plan and decisions log + +## Scripts + +- `scripts/inventory.sh` — collects hardware/network inventory from all nodes + +## Node access + +SSH: `ssh adamcarr@planck0NN.local` (key auth set up 2026-09-19). mDNS +resolves `planck0NN.local` across subnets. diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md new file mode 100644 index 0000000..f411efe --- /dev/null +++ b/docs/INVENTORY.md @@ -0,0 +1,26 @@ +# Planck Cluster Inventory + +Generated: 2026-09-19T09:33:49-07:00 + +| Node | Model | RAM | SSD | IP | CPU Serial | Bootloader | Uptime | Temp | +|------|-------|-----|-----|----|-----------|------------|--------|------| +| planck001 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.30.173 | 1000000049daa411 | 2023/01/11 17:40:52 | up 22 minutes | temp=50.6'C | +| planck002 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.0.110 | 10000000e8cc396c | 2023/01/11 17:40:52 | up 22 minutes | temp=50.1'C | +| planck003 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.109 | 100000009f4be51d | 2023/01/11 17:40:52 | up 22 minutes | temp=52.5'C | +| planck004 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.250 | 10000000776d4ede | 2023/01/11 17:40:52 | up 22 minutes | temp=52.1'C | +| planck005 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.178 | 10000000ec4c160b | 2023/01/11 17:40:52 | up 23 minutes | temp=51.1'C | +| planck006 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.2 | 10000000997f08be | 2023/01/11 17:40:52 | up 23 minutes | temp=47.2'C | +| planck007 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.48 | 10000000217267c2 | 2023/01/11 17:40:52 | up 23 minutes | temp=46.7'C | +| planck008 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.43 | 10000000265acd60 | 2023/01/11 17:40:52 | up 23 minutes | temp=46.7'C | +| planck009 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.82 | 100000007a7adb3f | 2023/01/11 17:40:52 | up 23 minutes | temp=45.7'C | +| planck010 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.91 | 100000003e589194 | 2023/01/11 17:40:52 | up 23 minutes | temp=47.2'C | +| planck011 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.0.185 | 10000000d84a8d2d | 2023/01/11 17:40:52 | up 23 minutes | temp=46.7'C | +| planck012 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.0.235 | 10000000697a8fa2 | 2023/01/11 17:40:52 | up 23 minutes | temp=47.7'C | +| planck013 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.230 | 1000000080d1dcba | 2023/01/11 17:40:52 | up 23 minutes | temp=46.7'C | +| planck014 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.4 | 10000000b5044238 | 2023/01/11 17:40:52 | up 23 minutes | temp=44.8'C | +| planck015 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.31 | 10000000a0dcbc5a | 2023/01/11 17:40:52 | up 23 minutes | temp=43.3'C | +| planck016 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.94 | 100000004a07a5bc | 2023/01/11 17:40:52 | up 23 minutes | temp=48.7'C | +| planck017 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.94 | 10000000da3159b0 | 2023/01/11 17:40:52 | up 23 minutes | temp=48.7'C | +| planck018 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.102 | 100000000c40bc0d | 2023/01/11 17:40:52 | up 23 minutes | temp=48.7'C | +| planck019 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.2.32 | 10000000ac5463d6 | Feb 16 2021 13:23:36 | up 23 minutes | temp=48.2'C | +| planck020 | Raspberry Pi 4 Model B Rev 1.4 | 8GB | 238.5G/Tech | 192.168.1.23 | 10000000c123eb6e | 2023/01/11 17:40:52 | up 23 minutes | temp=48.2'C | diff --git a/docs/MACS.txt b/docs/MACS.txt new file mode 100644 index 0000000..cbb9ec6 --- /dev/null +++ b/docs/MACS.txt @@ -0,0 +1,20 @@ +planck001 dc:a6:32:e9:7f:75 +planck002 dc:a6:32:e9:7f:de +planck003 dc:a6:32:e9:7d:fa +planck004 dc:a6:32:e9:7f:7b +planck005 dc:a6:32:e9:7f:33 +planck006 dc:a6:32:e9:7d:8b +planck007 dc:a6:32:e9:7d:bd +planck008 dc:a6:32:e9:7e:b2 +planck009 dc:a6:32:e9:7d:df +planck010 dc:a6:32:e9:7e:e2 +planck011 dc:a6:32:e9:7d:46 +planck012 dc:a6:32:e9:7d:78 +planck013 dc:a6:32:e9:7f:67 +planck014 dc:a6:32:e9:78:d1 +planck015 dc:a6:32:e9:78:ec +planck016 dc:a6:32:e9:7e:e5 +planck017 dc:a6:32:e9:7d:eb +planck018 dc:a6:32:e9:7e:ed +planck019 dc:a6:32:e9:7e:a6 +planck020 dc:a6:32:e9:7d:a4 diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000..874d0fc --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,50 @@ +# Rebuild plan & decisions log + +## Where we started (2026-09-19) + +- Old cluster: Docker Swarm (manager planck001) with Portainer, Traefik, and + what appears to be CapRover remnants. Broken: workers stuck in "pending" + because the manager's IP changed (workers dial stale `192.168.1.173:2377`). +- Decision: **wipe everything and rebuild clean** rather than repair. + +## Hardware + +- 20× Raspberry Pi 4 Model B Rev 1.4, 8GB RAM, 4 cores +- Boot: 238.5GB USB SSD on every node (no SD cards installed) +- Bootloader EEPROM: 2023/01/11 on 19 nodes; planck019 has Feb 2021 (update it) +- All nodes healthy: temps 43–52°C, disks ~4% used + +## Network + +- Ubiquiti UDM Pro, 2 switches, all nodes on the same router +- IPs currently drift across 192.168.0/1/2.x subnets via DHCP — planck001 sits + on 192.168.30.x (different subnet, cause unknown, doesn't matter: we're + rebuilding) +- Decision: **dedicated static addressing for the cluster** via UniFi DHCP + reservations (MAC table in `docs/MACS.txt`) + +## Storage + +- Synology NAS on same LAN — shared storage target (NFS) for cluster + workloads + +## Phases + +- [x] Phase 0 — Access + inventory (SSH keys on all 20, docs/) +- [ ] Phase 1 — Network design: static IPs / reservations in UniFi +- [ ] Phase 2 — Reimage all 20 nodes with fresh Raspberry Pi OS +- [ ] Phase 3 — Cluster runtime install (candidates: k3s vs Docker Swarm) +- [ ] Phase 4 — Storage integration with Synology (NFS CSI) +- [ ] Phase 5 — Apps: monitoring, Portainer/UI, whatever the lab is for + +## Open decisions + +1. **Reimage method** — options: + a. PXE/network boot: EEPROM boot-order set to netboot; DHCP next-server + points at a TFTP/HTTP installer. Zero-touch per node, reusable forever. + Installer server can run on the Synology (Docker) or a laptop. + b. SD-card bootstrap installer: flash one SD image, insert per node, it + wipes + installs the USB SSD, removes itself. Simple, physical walk. + c. Pull SSDs, image on a PC with Raspberry Pi Imager ×20. Most manual. +2. **Cluster runtime** — k3s (lighter, k8s-compatible, we manage it with + automation) vs Docker Swarm + Portainer (simplest). diff --git a/scripts/inventory.sh b/scripts/inventory.sh new file mode 100755 index 0000000..bac9020 --- /dev/null +++ b/scripts/inventory.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Collects hardware + network inventory from all planck nodes. +# Usage: ./scripts/inventory.sh > docs/INVENTORY.md +set -u + +FIRST=1 +NODES="${NODES:-20}" + +echo "# Planck Cluster Inventory" +echo "" +echo "Generated: $(date -Is)" +echo "" +echo "| Node | Model | RAM | SSD | IP | CPU Serial | Bootloader | Uptime | Temp |" +echo "|------|-------|-----|-----|----|-----------|------------|--------|------|" + +for i in $(seq -w 1 "$NODES"); do + host="planck0${i}.local" + info=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "adamcarr@${host}" ' + model=$(tr -d "\0" < /proc/device-tree/model) + ram=$(awk "/MemTotal/ {printf \"%.0fGB\", \$2/1024/1024}" /proc/meminfo) + ssd=$(lsblk -dn -o SIZE,MODEL /dev/sda 2>/dev/null | tr -s " " | tr " " "/") + ip=$(ip -4 -o addr show eth0 | awk "{print \$4}" | cut -d/ -f1) + serial=$(grep -m1 Serial /proc/cpuinfo | awk "{print \$3}") + bl=$(vcgencmd bootloader_version 2>/dev/null | head -1) + up=$(uptime -p) + temp=$(vcgencmd measure_temp 2>/dev/null || cat /sys/class/thermal/thermal_zone0/temp) + echo "${model}|${ram}|${ssd}|${ip}|${serial}|${bl}|${up}|${temp}" + ' 2>/dev/null) + if [ -z "$info" ]; then + echo "| planck0${i} | UNREACHABLE | | | | | | |" + else + IFS='|' read -r model ram ssd ip serial bl up temp <<< "$info" + sep=$FIRST && FIRST=0 + echo "| planck0${i} | ${model} | ${ram} | ${ssd} | ${ip} | ${serial} | ${bl} | ${up} | ${temp} |" + fi +done