269 lines
10 KiB
Markdown
269 lines
10 KiB
Markdown
# NenjimHub: Alpine, valgfri rå swap og OpenZFS
|
|
|
|
Designet bruger en klassisk MBR-partitionstabel:
|
|
|
|
- partition 1: FAT32 til Alpine diskless, APKoVL og APK-cache
|
|
- partition 2: valgfri rå Linux-swap
|
|
- partition 3: resten af kortet som OpenZFS-poolen `rpool`
|
|
|
|
ZFS er altid partition 3. Hvis den rå swapstørrelse er `0`, oprettes partition
|
|
2 ikke; partitionsnummer 2 står blot tomt. Det gør layoutet entydigt for alle
|
|
profiler.
|
|
|
|
`flash.sh` skriver kun til partition 1. Partition 2 og 3 ændres ikke, så en
|
|
senere Alpine-opdatering ikke overskriver swap eller ZFS-data.
|
|
|
|
## Filer i samme mappe
|
|
|
|
```text
|
|
alpine-rpi-3.24.1-aarch64.img
|
|
flash.sh
|
|
headless.apkovl.tar.gz
|
|
installZeroTier.sh
|
|
NenjimHub.conf
|
|
prepare-storage.sh
|
|
unattended.sh
|
|
wpa_supplicant.conf
|
|
zerotier-one-1.16.2-linux-musl-aarch64-sso0 # valgfri
|
|
zulu25.36.205-ca-jre25.0.4.1-linux_musl_aarch64.tar.gz
|
|
```
|
|
|
|
`prepare-storage.sh` downloader som udgangspunkt det officielle statiske
|
|
`zrepl-linux-arm64`-program fra zrepl v0.7.0 og kontrollerer SHA-256-værdien fra
|
|
GitHub. Det kan i stedet placeres lokalt ved siden af scriptet med navnet
|
|
`zrepl-linux-arm64`.
|
|
|
|
## Fælles konfiguration
|
|
|
|
Ret kun `NenjimHub.conf`; både `prepare-storage.sh`, `flash.sh` og første boot
|
|
bruger den samme fil. `DEVICE` skal være hele SD-kortet, eksempelvis `/dev/sdc`,
|
|
og aldrig en partition som `/dev/sdc1`.
|
|
|
|
Standardprofilen til Raspberry Pi Zero 2W er:
|
|
|
|
```ini
|
|
DEVICE="/dev/sdc"
|
|
NENJIMHUB_HOSTNAME="alpine-nenjim"
|
|
CONSOLE_KEYMAP_LAYOUT="dk"
|
|
CONSOLE_KEYMAP_VARIANT="dk"
|
|
SYSTEM_LOCALE="C.UTF-8"
|
|
|
|
VIRT_ZVOL_SIZE_MIB="4096"
|
|
|
|
SWAP_SIZE_MIB_PART="2048"
|
|
SWAP_SIZE_MIB_ZFS="0"
|
|
SWAP_SIZE_PCT_ZRAM="25"
|
|
|
|
SWAP_PRI_ZRAM="300"
|
|
SWAP_PRI_ZFS="200"
|
|
SWAP_PRI_PART="100"
|
|
|
|
ZEROTIER_ENABLE_SSO="0"
|
|
ZEROTIER_VERSION="1.16.2"
|
|
```
|
|
|
|
Sæt `NENJIMHUB_HOSTNAME` til et unikt navn for hvert apparat, eksempelvis
|
|
`nenjimhub-01`. Værdien må være ét hostname-led på 1-63 bogstaver, tal eller
|
|
bindestreger og må ikke begynde eller slutte med en bindestreg.
|
|
|
|
`CONSOLE_KEYMAP_LAYOUT="dk"` og `CONSOLE_KEYMAP_VARIANT="dk"` vælger det
|
|
almindelige danske PC-tastatur på Linux-konsollen. Første boot kontrollerer
|
|
keymap-navnet før `setup-alpine`, så en stavefejl stopper med en tydelig fejl i
|
|
stedet for at åbne en interaktiv menu.
|
|
|
|
`SYSTEM_LOCALE="C.UTF-8"` giver et neutralt UTF-8-loginmiljø uden at vælge
|
|
engelske eller danske dato- og talformater. Installationen aktiverer samtidig
|
|
Unicode på Linux-konsollen.
|
|
|
|
En swaptype slås fra ved at sætte dens størrelse eller procent til `0`.
|
|
Prioritet `0` er derimod en gyldig Linux-swapprioritet og slår ikke noget fra.
|
|
|
|
PC'en skal have OpenZFS 2.4.3, ZFS-kernemodulet indlæst, FAT/ext4-værktøjer,
|
|
Python 3 og enten Podman eller Docker. Minimumskapaciteten beregnes ud fra
|
|
konfigurationen; standardprofilen er beregnet til et SD-kort solgt som 16 GB.
|
|
|
|
## Trin 1: klargør hele kortet
|
|
|
|
```bash
|
|
sudo ./prepare-storage.sh
|
|
```
|
|
|
|
Dette trin sletter hele SD-kortet. Det opretter de valgte partitioner,
|
|
`rpool`, datasets `rpool/home`, `rpool/software` og `rpool/sysop` samt det
|
|
ext4-formaterede zvol `rpool/virt`. Java og zrepl lægges på
|
|
`rpool/software`.
|
|
|
|
Størrelsen på `rpool/virt` styres særskilt af `VIRT_ZVOL_SIZE_MIB`; den har
|
|
ingen forbindelse til ZFS-swapstørrelsen.
|
|
|
|
## Trin 2: installer eller opdater Alpine
|
|
|
|
```bash
|
|
sudo ./flash.sh
|
|
```
|
|
|
|
Dette trin erstatter kun partition 1. Det bygger på PC'en en matchende Alpine
|
|
Raspberry Pi-kernel/modloop med `zfs-rpi` og lægger first-boot-filerne ind.
|
|
Som sikkerhed skal du skrive `NENJIMHUB`, når scriptet beder om bekræftelse.
|
|
|
|
Scriptet gemmer også PC'ens aktuelle UTC-tid på kortet, så Pi'en uden RTC kan
|
|
validere HTTPS-certifikater, inden netværkstiden er synkroniseret. Vent på
|
|
slutteksten `Alpine partition 1 is ready`.
|
|
|
|
Ved en senere Alpine-opdatering køres kun trin 2. `flash.sh` bevarer `/sysop`
|
|
fra det gamle APKoVL, mens alle ZFS-data ligger urørt på partition 3.
|
|
|
|
## Første boot
|
|
|
|
Pi'en gør følgende automatisk:
|
|
|
|
1. aktiverer den rå swap-partition, hvis den er konfigureret
|
|
2. aktiverer midlertidig ZRAM til installationen, hvis ZRAM er konfigureret
|
|
3. importerer `rpool` fra partition 3
|
|
4. opretter og aktiverer `rpool/swap`, hvis dens størrelse er større end `0`
|
|
5. installerer pakker og genbruger en prekompileret ZeroTier-binærfil eller
|
|
bygger ZeroTier med ét compile-job, hvis filen ikke findes
|
|
6. skriver den endelige ZRAM-størrelse, opretter APKoVL og genstarter
|
|
|
|
`rpool/swap` oprettes på Pi'en, så `volblocksize` kan følge den faktiske værdi
|
|
fra `getconf PAGESIZE`. ZeroTier-kontrollen summerer al aktiv swap; den er ikke
|
|
afhængig af, om pladsen kommer fra partition, ZRAM eller ZFS.
|
|
|
|
På Pi Zero 2W er `ZEROTIER_ENABLE_SSO="0"` standard. Det udelader den meget
|
|
store Rust/Cargo-toolchain og bygger ZeroTier med GCC og ét job. Sæt værdien til
|
|
`1`, hvis hubben senere skal kunne bruge ZeroTier-netværk, som kræver SSO; det
|
|
gør førstegangsinstallationen væsentligt tungere.
|
|
|
|
### Genbrug den færdigbyggede ZeroTier-fil
|
|
|
|
Når en Pi har bygget ZeroTier færdig, kopieres binærfilen til PC'en fra den
|
|
persistente ZFS-placering. Kør dette på PC'en i mappen med scripts og erstat
|
|
`PI-ADRESSE` med Pi'ens IP-adresse:
|
|
|
|
```bash
|
|
scp sysop@PI-ADRESSE:/usr/local/software/zerotier/current/zerotier-one \
|
|
./zerotier-one-1.16.2-linux-musl-aarch64-sso0
|
|
chmod 0755 ./zerotier-one-1.16.2-linux-musl-aarch64-sso0
|
|
```
|
|
|
|
Ved næste kørsel kopierer `flash.sh` automatisk filen til kortet. Første boot
|
|
installerer den direkte og testkører den på Pi'en; både arkitektur og den
|
|
forventede version skal passe. Filnavnet indeholder også SSO-profilen, så et
|
|
SSO-build ikke forveksles med standardbuildet uden SSO.
|
|
|
|
Hvis den navngivne fil ikke findes ved siden af `flash.sh`, falder installationen
|
|
automatisk tilbage til lokal kompilering på Pi'en. Det er derfor nok at slette
|
|
eller flytte filen på PC'en, før et nyt SD-kort klargøres, for at fremtvinge en
|
|
ny kompilering.
|
|
|
|
`tmux`, `htop` og `btop` installeres i den tidlige pakkegruppe. Pakken
|
|
`kitty-terminfo` gør, at de kan bruges direkte fra Kitty med
|
|
`TERM=xterm-kitty`, og `musl-locales` plus `SYSTEM_LOCALE` giver UTF-8 til
|
|
tekstbaserede programmer. Installationen kan
|
|
følges lokalt sådan:
|
|
|
|
```bash
|
|
tmux new-session -s install
|
|
tail -f /media/mmcblk0p1/NenjimHub-Install.log
|
|
```
|
|
|
|
Når brugeren `nenjim` er oprettet, findes samme log her:
|
|
|
|
```bash
|
|
tail -f /var/log/NenjimHub-Install.log
|
|
```
|
|
|
|
Den sidste linje før genstart viser den samlede tid fra start af unattended
|
|
installation til reboot, målt monotont og vist som `HH:MM:SS`.
|
|
|
|
På normale boots venter `nenjim-wifi-ready` op til 60 sekunder på `wlan0`, før
|
|
Wi-Fi og networking startes. Tilsvarende venter `nenjim-virt` på det asynkront
|
|
oprettede `/dev/zvol/rpool/virt`-link, før ext4 kontrolleres og Docker samt
|
|
containerd får adgang til deres datalager. Begge ventetider er afgrænsede, så
|
|
en hardware- eller udev-fejl ikke kan blokere boot uendeligt.
|
|
|
|
Loginprompten på HDMI-konsollens `tty1` startes af BusyBox `init` via
|
|
`/etc/inittab`, ikke som en OpenRC-service. Installationen sikrer og gemmer
|
|
denne entry, så `sysop` kan logge ind lokalt efter boot.
|
|
|
|
Inden det endelige APKoVL oprettes, køres en fuld `apk cache sync`. Det er
|
|
vigtigt i diskless mode, fordi også pakker som `eudev`, der blev installeret
|
|
før den persistente cache blev aktiveret, skal ligge som APK-filer på
|
|
bootpartitionen. De egne storage- og Wi-Fi-services bruger `udev-settle` som
|
|
valgfri rækkefølge og ikke som en hård afhængighed.
|
|
|
|
`apk cache sync` er den autoritative cachekontrol. Tomme metadatapakker som
|
|
`udev-init-scripts` kræver ikke nødvendigvis en separat payloadfil med samme
|
|
navn; installationen kontrollerer derfor de leverede udev-services frem for
|
|
at gætte cachefilnavne.
|
|
|
|
Konti efter installation:
|
|
|
|
```text
|
|
sysop / sysop administrator via password-beskyttet doas og Docker-gruppen
|
|
nenjim / nenjim almindelig bruger uden wheel, doas og Docker-adgang
|
|
```
|
|
|
|
Medlemskab af Docker-gruppen er i praksis root-adgang. Det gives derfor kun
|
|
til `sysop`; `nenjim` fjernes eksplicit fra både `wheel` og `docker`.
|
|
|
|
Direkte root-login er låst på både normal konsol og SSH. Skift de to
|
|
standard-passwords, før maskinen placeres på et netværk, som andre har adgang
|
|
til.
|
|
|
|
## Persistente placeringer
|
|
|
|
```text
|
|
/etc/NenjimHub.conf kopi af den anvendte provisioningprofil
|
|
/home rpool/home
|
|
/usr/local/software rpool/software
|
|
/media/sysop rpool/sysop
|
|
/media/virt ext4 på rpool/virt
|
|
/var/lib/docker -> /media/virt/docker
|
|
/var/lib/containerd -> /media/virt/containerd
|
|
/sysop/zerotier -> /media/sysop/zerotier
|
|
/sysop/zrepl -> /media/sysop/zrepl
|
|
/etc/zrepl/zrepl.yml -> /sysop/zrepl/zrepl.yml
|
|
```
|
|
|
|
Selve `/sysop` ligger i Alpine/APKoVL, så sysop stadig kan logge ind, hvis ZFS
|
|
ikke kan importeres. De vigtige ZeroTier- og zrepl-data ligger på ZFS.
|
|
|
|
zrepl starter med den gyldige no-op-konfiguration `jobs: []`. Efter redigering
|
|
kan konfigurationen kontrolleres og servicen genstartes sådan:
|
|
|
|
```bash
|
|
doas zrepl configcheck
|
|
doas rc-service zrepl restart
|
|
```
|
|
|
|
## Swap-profiler og prioriteter
|
|
|
|
Linux foretrækker det højeste prioritetsnummer. Scripts accepterer enhver
|
|
selvstændig prioritet fra 0 til 32767. Derfor kan ZFS-swap godt gives højere
|
|
prioritet end partitions-swap. Første boot skriver en advarsel, hvis dette
|
|
valg bruges, men afviser det ikke.
|
|
|
|
En profil til en Pi med mere RAM kan eksempelvis være:
|
|
|
|
```ini
|
|
SWAP_SIZE_MIB_PART="1024"
|
|
SWAP_SIZE_MIB_ZFS="4096"
|
|
SWAP_SIZE_PCT_ZRAM="75"
|
|
|
|
SWAP_PRI_ZRAM="300"
|
|
SWAP_PRI_ZFS="200"
|
|
SWAP_PRI_PART="100"
|
|
```
|
|
|
|
ZRAM-størrelsen beregnes ved hver normal opstart ud fra `MemTotal`; pladsen
|
|
bruges kun efter behov. Under førstegangsinstallationen anvendes midlertidigt
|
|
100 % logisk ZRAM-størrelse, hvorefter den ønskede procent gemmes til næste
|
|
boot. Sættes `SWAP_SIZE_PCT_ZRAM="0"`, oprettes og aktiveres ZRAM slet ikke.
|
|
|
|
Swap på et ZFS-zvol kan under ekstremt hukommelsespres skabe en uheldig
|
|
afhængighed mellem ZFS og swap. Den rå partition er derfor den sikreste høje
|
|
belastningsbuffer på Pi Zero 2W. På en større Pi kan ZFS-swap prioriteres højere,
|
|
hvis det er det ønskede kompromis. ZFS ARC begrænses separat via
|
|
`ZFS_ARC_MAX_MIB`.
|