Am 15. Januar 2024 habe ich einen neuen
Raspi 4 B inklusive dem
empfohlenen offiziellen Netzteil erhalten und damit begonnen, die bisher
von einem Linux-PC gesteuerten Automationen zu Hause
(Stichwort SmartHome) auf diesen Raspi
zu migrieren.
Spätestens nachdem ich in einem Forum gelesen habe, dass ein Benutzer
während den Ferien erlebt hat, dass das Netzteil seines Raspis ausgefallen
ist und in der Folge seine Steuerungen zu Hause (Jalousien,
Gartenbewässerung, etc.) komplett ausgefallen sind, beschloss ich, einen
Weg zur Hochverfügbarkeit der Home-Automation zu beschreiten.
Hier wird dieser Weg beschrieben.
Hardware
Am 21. Februar 2021 erhielt ich die neue Hardware:
- Raspberry Pi 4 8G Model B
- Official Raspberry Pi 4 Power Adapter USB-C Schwarz
- SanDisk Max Endurance 32GB
Erstmals wollte ich einen Raspi mit 8GB RAM ausprobieren und sicherheitshalber
bestellte ich das schwarze Netzteil (im Unterschied zum weissen Modell, welches
im Januar 2024 gekauft wurde); vielleicht erkaufe ich mir damit eine gewisse
Sicherheit, obwohl ich nicht weiss, ob das schwarze und weisse Netzteil bzgl.
Hardware wirklich unterschiedlich sind.
Als SD-Karte entschied ich mich für eine 'SanDisk Max Endurance' in der
Hoffnung, dass diese lange haltbar ist.
Installation des Raspberry Pi 4 B
Der neue Raspi wurde gleich eingerichtet wie derjenige vom
Januar 2024.
Positioniert wurde der neue Raspi diesmal nicht im Untergeschoss, sondern im
Obergeschoss, um eine örtliche Redundanz zu etablieren.
Zwei Raspis/Rechner zu einem Hochverfügbarkeits-System verbinden
Die zwei Raspis haben die Namen raspi und raspi2. Anstatt nur via 'ping'
zu schauen, ob beide Rechner aktiv sind, wird regelmässig je ein
File auf den anderen Rechner kopiert. Falls dieser Vorgang nicht erfolgreich
ist, setzt sich der entspechende Rechner automatisch als MASTER.
Als crontab wird auf beiden Raspis folgendes eingetragen:
*/2 * * * * $HOME/smarthome/HA/ha.sh >> $HOME/smarthome/HA/ha.log 2>>$HOME/smarthome/HA/ha.errlog
Das File 'ha.sh' sieht wie folgt aus:
Zur besseren Darstellung wurde vor dem Einfügen folgender Befehl benutzt:
cat ha.sh | > ha.text
Das HTML-File-Fragment 'ha_new.html' dient dazu, auf einer Webseite
den Status der beiden Rechner darzustellen. Ist z.B. der Rechner 'raspi'
nicht erreichbar/down, dann sieht dieses HTML-Fragment wie folgt aus:
Das File PRIMARY.MASTER hat folgenden Inhalt: PRIMARY_MASTER='raspi'
und ist auf beiden Raspis identisch. Es dient dazu, zu definieren, welcher
der beiden Rechner der Master ist, falls beide Rechner online sind.
Das File MASTER.file wird durch das oben beschriebene Script ha.sh alle
zwei Minuten neu erstellt und sieht auf dem Master so aus:
MASTER='yes' SLAVE=raspi2
MASTER='no' SLAVE=raspi
Software auf den beiden Raspis/Rechnern, die zum Hochverfügbarkeits-System
gehören
Die Software auf beiden Rechnern sollte generell identisch gehalten werden.
Dies kann z.B. nach einem Backup (siehe unten) auch mittels einem Script
überprüft werden.
Auf beiden Rechnern
wird ja alle zwei Minuten getestet, ob der andere ebenfalls funktioniert und
erreichbar ist. Dieser Status ist jeweils im File (siehe oben)
$HOME/smarthome/HA/MASTER.file gespeichert (MASTER='yes' oder
MASTER='no'). In allen Scripts auf beiden Rechnern lesen wir nun gleich zu
Beginn diesen Status wie folgt aus:
und können jeweils das via cron aufgerufene Script gleich beenden.source $HOME/smarthome/HA/MASTER.file if test "$MASTER" = "no" ; then echo "Not MASTER - exit! `date +%d.%m.%Y_%H:%M`" exit fi
Dies wurde genauso implementiert für die Steuerung der Storen bzw. Rollläden und ebenso für die automatisierte Gartenbewässerung inklusive der Sicherheitsüberwachung der Bewässerung, welche das Wasserventil schliesst, sollte es zu einer Zeit, wo keine Bewässerung vorgesehen ist, noch geöffnet sein.
Wichtig ist allerdings. dass die beiden redundanten Rechner stets auf dem aktuellen Stand der Daten sind. Wo immer in einem Script daher (auf dem MASTER) neue Daten generiert werden, wird sichergestellt, dass diese auf den SLAVE kopiert werden:
Damit ist sichergestellt, dass beide Maschinen zu jeder Zeit bezüglich der Daten auf dem gleichen Stand sind und im Falle eine 'Failovers', also des nahtlosen und automatischen Umschaltens auf das redundante System, die Arbeiten problemlos und quasi kontinuierlich mit aktuellen Daten weitergeführt werden können.scp -p -o ConnectTimeout=5 Daten.file $SLAVE:smarthome/Verzeichnis_auf_slave/
Backup der beiden redundanten Raspis/Rechner
Ein Backup der selbst geschriebenen Software ist immer eine Notwendigkeit;
am besten immer dann, wenn die Software modifiziert wurde!
Aus diesem Grund habe ich auf einem dritten Rechner ein Verzeichnis
namens "RASPIsave" eingerichtet und dort folgendes Script gespeichert:
Mittels dem Befehl ./saveRASPIS_3.sh werden danach von beiden Raspis die jeweiligen Directories auf die Unterverzeichnisse raspi bzw. raspi2 kopiert - unter Beibehaltung der Daten und Rechte.#!/bin/bash # saveRASPIS_3.sh 17Jun2024/uk # # Erstellt mit Hilfe von ChatGPT # # Verzeichnisse BASE_DIR="$HOME/RASPIsave" ##MACHINES=("raspi" "raspi2" "raspi3") MACHINES=("raspi" "raspi2") USER=$LOGNAME cd $BASE_DIR || exit 1 # Farben definieren RED='\033[0;31m' NC='\033[0m' # Keine Farbe # Funktion zum Synchronisieren von Verzeichnissen sync_dirs() { local machine=$1 local paths=( "/home/$USER" "/var/www/html" "/etc/network/if-up.d" "/usr/lib/cgi-bin" "/usr/local/bin" "/etc/webhook.conf" "/etc/systemd/system/webhook.service" ) for path in "${paths[@]}"; do local dest_dir="$BASE_DIR/$machine${path%/*}" mkdir -p "$dest_dir" # Prüfen, ob die Datei oder das Verzeichnis auf dem Remote-Server existiert ssh "$USER@$machine" "test -e $path" if [ $? -eq 0 ]; then rsync -avHx --delete "$USER@$machine:$path" "$dest_dir/" else echo -e "${RED}!!! $path does not exist on $machine${NC}" fi done } # Synchronisieren aller Maschinen for machine in "${MACHINES[@]}"; do sync_dirs "$machine" done # Finden und Auflisten von *errlog-Dateien echo " " echo "*errlog - Files grösser als Null von OCTAVE: " echo " " find "$BASE_DIR" -name "*errlog" -path "*octave*" -type f -size +0 -exec ls -l {} \; echo " " echo "*errlog - Files grösser als Null OHNE OCTAVE: " echo " " find "$BASE_DIR" -name "*errlog" ! -path "*octave*" -type f -size +0 -exec ls -l {} \; #
Mittels dem Befehl ./differ.sh werden (in diesem Fall) in den Backups der beiden Rechner jeweils die Shell-Scripts (*.sh), die Octave-Scripts (*.m) sowie die FORTRAN-Source-Codes (*.f) und die Executables (*.exe) - falls vergessen wurde, das Programm auf beiden Rechnern zu kompilieren bzw. das ausführbare Programm auf den anderen Rechner zu kopieren - der Fortran-Programme miteinander verglichen und bei Differenzen eine entsprechende Ausgabe gemacht.#!/bin/bash # differ.sh 17Jun2024/uk # # Erstellt mit Hilfe von ChatGPT # cd $HOME/RASPIsave # Verzeichnisse dir1="raspi" dir2="raspi2" # Farben definieren RED='\033[0;31m' NC='\033[0m' # Keine Farbe # Function to compare files in both directories compare_files() { ext=$1 echo "Comparing *.$ext Files:" # Check files in dir1 against dir2 for file in $(find $dir1/ -type f -name "*.$ext" | grep -v makeLink.sh | grep -v _alt | grep -v _new | cut -d"/" -f 2-); do if [ -f "$dir2/$file" ]; then if ! diff "$dir1/$file" "$dir2/$file"; then echo -e "${RED}^^^Difference found in: $dir1/$file${NC}" fi else echo -e "${RED}!!! File missing in $dir2: $file${NC}" fi done # Check files in dir2 against dir1 for file in $(find $dir2/ -type f -name "*.$ext" | grep -v makeLink.sh | grep -v _alt | grep -v _new | cut -d"/" -f 2-); do if [ ! -f "$dir1/$file" ]; then echo -e "${RED}!!! File missing in $dir1: $file${NC}" fi done } # Compare different file extensions compare_files "sh" compare_files "m" compare_files "f" compare_files "exe" # Compare specific /etc files echo "Comparing some /etc Files:" for file in etc/webhook.conf etc/systemd/system/webhook.service; do if [ -f "$dir1/$file" ]; then if [ -f "$dir2/$file" ]; then if ! diff "$dir1/$file" "$dir2/$file"; then echo -e "${RED}^^^Difference found in: $dir1/$file${NC}" fi else echo -e "${RED}!!! File missing in $dir2: $file${NC}" fi else if [ -f "$dir2/$file" ]; then echo -e "${RED}!!! File missing in $dir1: $file${NC}" fi fi done #