Quantcast
Channel: thomas-leister.de

Rock64 Mini NAS Upgrade (2022)

$
0
0

Nachdem mein Mini NAS auf Basis eines Pine Rock64 Single Board Computers (SBC) 4 Jahre lang gute Dienste verrichtet hat, war es an der Zeit, dem Setup ein Upgrade zu verpassen. Das NAS wird mittlerweile nicht nur mehr nur für Backups genutzt, sondern auch ein kleiner DNLA-Medienserver im lokalen Netzwerk. Daher war es mir wichtig, das NAS so umzubauen, dass

  1. Die Datensicherheit gewährleistet ist, also keine Daten durch Fehler verfälscht werden oder verloren gehen
  2. Mehr Speicherplatz zur Verfügung steht

Ich habe mich entschieden, die eine SSD mit einem Terabyte Speicherplatz durch zwei SSDs mit jeweils 2 TB Speicherplatz zu ersetzen und diese in einem RAID-1 Verbund laufen zu lassen. Als Dateisystem sollte ZFS eingesetzt werden. Für beide SSDs sollte ein ansehliches Gehäuse gefunden werden, das ich neben mein transparentes und halboffenes Rock64 Acrylgehäuse stellen kann.

Das Rock64 auf dem neuen SSD-Gehäuse
Das Rock64 auf dem neuen SSD-Gehäuse

ZFS als Dateisystem

ZFS nutze ich auf den großen Servern schon einige Jahre und bin begeistert von dessen Featureset und der Zuverlässigkeit. Besonders schön: Mit wenigen Kommandos übernimmt ZFS nicht nur die Rolle eines klassischen Dateisystems (mit speziellen Funktionen, wie z.B. Snapshots), sondern auch die Funktion eines Volume-Managers (Z.b: LVM) und eines RAID-Managers (MD). Und weil das noch nicht genug ist, übernimmt es in meinem Fall auch die Verschlüsselungs-Funktion, die man üblicherweise LUKS anvertraut. Alles in einem zu haben, gibt mir ein gutes Gefühl. Ich bin kein Fan von mehreren Schichten und Komplexität, wo sie nicht sein muss.

SSDs und Gehäuse

Bei den SSDs habe ich mich für die von mir geschätzten und bekannten Crucial BX 500 (2 TB) entschieden. Ich habe sowohl mit den MX500 als auch mit den BX500 langjährige, gute Erfahrungen gemacht - auch im RAID-Verbund und mit ZFS. Die BX500 unterscheiden sich maßgeblich in ihrer Schreibfestigkeit und der Garantie - hier liegen die MX500 leicht vorn. Da ich auf dem NAS aber ohnehin nicht exzessiv viel schreiben werde, habe ich mir einige Euro gespart und zu den BX500 gegriffen. Bevor ich die `Totel Terabytes Written" erreicht haben werde, werde ich vermutlich schon ein neues NAS System zusammengebaut oder zumindest mehr Speicherbedarf haben.

Oh - und warum überhaupt SSDs und nicht viel günstigere HDDs? Darum:

  • SSDs sind schnell
  • SSDs sind robust
  • SSDs brauchen wenig Energie
  • SSDs sind leise
  • HDDs kommen mir antik vor. Ja, vielleicht nicht zu recht, aber da bin ich eitel.

Das Gehäuse - ein IcyBox “RAID Gehäuse für 2x HDD/SSD mit USB 3.1” hat mich gleich begeistert, weil es nicht nur gut aussieht und kompakt ist, sondern mit USB 3.1 auch eine schnelle Anbindung bietet. Da die beiden SSDs im RAID laufen, wird die USB Schnittstelle doppelt belastet. Mit USB 2 würde man hier sehr schnell an unangenehme Grenzen stoßen. USB 3.1 wird zwar von Rock64 noch nicht untersützt, aber zumindest kann ich auf USB 3.0 zurückgreifen und mich freuen, dass mein Gehäuse zukunftsfähig ist … falls das Rock64 Board einmal gegen ein stärkeres ausgetauscht wird.

Die eingebaute RAID-Funktionalität des Gehäuses interessiert mich übrigens nicht besonders. Ich nutze das Gehäuse nur im “Single” Betrieb. Die beiden SSDs erscheinen für das Betriebssystem also als zwei voneinander unabhängige Laufwerke. Für die RAID-Funktionalität vertraue ich auf ZFS sehr viel mehr, als auf ein günstiges Festplattengehäuse. Zudem dürfte sich eine Wiederherstellung schwierig gestalten, wenn der integrierte RAID Controller einmal Schaden nehmen sollte …

Das Rock64 mit neuem SSD-Gehäuse im 19" Rack
Das Rock64 mit neuem SSD-Gehäuse im 19" Rack

Ein Armbian-Update

Mein Mini NAS läuft seit jeher mit Armbian. Beim ersten Anstecken der SSDs gab es jedoch einen Schreckmoment: Während die SSDs an meinem Laptop tadellos als zwei Blockdevices erkannt wurden, passierte am Rock64 USB 3.0 Anschluss nichts. Am USB 2.0 Anschluss hingegen wurden die SSDs erkannt.

Da sowieso ein Armbian-Upgrade auf eine neuere Version anstand und für diese Version auch noch keine ZFS-Pakete bereitstanden, nutzte ich die Gelegenheit zu einem Armbian-Upgrade - und siehe da: Nun wurden auch die SSDs korrekt erkannt. Glück gehabt!

ZFS Installieren

Die Installation von ZFS auf einer neuen Armbian-Version gestaltet sich sehr einfach (wenn auch etwas zeitintensiv). Da ZFS - anders als die anderen üblichen Dateisysteme - aus Lizenzgründen nicht im Linux-Kernel enthalten ist, muss es “out of tree” als Kernelmodul gebaut werden. Das nimmt einem das zfs-dkms Paket ab, welches über die Armbian-Paketquellen bereitsteht. Damit der Kompiliervorgang erfolgreich durchgeführt werden kann, müssen zuerst die Linux-Headerdateien für Armbian installiert werden. Die Installation führt über armbian-config:

$ armbian-config
Dann "Software" > "Install Headers"

Schließlich können endlich die ZFS-Pakete installiert werden:

ZFS Pakete installieren:

apt install zfsutils-linux zfs-dkms

Im Zuge der Paketinstallation wird ein dynamisches Kernelmodul für ZFS gebaut - also nicht wundern, wenn der Schritt “Building initial module for 5.15.72-rockchip64” einige Minuten dauert. Dem Rock64 geht dabei ziemlich die Luft aus …

Nach der Installation kann mittels zfs list nachgesehen werden, ob der Dateisystemtreiber korrekt in den Kernel integriert ist. Erscheint keine Fehlermeldung, ist ZFS bereit. Andernfalls muss das Rock64 evtl. neu gestarter werden.

Verschlüsselten RAID-1 ZFS Pool erzeugen

Nun ist es Zeit, sich um die Verschlüsselung zu kümmern: Persönliche Daten auf dem NAS sollen auch nach der zukünftigen Entsorgung meiner SSDs privat bleiben. Hierzu will ich die SSDs per default Verschlüsseln (wobei sich aber für gewisse ZFS Volumes auch Ausnahmen definieren lassen).

Die Entschlüsselung geschieht ohne weiteres Zutun beim Booten über eine Key-Datei, die im Root-Filesystem des Rock64 (der SD-Karte) liegen. Die Schlüsseldatei wird wie folgt erzeugt und abgelegt:

mkdir /var/lib/zfs/
tr -dc A-Za-z0-9 </dev/urandom | head -c 16 > /var/lib/zfs/raid2tb.key
chmod 600 /var/lib/zfs/raid2tb.key

Es empfiehlt sich, die Schlüsseldatei kurz mittels cat auszugeben, um sich den Schlüssel in einem Passwortmanager wegzusichern. Geht diese Datei bzw. ihr Inhalt verloren, können die auf den SSDs befindlichen Daten nicht wiederhergestellt werden.

Nun wird der ZFS-Pool generiert: Wie schon beschrieben - mit RAID-1 und aktiver Verschlüsselung (per default).

Pool erstellen:

zpool create \
-o feature@encryption=enabled \
-O encryption=on \
-O atime=off \
-O keylocation=file:///var/lib/zfs/raid2tb.key \
-O keyformat=passphrase \
raid2tb mirror \
/dev/disk/by-id/ata-CT2000BX500SSD1_2237E6655828 \
/dev/disk/by-id/ata-CT2000BX500SSD1_2237E665585F

(Achtung, erste -o Option is klein geschrieben!) - atime=off schaltet “access time” feature ab

Damit die automatische Entschlüsselung beim Boot funktioniert, wird eine neue systemd-Servicedatei angelegt unter /etc/systemd/system/zfs-load-key.service:

[Unit]
Description=Load encryption keys
DefaultDependencies=no
After=zfs-import.target
Before=zfs-mount.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/zfs load-key -a
StandardInput=tty-force
[Install]
WantedBy=zfs-mount.service

Dann wird der neue Service aktiviert:

systemctl daemon-reload
systemctl enable zfs-load-key

ZFS Volumes erstellen

In meinem Fall soll es zunächst zwei ZFS Volumes und daher zwei Mountpoints geben: Ein Volume für Backup-Daten und ein zweites als Medienspeicher für den DLNA-/Medienserver.

mkdir -p /mnt/raid2tb/backup
mkdir -p /mnt/raid2tb/media

ZFS Datasets anlegen:

zfs create \
-o mountpoint=/mnt/raid2tb/backup \
raid2tb/backup
zfs create \
-o mountpoint=/mnt/raid2tb/media \
raid2tb/media

Automatisch erstellten Mountpoint für Stamm-Dataset entfernen (wird nicht benötigt):

zfs set mountpoint=none raid2tb

Die ZFS Volumes werden automatisch nach dem Erzeugen an den Mountpoints gemountet und sind einsatzbereit.

Performance mit Verschlüsselung

SSDs im Mirror-Betrieb (RAID-1) und verbunden durch USB 3.0 versprechen hohe Geschwindigkeiten beim Lesen und Schreiben, oder?

“Rohes” Lesen von nur einer Festplatte - ohne Beachtung des Dateisystems:

hdparm -tT --direct /dev/sda
/dev/sda:
Timing O_DIRECT cached reads: 628 MB in 2.00 seconds = 313.31 MB/sec
Timing O_DIRECT disk reads: 976 MB in 3.01 seconds = 324.77 MB/sec

Das sieht schon mal ganz ordentlich aus! Definitiv mehr, als ich über die Gigabit-Ethernet Schnittstelle des Rock64 übertragen kann (brutto theoretisch max. 120 MB/s). Hier haben wir also kein Problem.

Deutlich trauriger sieht die Situation aber aus, wenn ich eine Datei vom verschlüsselten ZFS Dataset auslese:

Lesen:

fio --rw=read --name=test --size=1G --direct=1
Run status group 0 (all jobs):
READ: bw=9184KiB/s (9404kB/s), 9184KiB/s-9184KiB/s (9404kB/s-9404kB/s), io=1024MiB (1074MB), run=114175-114175msec

Schreiben:

fio --rw=write --name=test --size=1G --direct=1
Run status group 0 (all jobs):
WRITE: bw=10.6MiB/s (11.2MB/s), 10.6MiB/s-10.6MiB/s (11.2MB/s-11.2MB/s), io=1024MiB (1074MB), run=96264-96264msec

Nur 9.4 MB/s beim Lesen und 11.2 MB/s beim Schreiben? Wie kann das sein? Die - zugegeben - sehr ernüchternde Performance wird durch die Verschlüsselung verursacht. Wie sich später herausstellte, beherrscht OpenZFS die ARM AES-Instuktionen für die hardwarebeschleunigte Verschlüsselung noch nicht und greift daher auf eine Software-basierte Verschlüsselung zurück. Diese ist Größenordnungen langsamer, als wir es von HW-beschleunigter Verschlüsselung kennen.

EIn Rückschlag, den ich nicht erwartet habe. Es gibt aber etwas Trost, denn die bereits aktivierte Verschlüsselung lässt sich bei Bedarf für einzelne ZFS Volumes auch ausschalten. Zum Beispiel für mein “media” Volume, bei dem ich sehr gut auf Verschlüsselung verzichten kann. Denn ob nun jemand in der Lage ist, Fernsehserien, Filme oder Musik aus meinen SSDs zu extrahieren, ist mir relativ egal.

Verschlüsselung wieder ausschalten

Für das “media” Dataset soll die Verschlüsselung also abgeschaltet werden. Allerdings muss dazu ein neues Dataset ohne Verschlüsselung erstellt werden, denn die Verschlüsselung kann nicht einfach abgeschaltet werden. Einmal beim Generierten eingeschaltet, bleibt sie für ein Dataset immer aktiv. Daher benennen wir das alte Dataset einfach um und erzeugen ein neues ohne Verschlüsselung:

zfs rename raid2tb/media raid2tb/media-old

Mountpoint des alten Datasets ändern:

zfs set mountpoint=/mnt/raid2tb/media-old raid2tb/media-old

Neues Dataset ohne Verschlüsselung erstellen:

zfs create -o encryption=off -o mountpoint=/mnt/raid2tb/media raid2tb/media

Daten verschieben (falls vorhanden):

mv /mnt/raid2tb/media-old/* /mnt/raid2tb/media/

Performance ohne Verschlüsselung

Nun nochmal ein Performancetest ohne Verschlüsselung:

Lesen:

fio --rw=read --name=test --size=1G --direct=1
[...]
Run status group 0 (all jobs):
READ: bw=123MiB/s (128MB/s), 123MiB/s-123MiB/s (128MB/s-128MB/s), io=1024MiB (1074MB), run=8357-8357msec

Schreiben:

fio --rw=write --name=test --size=1G --direct=1
[...]
Run status group 0 (all jobs):
WRITE: bw=61.5MiB/s (64.5MB/s), 61.5MiB/s-61.5MiB/s (64.5MB/s-64.5MB/s), io=1024MiB (1074MB), run=16657-16657msec

128 MB/s und 64.5 MB/s. Das ist zwar immer noch weit vom theoretischen Maximum der SSDs entfernt (irgendwo bei ca 500 MB/s), allerdings kann man hiermit schon gut arbeiten. Beim Lesen wäre ohnehin das Ethernet der Flaschenhals und auch mit der Schreibrate von nur 61 MB/s kann ich mich arrangieren, wenn ich bedenke, dass die meisten meiner Dateiübertragungen über das SSH Protokoll laufen und dadurch sowieso schon auf ca. 26 MB/s limitiert sind (ähnliches Problem: Auch bei SSD kommt das Rock64 mit der SSH-Verschlüsselung kaum hinterher).

Die geringe Schreib-/Leserate bei verschlüsselten Datasets bleibt ernüchternd. Hier muss ich wohl auf einen Entwickler warten, der die ZFS Cryptofunktionen fit für ARM Prozessoren macht. Ob daran ein dringendes Interesse besteht, darf aber bezweifelt werden. Solange muss ich mit der eher mäßigen Performance leben oder auf eine andere Lösung umschwenken. Da wäre zum Beispiel noch LUKS, welches auf ARM besser performt _(… und welches ich eigentlich vermeiden wollte, weil es Dinge wieder komplexer macht … *grummel*).

Fazit und Ausblick

Ich habe gemischte Gefühle. Auf der Positiv-Seite stehen die nun aktivierte Verschlüsselung, die Ausfall- und Datensicherheit durch ein RAID-1 und die Möglichkeit, mittels ZFS Snapshots von meinen Dateien anlegen zu können. Alles mittels ZFS realisieren zu können, gefällt mir. Außerdem verfüge ich nun über mehr Speicherplatz und habe die SSDs in einem schicken neuen Gehäuse untergebracht, das in meinem Rack positiv auffällt und die LED-Lightshow bereichert.

Ein negativer Beigeschmack bleibt wegen der miesen ZFS Performance auf verschlüsselten Datasets. Ich kann vorerst damit leben, weil in der Kette zwischen Laptop und Speicher auch noch andere limitierende Faktoren hängen; zum Beispiel das WLAN oder SSH-Verbindungen. Auf längere Sicht will ich die Performance aber verbessern. Sei es durch ein HW-Upgrade (es gibt da vielversprechende Rock64 Alternaiven und Nachfolger), oder durch eine SW-Änderung. Vielleicht habe ich ja Glück und HW AES-SUpport für ARM wird von ZFS schon bald unterstützt. Das dazugehörige GitHub Ticket ist jedenfalls auf der Watchlist.


Jellyfin Mediaserver auf meinem Rock64 Mini NAS

$
0
0

Durch einige Posts auf Mastodon und ein Video von Jeff Geerling bin ich auf die Multimediaserver-Software Jellyfin aufmerksam geworden. Mir war das Konkurrenzrodukt Plex bereits bekannt, aber mit Jellyfin bekommt man eine reine FOSS Lösung, bei der man sich keine Gedanken um Lizensierung oder Kosten machen muss. Für meine Ansprüche - vor allem Musikstreaming - sollte Jellyfin genügen, daher habe ich es kurzerhand auf meinem Rock64-basierten Mini NAS installiert.

Die Jellyfin Albumansicht für Musik
Die Jellyfin Albumansicht für Musik

Setup

Jellyfin kann als Debian-Paket oder Container installiert werden. Bei den Containerumgebungen kann man aus Docker und Podman wählen. Da Jellyfin für mich neu ist und ich den Betrieb erst einmal erproben will, habe ich mich für ein Podman-basiertes Setup entschieden. Auf Docker verzichte ich gerne, wenn es geht. Das modernere Podman mit seinen Features ist mir grundsätzlich sympathischer - aber das ist eine andere Geschichte und soll hier keine weitere Rolle spielen.

Zunächst wird Podman inkl. zweier Abhängigkeiten von den Ubuntu/Armbian-Paketquellen installiert:

apt install podman uidmap slirp4netns

Danach wird ein neuer User angelegt, unter dem der Jellyfish-Container später ausgeführt werden soll. Mit Podman können Container ohne Daemon und ohne Root-Rechte als einfacher Benutzer ausgeführt werden. Von diesem Sicherheitsvorteil will ich selbstverständlich profitieren:

adduser --disabled-password --home /opt/jellyfin jellyfin
su - jellyfin

(ein “normaler” Login dieses jellyfin Users wird mittels --disabled-password ausgeschlossen. Somit muss man kein Passwort vergeben und sich auch keine Gedanken um einen weiteren Angriffsvektor über einen Benutzeraccount machen. Der pseudo-Login wird über su ausgeführt.)

Im Homeverzeichnis des neuen Benutzers (/opt/jellyfin) wird ein neues Bash-Script “init-jellyfin.sh” angelegt. Dieses dient nur dem ersten Start (und weiteren Starts nach einer Konfigurationsänderung). Nach dem ersten Start wird das Script aber idR. nicht mehr benutzt, sondern auf den systemd-basierten Autostart des Podman-Containers zurückgegriffen. Inhalt der Scriptdatei:

podman run \
--detach \
--label "io.containers.autoupdate=registry" \
--name jellyfin \
--publish 8096:8096/tcp \
--rm \
--user $(id -u):$(id -g) \
--userns keep-id \
--volume jellyfin-cache:/cache:Z \
--volume jellyfin-config:/config:Z \
--mount type=bind,source=/mnt/raid2tb/media,destination=/media,ro=true \
docker.io/linuxserver/jellyfin:latest

(Pfad /mnt/raid2tb/media ggf. anpassen und zu eigenem Medienbibliothek-Pfad ändern!)

Die Datei wird nun ausführbar gemacht und gestartet:

chmod u+x create-jellyfin.sh
./create-jellyfin.sh

Im Hintergrund werden die Containerimages heruntergeladen und die darin befindlichen Applikationn gestartet. Das kann auf einem SBC wie dem Rock64 einige Momente dauern. Wird die Kommandozeile ohne Fehlermeldung wieder freigegeben, scheint alles funktioniert zu haben. Im Webbrowser sollte dann unter http://<server.ip>:8096 das Webinterface zu sehen sein.

systemd Service für Autostart erstellen

Mit den aktuellen Einstellungen wird der Container nach einem Server-Neustart nicht mehr automatisch gestartet und müsste händisch mittels

podman start jellyfin

neu gestartert werden. Das können wir mit einem systemd User-Service ändern.

Systemd erlaubt es, benutzerspezifische Services anzulegen und zu starten. Während der Jellyfin-Container im Hintergrund noch läuft, können wir über ein spezielles Podman-Kommando automatisch einen passenden systemd Service generieren lassen. Das geht so:

mkdir -p ~/.config/systemd/user/
podman generate systemd jellyfin > ~/.config/systemd/user/jellyfin.service

Im nächsten Schritt würde man den neuen Service normalerweise mittels systemctl --user enable jellyfin aktivieren. Da wir zu Beginn einen jellyfin Account ohne Passwort angelegt und uns mittels su als dieser neue Benutzer eingeloggt haben, muss zunächst noch eine Korrektur vorgenommen werden.

Würde man an dieser Stelle das genannte systemd Kommando ausführen, erschiene die Fehlermeldung

Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

Das kommt daher, dass wir uns mit dem jellyfin User nicht regulär via SSH oder ein TTY eingeloggt haben. Deshalb wurde der systemd user Service für den Benutzer nicht aktiviert und er verfügt über keine gültige Konfiguration des DBus-Zugriffs. Mit den folgenden Zeilen und Kommandos kann das Problem beseitigt werden:

In ~/.profile am Ende hinzufügen:

# This is for enabling "systemctl --user" commands without valid login session
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"

Nun zum root User wechseln (z.B. durch exit) - dann als root User ausführen:

loginctl enable-linger $(id -u jellyfin)
systemctl start user@$(id -u jellyfin).service

Zurück zum jellyfin User (su - jellyfin) - und der Service wird aktiviert:

systemctl --user daemon-reload
systemctl --user enable --now jellyfin.service

Das sollte nun ohne Fehlermeldungen funktionieren. Solltet ihr eine zsh Shell verwenden, muss noch mittels source ~/.profile die .profile Datei gesourced werden. Alternativ lassen sich die oben genannten export Zeilen selbstverständlich auch in die ~/.zshrc Datei unten einbetten.

WebUI: Medienbibliotheken einrichten

Alles weitere wird im Webfrontend von Jellyfin eingerichtet. Das Initialsetup wird euch zu euren Wunschbibliotheken befragen. Hier gilt es zu beachten, dass der Medienpfad als Pfad innerhalb des Containers angegeben wird - und das ist idR. immer /media. Egal, wie der Pfad zu euren Medien außerhalb des Containers lauten mag. Die beiden Pfade wurden im init-jellyfin.sh Script aufeinander gemapped.

Extra: Webinterface mit Nginx Proxy

Wer seine Jellyfish-Instanz auf Port 80 und / oder über eine sichere HTTPS Verbindung bereitstellen will, kann sich von der Beispielkonfiguration unter https://jellyfin.org/docs/general/networking/nginx.html inspirieren lassen.

Die Einrichtung wird vor allem dann empfohlen, wenn ihr außerhalb des heimischen Netzwerks mit Jellyfin kommunizieren wollt. So umgeht ihr auch elegant Portsperren für nicht-HTTP Ports in Hotel WLANs und sorgt dafür, dass niemand euren Traffic belauscht.

Ein Wort zur Performance

Wunder darf man von einem kleinen Single Board Computer wie dem Rock64 nicht erwarten. Sobald der Medienscan abgeschlossen ist, reagiert die Weboberfläche zwar nicht raasend schnell, aber angemessen zügig. Was die Live-Transcodierung von Videos angeht, wird man aber enttäuscht: Wie sich herausstellt, bringt Jellyfin zwar alles mit, was es zum Hardware-Kodierung braucht, soch das Rock64 Board ist dafür nicht geschaffen und kann verschiedene Mediencodecs bestenfalls in Hardware decodieren - nicht aber codieren.

Wenn höher aufgelöste Filme gestreamt werden sollen und das Zielgerät das Format nicht direkt unterstützt, stößt man schnell an Grenzen. Der Filme-Fan greift also lieber zu einem x86-basierten Computer mit einem halbwegs starken Prozessor, der die fraglichen Codecs wie H264 und H265 sowie VP9 in Hardware codieren kann.

Da ich vorhabe, fast ausschließlich Musik zu streamen, fällt dieser Nachteil für mich nicht besonders ins Gewicht.

Die Jellyfin Startseite
Die Jellyfin Startseite

Restic REST-Server installieren

$
0
0

Restic lässt sich wunderbar mit einer Reihe verschiedener Speicher und Speicherzugriffsprotokolle verwenden: SFTP, S3, Rclone, … Die für mich interessanteste ist dabei jedoch die REST-Schnittstelle, über die mit dem Restic-Server kommuniziert werden kann. Restic-Server wird dabei auf einem NAS oder anderen Server installiet und bildet das Backend. Eine Restic-Instanz auf einem zu sichernden Client kann sich zu Restic-Server verbinden und Daten sicher und vor allem sehr performant in den Speicher schreiben. Neben der Möglichkeit, ein “append only” Repository zu betreiben, heben die Entwickler vor allem die bessere Performance z.B. im Vergleich zu SFTP hervor:

Compared to the SFTP backend, the REST backend has better performance, especially so if you can skip additional crypto overhead by using plain HTTP transport (restic already properly encrypts all data it sends, so using HTTPS is mostly about authentication). But, even if you use HTTPS transport, the REST protocol should be faster and more scalable, due to some inefficiencies of the SFTP protocol … - https://github.com/restic/rest-server/blob/master/README.md

Die Installation eines solchen Restic Rest-Servers ist schnell erledigt:

Systembenutzer einrichten und Restic REST-Server herunterladen

Als root einloggen, neuen Systemuser erstellen:

adduser --system restic-server

Unter https://github.com/restic/rest-server/releases den neuesten Release suchen und Datei für System herunterladen, z.B. für ARM-basierte Linux Systeme:

wget https://github.com/restic/rest-server/releases/download/v0.11.0/rest-server_0.11.0_linux_arm64.tar.gz
tar xzf rest-server_0.11.0_linux_arm64.tar.gz
cp rest-server_0.11.0_linux_arm64/rest-server /usr/local/bin/restic-server

Das heruntergeladene Binary ausführbar machen:

chmod +x /usr/local/bin/restic-server

Und den Restic REST-Server testweise starten:

restic-server --version

Das Backupverzeichnis vorbereiten

Meine Backups sollen im Verzeichnis /mnt/raid2tb/backup liegen. Es besteht die Möglichkeit, mehrere Nutzer authentifiziert auf den Server zugreifen zu lassen, deshalb habe ich für meinen persönlichen Account einen Unterordner thomas angelegt. Der Verzeichnisname entspricht dem Benutzernamen bei der Authentifizierung.

mkdir -p /mnt/raid2tb/backup/thomas
chown -R restic-server /mnt/raid2tb/backup

Zugang für einen neuen Restic Server Benutzer erstellen:

apt install apache2-utils
cd /mnt/raid2tb/backup
htpasswd -B -c .htpasswd thomas
chown -R restic-server /mnt/raid2tb/backup/.htpasswd
chmod 600 /mnt/raid2tb/backup/.htpasswd

(thomas durch eigenen Benutzernamen ersetzen!)

Dieses Passwort gilt nicht für die Restic Verschlüsselung des Backups, sondern nur für den Zugang zum Restic-Server bzw. dessen HTTP-Schnittstelle. Die Verschlüsselung des Backups ist davon unabhängig. Achtet darauf, dass der Benutzername im htpasswd Befehl genau dem Verzeichnisnamen entspricht, denn nur so kann der Restic-Server Benutzernanmeldung und Backupverzeichnis einander zuordnen.

Nginx Proxy

Damit der Traffic zum Backupserver verschlüsselt wird, soll eine TLS-Transportverschlüsselung auf der Verbindung zwischen Client und Restic-Server verwendet werden. Der Restic-Server kann zwar über das --tls Flag auch schon selbst mit SSL-Sicherheitszertifikaten umgehen - da ich aber bereits einen Nginx-Server auf meinem NAS betreibe, um meinen Jellyfin Service auszuliefern, soll in meinem Fall Nginx das Zertifikatshandling übernehmen.

Eine passende Nginx-Konfiguration sieht so aus:

server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name nas.meinserver.tld;
root /var/www/default;
ssl_certificate /etc/tls/nas.meinserver.tld/fullchain.pem;
ssl_certificate_key /etc/tls/nas.meinserver.tld/privkey.pem;
access_log off;
error_log off;
client_max_body_size 1G;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
location / {
proxy_pass http://[::1]:8000;
}
if ($ssl_protocol = "") {
return 301 https://$server_name$request_uri;
}
}

Die Konfiguration wird nach /etc/nginx/sites-available/restic-server.conf geschrieben und aktiviert:

ln -s /etc/nginx/sites-available/restic-server.conf /etc/nginx/sites-enabled/restic-server.conf
systemctl reload nginx

Systemd Service anlegen

Einen systemd Service anlegen unter /etc/systemd/system/restic-server.service

[Unit]
Description=Restic Server
After=syslog.target
After=network.target
[Service]
Type=simple
User=restic-server
ExecStart=/usr/local/bin/restic-server --path /mnt/raid2tb/backup --private-repos
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target#

Service aktivieren und starten:

systemctl daemon-reload
systemctl enable restic-server
systemctl start restic-server

Mittels systemctl status restic-server kann überprüft werden, ob der Server läuft:

[...]
Active: active (running)
[...]
systemd[1]: Started Restic Server.

Verbindung mit dem Client

Auf dem Client wird der Server über die Umgebungsvariable RESTIC_REPOSITORY eingestellt. In meinem Beispiel mit HTTP und Nginx als Web-Proxy sieht die URL wie folgt aus:

RESTIC_REPOSITORY="rest:https://thomas:thomaspasswort@nas.meinserver.tld/thomas"

Dabei werden Benutzername und Passwort durch einen Doppelpunkt getrennt. Der Teil nach dem @-Zeichen richtet sich nach Art des Setups. In meinem Fall mit HTTPS Support und einem vorgeschalteten Nginx nenne ich einfach die Subdomain meines NAS. Ohne den Nginx muss ggf. der Port noch genannt werden, z.B. nas.meinserver.tld:8000.

DLNA und andere SSDP Dienste über lokale Subnetze hinweg nutzen (OpenWRT)

$
0
0

Zur besseren Trennung zwischen persönlichen Geräten (Laptop, PC), IoT Geräten und Servern setze ich Zuhause einen OpenWRT Router ein, der für die Gerätegruppen einzelne Subnetze aufspannt. So können Laptops beispielsweise auf alle anderen Dienste und Subnetze zugreifen, während Server sich nicht zum Laptop verbinden können uns in ihrem Netzwerk eingesperrt bleiben. Einzig der Internetzugang ist für sie freigegeben. Das Setup lässt mich Nachts ruhiger schlafen, bringt aber auch Nachteile mit sich: Einige der Dienste, die man in seinem Heimnetzwerk betreibt, sind für diese Auftrennung nicht konzipiert und funktionieren nicht auf Anhieb. So auch DLNA, über welches Medienserver ihre Inhalte beispielsweise an ein TV-Gerät freigeben können. Hinter DLNA verbirgt sich eigentlich eine ganze Gruppe von Protokollen und Standards. Ein wichtiges Protokoll im DLNA-Standard ist SSDP (Simple Service Discovery Protocol).

Das Problem: SSDP - Simple Service Discovery Protocol

SSDP ist ein Broadcast-basiertes Protokoll und wird im DLNA-Kontext dazu genutzt, einen DLNA-Client oder DLNA-Server im Netzwerk bekannt zu machen. Schließlich soll der Fernseher ja Bescheid wissen, a) dass ein DLNA-Server existriert und b) wie er ihn erreichen / ansprechen kann. Beide Informationen werden in Form von Broadcast-Paketen im Broadcast-IP-Bereich 239.255.255.250 (IPv4) bzw. ff02::c (IPv6) verkündet, sodass jedes Gerät im selben Netzsegment diese Informationen mitlesen und ggf. nutzen kann. Diese Broadcast-Nachrichten sind idR. auf das aktuelle Subnetz limitiert. Sie werden also nicht geroutet und somit nicht an andere Subnetze weitergegeben.

Hier liegt das Problem: Befindet sich der Fernseher also im IoT-Netz 192.168.4.0/24 und der DLNA-Server im Server-Subnetz 192.168.3.0/24, werden diese normalerweise die gegenseitigen SSDP-Broadcastpakete nicht empfangen und sich somit nicht gegenseitig erkennen können.

Die Lösung: Broadcast-Routing mit smcroute

Zum Glück gibt es eine Lösung für das Problem: Den “Static Multicast Routing Daemonsmcroute. Der Daemon routet dabei nicht selbst Pakete, sondern sorgt durch eine korrekte Konfiguration des Linux-Kernels dafür, dass zwischen definierten Subnetzen und Geräten Broadcastpakete ausgetauscht werden.

Broadcast vs. Multicast: Von Broadcast spricht man in einem Netzwerk, wenn alle Mitglieder dieses Netzwerks die entsprechenden Pakete mitlesen können. Macht man diese Pakete nur für bestimmte Teilnehmer oder für weitere Subnetze verfügbar, spricht man von Multicast. Da in smcroute die Quellen und Ziele konkret definiert werden (müssen) und Pakete über Netzgrenzen hinweg transportiert werden, wird der Begriff “Multicast Routing Daemon” verwendet.

Nach der Installation des smcroute Pakets kann der Daemon in der Konfigurationsdatei unter /etc/smcroute.conf konfiguriert werden. Hier verbirgt sich u.U. schon eine vorausgefüllte Beispieldatei, welche ich durch folgende ersetzt habe:

# Enable bridge interfaces for smcroute use
phyint br-SRV enable
phyint br-lan enable
phyint br-IOT enable
# Allowed routing for IPv4
mgroup from br-SRV source 192.168.3.2 group 239.255.255.250
mroute from br-SRV source 192.168.3.2 group 239.255.255.250 to br-IOT br-lan
mgroup from br-IOT group 239.255.255.250
mroute from br-IOT group 239.255.255.250 to br-lan br-SRV
mgroup from br-lan group 239.255.255.250
mroute from br-lan group 239.255.255.250 to br-SRV br-IOT
# Same for IPv6
mgroup from br-SRV source fd00:0:0:3::2 group ff02::c
mroute from br-SRV source fd00:0:0:3::2 group ff02::c to br-IOT br-lan
mgroup from br-IOT group ff02::c
mroute from br-IOT group ff02::c to br-lan br-SRV
mgroup from br-lan group ff02::c
mroute from br-lan group ff02::c to br-SRV br-IOT

Die Konfiguration bewirkt, dass Multicast-Pakete zwischen den Netzwerkbrücken (und Interfaces) br-lan, br-IOT und br-SRV ausgetauscht werden, wenn sie in den von SSDP genutzen Broadcast-Adressbereich fallen.

(durch source 192.168.3.2 bzw. source fd00:0:0:3::2 gilt für Pakete aus dem SRV-Netz die zusätzliche Einschränkung, dass nur Pakete von meinem Jellyfin-Server aus diesem Netz geroutet werden dürfen)

Der smcroute Daemon muss danach neu gestartet werden:

service smcroute restart

Dabei kann es hilfreich sein, nach dem Start auch das Systemlog /var/log/messages im Blick zu behalten, um Fehler bei der Konfiguration zu erkennen.

Man könnte meinen, jetzt sollten sich DLNA-Server und -Clients bereits finden können. Wäre da nur nicht …

Der Showstopper: Die TTL (Time-to-Live)

Da Broadcast-Pakete (eigentlich!) das eigene Subnetz nicht verlassen sollen, haben sie eine Time-To-Live von 1. Das bewirkt, dass solche Pakete vom Router nicht weiter für das Routing in andere Netze in Betracht gezogen werden. Eigentlich eine vernünftige Sache - doch in unserem Fall soll ja gerade dies erreicht werden. Die TTL muss also angehoben werden, damit SSDP Broadcast-Pakete “es über den Router hinaus schaffen” können. Die Anpassung kann an verschiedenen Orten erfolgen

  1. Zum einen kann das Paket an der Quelle, z.B. DLNA-Server, eine höhere TTL verpasst bekommen ..
  2. … oder in der Prerouting-Queue des OpenWRT-Routers, bevor es aufgrund seiner TTL von 1 verworfen wird.

Weg 2) kann zentral verwaltet werden und an den Clients sind keine Änderungen nötig. Das ist Vorteilhaft, weil ich die TTL der DLNA-Pakete, die mein Fernseher schickt, ohnehin nicht erhöhen kann. Dazu müsste ich mir schon Zugriff auf sein Linux-System verschaffen.

An meinem OpenWRT Router habe ich also folgendes in die zusätzlichen Firewallregeln (/etc/firewall.user) eingefügt:

# SSDP: Increase DLNA Broadcast TTL
iptables -t mangle -A PREROUTING -i br-lan -d 239.255.255.250 -j TTL --ttl-inc 1
iptables -t mangle -A PREROUTING -i br-IOT -d 239.255.255.250 -j TTL --ttl-inc 1
iptables -t mangle -A PREROUTING -i br-SRV -d 239.255.255.250 -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-lan -d ff02::c -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-IOT -d ff02::c -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-SRV -d ff02::c -j TTL --ttl-inc 1

Mittels --ttl-inc wird die TTL um eins erhöht, sodass die betreffenden Pakete 1x (von einem Subnetz ins nächste) geroutet werden können.

Nicht zu vergessen: Firewallfreigaben für DLNA

Eine Sache fehlt noch: Sollte sich eine restriktive Firewall zwischen den Subnetzen befinden, müssen für SSDP und alle anderen DLNA-Übertragungen noch Firewallfreigaben erteilt werden. In meinem Netzwerk muss also konkret eine eingeschränkte Kommunikation zwischen den Netzen lan, iot und srv stattfinden können.

Für SSDP soll die Firewall für Zieladresse 239.255.255.250 bzw. ff02::c und Port 1900 (UDP) durchlässig werden:

config rule
option name 'allow_iot_srv_ssdp'
option dest_port '1900'
option src 'iot'
option dest 'srv'
option target 'ACCEPT'
list proto 'udp'
list dest_ip '239.255.255.250'
list dest_ip 'FF02::C'
config rule
option name 'allow_iot_lan_ssdp'
option dest_port '1900'
option dest 'lan'
option target 'ACCEPT'
list proto 'udp'
option src 'iot'
list dest_ip '239.255.255.250'
list dest_ip 'FF02::C'
config rule
option name 'allow_srv_iot_ssdp'
option dest_port '1900'
option src 'srv'
option dest 'iot'
option target 'ACCEPT'
list proto 'udp'
list dest_ip '239.255.255.250'
list dest_ip 'FF02::C'
config rule
option name 'allow_srv_lan_ssdp'
option dest_port '1900'
option src 'srv'
option dest 'lan'
option target 'ACCEPT'
list proto 'udp'
list dest_ip '239.255.255.250'
list dest_ip 'FF02::C'

Mögliche Verbindungen sehen also so aus:

IoT => SRV
IoT => LAN
SRV => IoT
SRV => LAN

Vom LAN-Netz ausgehende Verbindungen sind hier nicht explizit aufgelistet, denn sie sind in meinem Netzwerk generell erlaubt und nicht eingeschränkt.

Die SSDP-Freigaben sind hiermit erledigt. Die Geräte könnten sich schon untereinander finden, doch noch keine Inhalte austauschen. Hier für muss eine Freigabe für den eigentlichen Stream-Port einegrichtet werden. Im Fall von meinem Jellyfin-DLNA-Server werden Inhalte via Port 8096 (TCP) gestreamed. Meine Freigaben dafür sehen so aus:

config rule
option name 'allow_iot_srv_nas_dlna_http'
option src 'iot'
option dest 'srv'
list dest_ip '192.168.3.2'
list dest_ip 'fd00:0:0:3::2'
option target 'ACCEPT'
list proto 'tcp'
option dest_port '8096'

Mehr Freigaben sind nicht nötig, denn Geräte aus dem LAN-Bereich dürfen ohnehin auf alles Zugreifen und Geräte aus dem SRV-Netz sollen niemals Streams aus dem IoT Netz oder dem LAN-Netz empfangen. Beim SSDP-Protokoll wurden bewusst mehrere Richtungen und Kombinationen berücksichtigt und freigeben, damit nicht nur SSDP NOTIFY (announcements) den richtigen Weg finden, sondern auch Fragen nach SSDP-Diensten (M-SEARCH). Generell ist hier aber nur das streamen vom SRV-Bereich entweder in LAN oder IOT vorgesehen.

Vergesst nicht, eure Änderungen an der Firewall zu aktivieren!

uci commit firewall
service firewall restart

Nach idR. weniger als 2 Minuten sollten sich die Geräte gemäß der FW-Konfiguration finden können. In meinem Beispiel der Fernseher im IoT Netz und der DLNA-Server im SRV Netz.

Die Sache mit IGMP

Je nach Konfiguration der Bridges auf dem OpenWRT-Router (bei mir br-lan, br-IOT und br-SRV) kann es vorkommen, dass sich DLNA-Geräte nicht oder nicht sofort finden. Das hängt mit dem Einsatz des IGMP-Protokolls zusammen…

Routet man Multicast-Pakete über Netzwerkgrenzen, kann es vorkommen, dass Netzsegmente von Multicast-Paketen geflutet werden, die keinen einzigen Client haben, welcher an diesen Paketen interessiert ist. Besonders dramatisch kann das Problem bei Multicast-Streams auftreten, wie z.B. IPTV, da hier entsprechend größere Mengen für den Videostream verteilt werden. Um unnötige Belastungen in größeren Netzwerken zu vermeiden, wurde IGMP (Internet Group Messaging Protocol) erfunden: Mittels IGMP kann ein Gerät an den Router melden, ob und an welchen Multicast-Paketen es interessiert ist. Erkennt der Router an einem Interface ein Gerät, das Interessen an Multicast-Paketen bekundet, leitet dieser die entsprechenden Pakete an dieses Subnetz weiter. Andernfalls nicht.

Normalerweise sollte IGMP auf dem OpenWRT Router für alle Bridges aktiviert sein. Das bedeutet: Wollen Geräte SSDP Pakete für DLNA empfangen, müssen sie diese beim Router zunächst “abonnieren”. Das funktioniert je nach Gerät mal mehr, mal weniger gut. Wer mit seinem Setup auf Probleme stößt oder gar keine SSDP Pakete in seinem Netzwerk erkennen kann, sollte einmal versuchen, IGMP auf allen involvierten Bridges am Router zu deaktivieren. Der Traffic erreicht dann immer alle Bridges (und somit Subnetze).

Wer in seinem Netzwerk ohnehin keine Multicast-Videostreams verteilt, kann IGMP getrost deaktiviert lassen. SSDP verursacht nur minimalen Traffic, der nicht ins Gewicht fällt. Wer hingegen IPTV im Netzwerk laufen hat und mehrere Subnetze betreibt, will IGMP sehr wahrscheinlich aktiviert lassen.


Weiterführende Links:

Apple Airplay über Subnetze hinweg ermöglichen (OpenWRT)

$
0
0

Ähnlich wie DLNA ist auch Apple Airplay nicht unbedingt für den Betrieb außerhalb eines einzigen Subnets ausgelegt. Mit ein paar Kniffen funktioniert es trotzdem …

mDNS: Basis vieler Heimnetzwerk-Dienste

Zunächst ist wichtig zu verstehen, dass Apple Airplay auf einer Sammlung von Standards und Protokollen aufbaut. Es genügt nicht den einen “Airplay-Port” in der Firewall durchzureichen und zu hoffen, dass alles funktioniert. Damit sich Airplay-Geräte in einem Netzwerk überhaupt gegenseitig finden können, wird das Multicast-DNS (auch mDNS) zur sog. Service Discovery eingesetzt. Dabei handelt es sich um nichts anderes als einen lokalen, dezentralen Verzeichnisdienst. Anders als beim herkömmlichen DNS gibt es also keine zentrale Serverinstanz, die sämtliche verfügbare Services und die zugehörigen Adressen vorhält. Durch regelmäßiges Bekanntgeben der selbst angebotenen Dienste via Broadcast / Multicast werden alle anderen Netzteilnehmer darüber in Kenntnis gesetzt, dass ein Service eines gewissen Typs unter einer bestimmten Adresse verfügbar ist.

Über das avahi-browse Tool können zum Beispiel folgende Einträge im eigenen Netzsegment abgerufen werden:

[thomas@thomas-nb]~% avahi-browse -a
+ wlp58s0 IPv6 [LG] webOS TV UK6470PLC _airplay._tcp local
+ wlp58s0 IPv4 [LG] webOS TV UK6470PLC _airplay._tcp local
+ wlp58s0 IPv6 LG webOS TV 8A66 _hap._tcp local
+ wlp58s0 IPv4 LG webOS TV 8A66 _hap._tcp local
+ wlp58s0 IPv6 Philips Hue - <id> _hue._tcp local
+ wlp58s0 IPv4 Philips Hue - <id> _hue._tcp local
+ wlp58s0 IPv4 turris _ssh._tcp local
+ wlp58s0 IPv4 Chromecast-Ultra-<id> _googlecast._tcp local
+ wlp58s0 IPv6 turris-2 _http._tcp local
+ wlp58s0 IPv6 EPSON ET-2650 Series-<id> _http._tcp local
+ wlp58s0 IPv6 <id> _teamviewer._tcp local
+ wlp58s0 IPv6 EPSON ET-2650 Series-<id> _printer._tcp local
+ wlp58s0 IPv6 EPSON ET-2650 Series-<id> _ipps._tcp local
+ wlp58s0 IPv4 RX-V777 <id> _http._tcp local
+ wlp58s0 IPv4 eDMP32MB_<id> _spotify-connect._tcp local
+ wlp58s0 IPv4 <id>@RX-V777 <id> _raop._tcp local
+ wlp58s0 IPv4 <id> _fosquitto._tcp local
[...]

Wie deutlich zu sehen ist, senden viele Geräte im Heimnetzwerk solche mDNS-Pakete und geben darüber Informationen über ihre Services preis. So informiert mein Drucker zum Beispiel darüber, dass er via IPP angesprochen werden kann. Ein Desktoprechner bewirbt seinen verfügbaren Teamviewer-Server und meine Philips Hue Bridge lässt andere Geräte (z.B. mein Smartphone) wissen, dass sie über ein Philips-spezifisches “hue” Protokoll angesprochen werden kann.

Geräte, die sich nicht im selben Netzsegment befinden, werden an dieser Stelle nicht sichtbar, weil mDNS (wie auch SSDP) standardmäßig nur im selben Subnetz funktioniert. Mein Fernseher und AV-Receiver, die beide Airplay beherrschen, bleiben also verborgen.

In meinem Artikel “DLNA und andere SSDP Dienste über lokale Subnetze hinweg nutzen (OpenWRT)” habe ich bereits einen Weg aufgezeigt, wie sich Multicast-Protokolle auch über verschiedene Subnetze hinweg nutzen lassen. Prinzipiell wäre es auch möglich, dasselbe für mDNS umzusetzen. Statt des SSDP-Multicast-Bereichs müsste dann der mDNS-Adressbereich im Router konfiguriert werden.

Für Airplay bzw. mDNS habe ich aber etwas anderes ausprobiert: Einen Avahi-Reflector!

Avahi (-Reflektor)

Auf meinem Router - einem Turris Omnia 2020 mit OpenWRT - befindet sich bereits der Avahi-Daemon. Avahi ist ein Daemon, der via mDNS im lokalen Netz verkündet, welche Services ein Gerät anbietet. Auf meinem Omnia kündigt Avahi beispielsweise SSH auf Port 22 und die Weboberfläche auf Port 80 an.

Praktischerweise kann Avahi mDNS-Einträge von einem Subnetz / Interface auslesen und dann in ein anderes Subnetz replizieren (oder auch “reflektieren”). Korrekt konfiguriert werden angekündigte Services aus Netz A also auch im Netz B und C angekündigt. Die dafür notwendige Konfiguration sieht in meinem Fall so aus:

(/etc/avahi/avahi-daemon.conf)

[server]
host-name=turris
domain-name=local
use-ipv4=yes
use-ipv6=yes
check-response-ttl=no
use-iff-running=no
allow-interfaces=br-lan,br-SRV,br-IOT
[publish]
publish-addresses=yes
publish-hinfo=yes
publish-workstation=no
publish-domain=yes
publish-aaaa-on-ipv4=no
publish-a-on-ipv6=no
[reflector]
enable-reflector=yes
reflect-ipv=no
[rlimits]
rlimit-core=0
rlimit-data=4194304
rlimit-fsize=0
rlimit-nofile=30
rlimit-stack=4194304
rlimit-nproc=3

Bedeutend sind vor allem die Zeilen unter [reflector] und allow-interfaces. Hinter den Interfaces befinden sich Bridged für meine Subnetze für LAN, Server und IoT Geräte. enable-reflector=yes sorgt schließlich für das Spiegeln der Einträge zwischen den Netzen.

Nach einem Serviceneustart sollte das Airplay-Gerät auf den anderen Clients zumindest schon einmal über das avahi-browse Tool sichtbar sein.

Firewall-Konfiguration

Die Firewall zwischen meinen Subnetzen ist relativ restriktiv, sodass meine Arbeit hier noch nicht erledigt ist. Airplay benötigt noch einige Freischaltungen, damit es korrekt funktioniert.

In meinem Szenario sollen nur Geräte aus dem LAN (br-lan) Inhalte an meinen AV-Receiver (br-IOT) mit Airplay senden können. Folgende Konfiguration habe ich hierzu in /etc/config/firewall hinterlegt:

config rule
option dest_port '80'
list proto 'tcp'
option name 'allow_iot_yamaha_lan_ipad_airplay_http'
option dest 'lan'
option target 'ACCEPT'
option src 'iot'
config rule
option dest_port '443'
list proto 'tcp'
option name 'allow_iot_yamaha_lan_ipad_airplay_https'
option dest 'lan'
option target 'ACCEPT'
option src 'iot'
config rule
option dest_port '554'
option src 'iot'
option name 'allow_iot_yamaha_lan_ipad_airplay_rtsp'
option dest 'lan'
option target 'ACCEPT'
config rule
option dest_port '3689'
option dest 'lan'
option target 'ACCEPT'
option src 'iot'
option name 'allow_iot_yamaha_lan_ipad_airplay_daap'
list proto 'tcp'
config rule
option dest_port '49152-65535'
option src 'iot'
option name 'allow_iot_lan_airplay_dynamic'
option dest 'lan'
option target 'ACCEPT'

Die Firewallregeln lassen Geräte aus dem IoT-Netzsegment auf definierte Resosurcen aus dem LAN-Segment zugreifen, denn beim Aufbau einer Airplay-Verbindung bietet die Streamingquelle (z.B. iPad) auf einem Port die Inhalte an - das Abspielgerät greift dann darauf zu. Die Freigaben müssen also gewissermaßen “umgekehrt” betrachtet werden. Nicht der Port am Abspielgerät wird freigeschaltet, sondern der Port an der Quelle.

Wenn nur bestimmte Geräte miteinander kombinierbar sein sollen, ließen sich die Regeln durch die Angabe von “Source” und “Destination” IP-Adressen noch verschärfen. Ich habe der Einfachheit darauf verzichtet.

Eine Liste der von Airplay verwendeten Ports lässt sich übrigens hier einsehen. Insbesonders die letzte Regel hat mir Kopfschmerzen bereitet, weil sie nicht explizit im Zusammenhang mit “Airplay” aufgelistet ist.

T-Shirts für meine Mastodon-Instanz metalhead.club

$
0
0

Zwei Dinge sind in der Regel Mangelware, wenn man eine Mastodon-Instanz betreibt: Finanzielle Mittel und öffentliche Aufmerksamkeit. Doch beide sind wichtig, damit die Instanz weiter betrieben werden kann und - falls gewünscht - ein Wachstum oder Reichweite erzielen kann.

Mit metalhead.club verfoge ich das Ziel, eine professionell gehostete Plattform für alle anzubieten, die sich im Metal-Musikgenre heimisch fühlen. Damit eine solche themengebundene Instanz leben kann und ihre Nutzer den größten Nutzen davon haben, muss sie eine gewisse Bekanntheit erlangen. Dennoch kommen die üblichen Werbemittel nur bedingt in Frage - sei es, weil sie nicht meine Vorstellung von Datenschutz implementieren oder aber derzeit nicht ohne weiteres sinnvoll finanzierbar sind. Mit den kostbaren Spendengeldern muss sparsam umgegangen werden.

Zudem ist die Zielgruppe zwar groß, aber relativ speziell: Während man ein LED-Leuchtmittel vermutlich an jeden verkaufen kann, gelingt das mit einem sozialen Netzwerk für Fans des Metals nicht ganz so einfach. Die richtigen Menschen müssen in ihrer “Sprache” angesprochen werden.

… und was bietet sich da besser an, als etwas zu verkaufen, das in der Szene einen hohen Stellenwert hat und gut sichtbar ist? T-Shirts! Als Bandshirts sind sie privat, auf Festivals oder Konzerten überall zu sehen und zeigen, welche Bands der Träger gerne hört. Ähnlich soll es auch mit den Instanz-T-Shirts funktionieren: Als jemand, der ein T-Shirt seiner Instanz trägt, kann man als eine Art mobiler Werbeträger funktionieren und gleichzeitig das fördern, was man selbst unterstützenswert findet - die metalhead.club Mastodon-Instanz!

Nachdem sie von einigen metalhead.club Mitgliedern bereits seit langem enthusiastisch gefordert wurden, habe ich im Sommer 2023 die erste T-Shirt Bestellaktion ausgerufen und 100 metalhead.club T-Shirts bestellt und an die Mitglieder verkauft. Nun - im Frühjahr 2024 - war es Zeit für die zweite Aktion mit einem anderen T-Shirt-Modell.

Wie ich an das Projekt “metalhead.club T-Shirts” herangegangen bin, will ich im folgenden beschreiben und den ein oder anderen Hinweis geben. Vielleicht hilft es so manchem, der auch mit dem Gedanken spielt, Merchandise zu seiner Mastodon-Instanz anzubieten.

Tom wearing metalhead.club t-shirt

Selbst machen oder zurücklehnen?

Im Internet und in den Städten gibt es zahlreiche Shops, die verschiedenste Merch-Artikel anbieten und dabei häufig sogar den Versand zum Käufer direkt übernehmen. Beispiele dafür sind Spreadshirt, Red Bubble oder Printful. Teilweise lassen sich die Lieferanten sogar automatisch über Onlineshop Plugins beauftragen, sodass man als Verkäufer der Artikel eigentlich nicht mehr viel unternehmen muss: Der Kunde bestellt in einem Onlineshop, der Lieferant führt die Produktion aus, wickelt die Zahlung ab und verschickt die Ware an den Kunden. Als Betreiber des Shops kassiert am Ende ein paar Euro vom Verkauf.

Bei meinem T-Shirts will ich jedoch einen anderen Weg gehen. Solange es die Stückzahlen (ca 100 Stück Pro Aktion) zulassen, will ich selbst in den Bestellprozess involviert sein: Zahlungsentgegennahme und Versand führe ich selbst durch - auch wenn es zum Teil mühselige Arbeit ist. So kann ich vermeiden, dass unnötig Geld in Form von Provisionen und Gebühren an Dritte fließt und bin außerdem in der Lage, einen datenschutzfreundlichen Einkauf zu ermöglichen: Die Kunden und ihre Adressdaten kenne nur ich. Nach der Verwendung können sie gelöscht werden. Außerdem kann ich selbst die Qualität kontrollieren und die Verpackung auswählen, sowie nach belieben Extras dazugeben, beispielsweise metalhead.club Visitenkarten.

Obwohl ich zunächst mit dem Gedanken gespielt habe, die T-Shirts selbst zu bedrucken, habe ich die Idee schnell verworfen. Denn dafür sind neben dem passenden Equipment auch viel Zeit und Platz nötig - alles Dinge, die ich nicht habe. Meine T-Shirts lasse ich von einem Zulieferer einkaufen und bedrucken. Das Resultat vertreibe ich dann selbst weiter.

Die Zutaten: T-Shirts und (Sieb-)Druck

Wenn es um Merchandise oder Teambekleidung mit Druck geht, gibt es einen großen Namen in der Branche: Stanley/Stella. Das Unternehmen stellt seit 2012 hochqualitative Kleidung her, die dann von Dritten weiter gestaltet werden kann - eine Art “Blankokleidung”. Ein besonderes Merkmal ist die Biobaumwollzertifizierung.

Die T-Shirts der Marke wurden mir im Gespräch mit einigen metalhead.club Mitgliedern wärmstens empfohlen und mir die hohe Qualität bestätigt. Mittlerweile kann ich auch eine absolute Empfehlung aussprechen - die Kleidung hat sich bewährt und der Preis stimmt. Der Name war also gesetzt.

Die Modellauswahl ist Geschmackssache. Die erste Bestellaktion drehte sich um die Modelle “Presenter” und “Evoker” - bei der zweiten Aktion habe ich mich auf das “Crafter” Modell beschränkt, um zusätzlichen Aufwand zu vermeiden. Auch die Farbe habe ich auf Schwarz eingeschränkt, um nur noch die Kleidergrößen als einzige Variable zu haben. Das macht die Abwicklung einfacher und somit weniger fehleranfällig. Es empfiehlt sich, von allen Größen ein paar Sütck mehr zu bestellen. Sei es, um Qualitätsmängel auszugleichen, verlorene Sendungen zu ersetzen, oder einfach nur, um ggf. über eine größere Stückzahl einen besseren Stückpreis zu erzielen. Überschüssige T-Shirts bin ich bei der Bestellaktion 2023 relativ schnell losgeworden, da nicht alle Interessenten von der Aktion erfahren haben oder sie aus anderen Gründen verpasst haben.

Bezüglich des Druckverfahrens war für mich ziemlich schnell klar, dass ich einen Siebdruck wollte. Im Vergleich ist es zwar nicht unbedingt das günstigste oder schnellste Verfahren, aber es verspricht eine sehr hohe Haltbarkeit, insbes. bei häufigem Waschen, da die Farbe tief in das Gewebe eindringen und sich dort gut halten kann. Was wäre denn ein metalhead.club T-Shirt, das Mosphits nicht standhält und nach ein paar Wäschen nicht mehr lesbar ist?

Meine T-Shirts lasse ich von Promoyard bedrucken - einem regionalen Unternehmen, das schon bei der ersten Bestellung überzeugen konnte. Gedruckt wird mit ÖkoTex-zertifizierten, hautfreundlichen Farben.

T-Shirts in a box

Der Bestellablauf

Nachdem T-Shirt Hersteller und Druckerei feststanden, habe ich angefangen, die Aktion zu bewerben. Zum einen habe ich auf meinem 650thz.de Services Blog einen Post verfasst, der die Information zu den T-Shirts und Bestellinformationen zusammenfasst - zum anderen habe ich auch auf Mastodon Beiträge zu der Aktion verfasst und eine Admin-Ankündigung für meine Instanz metalhead.club eingestellt. So wird jedes Mitglied über eine gesondert eingeblendete Nachricht in Web und App über die T-Shirt Aktion benachrichtigt.

Nach der ersten Bestellaktion im Sommer 2023 war klar, dass ich häufiger auf die Aktion hinweisen musste, um möglichst alle Interessenten zu erreichen. Der Bestellzeitraum war zwar 6 Wochen lang, aber durch die Urlaubszeit und zeitlich nur relativ konzentrierte Hinweise hat nicht jeder von der Bestellaktion erfahren.

Beim zweiten Mal im Frühjahr 2024 habe ich zu Beginn täglich morgens einen Post abgesetzt, um Mitglieder (zumindest in Deutschland) morgens vor der Arbeit oder auf dem Weg zu erreichen, wenn sie Zeit für Mastodon haben. Offenbar konnte ich so noch einige Interessenten erreichen. Meine Hinweise habe ich innerhalb der sechswöchigen Bestellfrist zum Ende hin zwar reduziert, um niemanden zu langweilen, aber kurz vor Ablauf der Frist noch einmal vermehrt gepostet, um Kurzentschlossene abzufangen.

Technisch habe ich die Bestellungen sehr einfach abgewickelt: Wer an einem T-Shirt interessiert war, schrieb mir eine E-Mail mit Anzahl der T-Shirts, Größe und Anschrift. Im Anschluss wurde dem Besteller eine E-Mail mit einer Empfangsbestätigung und der Bankverbindung sowie einem Stripe.com Link zur Bezahlung zugeschickt. Die Zahlungen habe ich täglich manuell abgeglichen und vermerkt. Nach eingehender Zahlung wurde nochmals eine Zahlungsbestätigung via E-Mail versendet. Alle Bestellungen und den Bezahlstatus habe ich in einer unspektakulären LibreOffice Calc Tabelle festgehalten.

Spätestens nach der ersten T-Shirt Aktion schwebte mir zwar eine Zeit lang eine einfache Django-basierte Bestellverwaltungsoberfläche vor, doch ich fand nicht ausreichend Zeit, diese zu entwickeln. Für Bestellungen in der Größenordnung um 100 Stück war aber auch eine einfache Tabellenkalkulationsanwendung noch in Ordnung - wenn auch weniger praktisch.

Eine Bezahlung via Vorkasse habe ich übrigens gewählt, um das Risiko auf meiner Seite gering zu halten, etwa durch verspätete oder ausbleibende Zahlungen. Glücklicherweise haben sich die Mitglieder auf metalhead.club (und auch Besteller anderer Instanzen) aber als aüßerst pflichtbewusst und zuverlässig erwiesen. Probleme mit der Bezahlung gab es nicht.

Pile of packages

Der Versand

Nachdem die Bestellungen gesammelt waren und der Bestellzeitraum endete, gab ich meinerseits die Herstellung der T-Shirts bei meinem Lieferanten in Auftrag. Innerhalb von ca 2 Wochen wurden die T-Shirts geliefert und ich machte mich daran, diese nach und nach an die Mitglieder zu verschicken.

Versandmaterial

Beim Versandmaterial habe ich mir viele Gedanken gemacht, denn es sollte die Ware nicht nur trocken und sicher ans Ziel bringen, sondern auch möglichst umweltfreundlich und ggf. wiederverwendbar sein. Mit Biobiene.com habe ich genau den richtigen Lieferanten dafür gefunden! Der Shop bietet verschiedene Versandmaterialien auf Graspapier-Basis und andere umweltfreundliche Alternativen an.

Zum Einsatz kamen bei der ersten metalhead.club T-Shirt Aktion folgende Materialien:

Die Kleiderschutzhüllen sind mit ihren DIN-A3 Maßen zwar etwas zu groß, lassen sich aber dafür umso komfortabler besser mit den T-Shirts füllen. Die Alternative - “Polybeutel” aus Papier sind zwar pro Stück günstiger, können allerdings erst ab 250 Stück gekauft werden. Ich hab mich also für erstere entschieden.

Die Versandtaschen aus Graspapier können nach dem Aufreißen ein zweites Mal benutzt werden: Ein zweiter, innenliegender Klebestreifen ermöglicht den mehrmaligen Einsatz.

Die Lieferscheinhüllen sind für Rechnungen gedacht, die im Falle internationaler Sendungen (nicht-EU-Ausland) für den Zoll notwendig sind. Außerdem muss auf den Versandtaschen zusätzlich eine Zolldeklaration aufgeklebt werden - mit Angaben über den Inhalt, Gewicht und Wert.

Die Empfängeradressen habe ich zunächst auf Papier ausgedruckt und aufgeklebt, allerdings stellte sich das - wenig überraschend - als sehr zeitaufwendig heraus. Später habe ich mir bedruckbare Aufkleber in DIN-A4 Größe bestellt, die Adressen darauf ausgedruckt und ausgeschnitten. Die Deutsche Post fasst bei der Online-Eingabe mehrerer Adressen alle Anschriften zu einer PDF-Seite zusammen (max 10 Anschriften pro Seite) und bietet diese zum Ausdruck an. Ausgedruckt können die Anschriften dann ausgeschnitten und direkt aufgeklebt werden.

Für die zweite Aktion wollte ich auch auf umweltfreundliche Adressaufkleber setzen und habe mir Heisap Papieretiketten HEI027 bestellt.

Versandtarife bei Post und DHL

Den Versand habe ich für Empfänger in Deutschland über die Deutsche Post abgewickelt. Über die Warensendung 500 können problemlos auch 3 T-Shirts in einer Versandtasche verschickt werden.

Sendungen ins EU-Ausland und weltweit habe ich über DHL verschickt. Das “Päckchen EU / bzw. International XS 2 kg” eignet sich dafür.

Sendungen innerhalb Deutschlands waren idR. nach 3 Werktagen bei den Empfängern. Bei internationalen Sendungen vergingen aber zum Teil mehrere Wochen. Vor allem bei Sendungen nach Kanada und Mexiko war Geduld gefragt. Aber auch Sendungen nach Spanien und Ungarn schienen zunächst verschollen zu sein, bevor sie viele Tage nach dem erwarteten Empfangsdatum noch in Postfilialen gefunden wurden. Es lohnt sich also, Geduld zu haben (und den Empfänger ggf. anzuweisen, noch einmal nachzufragen und das Päckchen suchen zu lassen).

Many packages in two Deutsche Post transport boxes

Versenden der T-Shirts

Sobald die T-Shirts und das Versandmaterial da sind, kann es losgehen. Beim letzten Mal habe ich den Versand der knapp 100 T-Shirts über ca 3 Wochen gestreckt, um nicht die Konzentration zu verlieren. Denn Fehler beim Versand können relativ schnell relativ teuer werden - vor allem, wenn es um Empfänger im Ausland geht.


Die T-Shirt-Aktion 2023 war ein voller Erfolg und die Bestellphase für die Aktion Frühling 2024 hat letzte Woche geendet. Ich bin gespannt auf die zahlreichen Fotos der metalhead.club Member mit ihren T-Shirts! Es ist schön, die Mitglieder auf den Fotos in einer Art gemeinsamen Uniform zu sehen!

T-Shirts with metalhead.club business cards on top

Minix Z100-0dB als neuer Homeserver für Backups und Medienstreaming

$
0
0

2018 habe ich ein kleines ARM-NAS auf Basis des damals relativ neuen Rock64 Boards von Pine aufgebaut. Das NAS sollte nur leichte Aufgaben wie z.B. das Speichern von Laptop-Backups und Bereitstellen von DNS-Diensten übernehmen. Die Aufgabe hat es seither prima gemeistert. In den letzten Jahren ist allerdings eine neue Aufgabe dazugekommen, die etwas mehr Leistung benötigt: Das Verwalten meiner Medienbibliothek.

Das Rock64 NAS stößt an seine Grenzen

Mittels Jellyfin-Medienserver sollte meine Musik Zuhause und unterwegs immer verfügbar sein. Auch einige von DVD gerippte Konzertfilme sollten via DLNA auf dem Fernseher verfügbar gemacht werden. Mit Musik klappte das Streaming leistungsmäßig, aber beim Thema Video ist das Rock64 Board mit seiner relativ betagten Rockchip CPU schnell an seine Grenzen gekommen. Da der LG Fernseher nur ganz bestimmte Videocodecs versteht, fordert er beim DLNA Server häufig Videotranscoding an. Dieses Transcoding ist relativ leistungshungrig, wenn nicht spezielle CPU-Hardwareeinheiten vorhanden sind. Zudem muss sichergestellt werden, dass das HW-basierte Transcoding durch die Software genutzt werden kann. Entsprechende Libraries und Treiber müssen auf dem System also installiert sein.

Wie im ARM Umfeld (leider) schon fast üblich, ist die Softwaresituation etwas unübersichtlich. Es gibt in der Regel eine schlecht gewartete Linux-Variante vom Boardhersteller und einige Community-Distributionen, die versuchen, es besser zu machen. Jede hat allerdings andere Baustellen und es ist nicht einfach, die eine Distribution zu finden, die auf dem neuesten Stand ist, stabil funktioniert und dabei alle Features unterstützt, die auf dem Board bereitstehen. Ich bin lange Zeit mit Armbian gut gefahren, allerdings konnte nie eine HW-Beschleunigung für die Videotranscodierung zum Laufen bringen.

Das Thema habe ich ehrlicherweise aber auch nicht sehr intensiv verfolgt, denn auch an anderer Stelle schwächelte mein Rock64 System. Alleine schon der Aufbau derJellyfin-Website dauerte einige Sekunden - gefolgt von weiterem Warten, bis Alben-Thumbnails meiner Musikeinkäufe geladen waren. Spaß hat das nicht gemacht und ein Durchsuchen war relativ mühselig.

Alles neu macht der Mai / Juni

Nun - ca 6 Jahre später, ist es Zeit, meinen Rock64 in die Rente zu schicken und ein neues, leistungsstärkeres NAS aufzubauen. Ich habe mich etwas in der Selfhosting Community umgesehen und bin relativ schnell auf die Intel N100 CPU gestoßen. Dabei handelt es sich gewissermaßen um ein x86-basiertes Konkurrenzprodukt, das Intel in das Rennen gegen die üblichen Raspberry Pi ARM Chips und Rockchip-Alternativen schickt. Die CPU kommt mit ca 6 Watt Verlustleistung aus und ist gleichzeitig relativ flott. Das macht sie perfekt für den Einsatz in einem Homeserver NAS, das die meiste Zeit nichts tut und nur sporadisch die Höchstleistung abruft.

Einen weiteren Vorteil bringt die CPU mit sich: Es mag in Zeiten von ARM Single Board Computern etwas uncool erscheinen, aber sie ist x86 basiert und verhält sich somit genauso wie jeder andere handelsübliche PC. Ich kann einfach ein eine normale Linuxdistribution darauf installieren, bin nicht abhängig von schlecht gewarteten und veralteten Linux-Kerneln, die für das jeweilige Board angepasst werden mussten und habe die volle Softwarepalette zur freien Auswahl. Zugegeben - die Situation mit Linux auf ARM ist in den letzten Jahren schon viel besser geworden. Dennoch: Es gibt immer wieder kleine Stopper, die einem die Freude nehmen.

Ein Beispiel: ZFS für Linux beherrscht Verschlüsselung. Allerdings kann diese unter ARM noch nicht auf eine Hardwareunterstützung zurückgreifen. ARM Prozessoren haben zwar eine Einheit zur Crypto-Beschleunigung, aber diese ist unter ARM noch nicht für ZFS implementiert. Auf der x86 Seite sieht es schon ganz anders aus: Hier ist die HW-unterstützte Verschlüsselung für ZFS verfügbar und lange erprobt. Auf meinem Rock64 habe ich dann schließlich auf die SW-basierte Verschlüsselung zurückgegriffen - unter Akzeptanz gewaltiger Performance-Einbußen.

Um ein weiteres Beispiel zu nennen: Auch die Acustic Analysis Funktion des Plex Media Servers ist unter ARM nicht verfügbar.

Es sollte also ein NAS mit Intel N100 werden. Da die CPU auch für Mini PCs sehr beliebt zu sein scheint, habe ich mich zuerst bei den Mini PCs nach einem passenden Modell umgesehen. Die Auswahl an asiatischen Produkten ist überwältigend. Nur wenige haben einen soliden Eindruck auf mich gemacht. Darunter auch die Produkte von Minisforum. Speziell habe ich mir den Minisforum UN100L herausgepickt. Leider scheint es kaum Mini PCs mit Anschlussmöglichkeiten für zwei SATA SSDs zu geben (geschweige denn zwei M.2 SSDs). Schnell habe ich mich damit abgefunden, weiterhin mein externes USB RAID Gehäuse verwenden zu müssen.

Während meiner weiteren Recherche habe ich außerdem bei CNX Software vom UP 7000 Edge Series gelesen. Das Gerät hat ungefähr Raspi-Formfaktor und kommt mit einem großen Kühlkörper. Der dazugehörige CNX Software Review fällt gut aus. Ich war bereits im Bestellprozess und hatte meine Kreditkarte gezückt, als ich doch noch einen Rückzieher gemacht habe. Der Preis auf der Website geht für mich zwar noch in Ordnung, allerdings ist folgendes nicht im Preis enthalten:

  • Steuern
  • Zollgebühren
  • Versand
  • Ein passendes Netzteil

All diese Punkte treiben den Preis dann schließlich auf ca 380 €. Für meinen Geschmack zu viel für das gebotene. Vor allem in Anbetracht dessen, dass Mini PCs mit SSD, min 8 GB RAM und Intel N100 schon für knapp über 200 € angeboten werden. Das Up 7000 hat mich vor allem durch seine Kompaktheit überzeugt und das “Industrial PC”-mäßige, von dem ich mir eine hohe Zuverlässigkeit erhofft habe.

Schließlich bin ich irgendwo in der Mitte zwischen Mini PC und Industrial PC hängen geblieben - nämlich bei dem Minix Z100 0dB PC. Dieser ist ebenfalls durch große Kühlrippen auf der Oberseite passiv gekühlt, überzeugt durch sein Alu-Chassis und die angebotenen Schnittstellen. Für das Betriebssystem und Anwendungen kann ich die mitgelieferte M.2 SSD nutzen. Die Nutzdaten kommen wie gehabt auf die beiden SSDs in meinem externen RAID-Gehäuse.

Foto zeigt Minix Z100-0dB in meiner Hand

Erste Eindrücke vom Minix Z100 0dB PC

Wenige Tage nach meiner Bestellung war der Minix Computer auch schon da. Selbstverständlich wollte ich das vorinstallierte Windows 11 möglichst schnell loswerden und habe - wie auf fast allen meinen Server - ein klassisches Debian in der aktuellen Version Bookworm installiert. Allerdings durfte Windows dann doch einige Male starten, denn es war gar nicht so einfach, Debian vom USB Stick zu starten. Vielleicht habe ich mich auch nur ungeschickt angestellt…

So hat es dann geklappt:

  • Außen liegenden HDMI verwenden (nicht sicher, ob es eine Rolle spielt)
  • Auf Tastatur F11 gedrückt halten
  • Einschalten

Der Boot Manager sollte sich dann öffnen. Vor der Debian Installation bin ich noch ein paar BIOS-Einstellungen durchgegangen (Menüpunkt “Enter Setup” wählen). Robtech erwähnt in seinem Video über den Minix Z100-0db, dass C states per default nicht aktiv seien und das Gerät deshalb nicht so viel Leistung hätte, wie es eigentlich könnte. Bei mir war die C-States Einstellung allerdings schon auf “enabled” gesetzt. Das deckt sich mit einem späteren Update in den YouTube Kommentaren.

Ansonsten habe ich noch eine Timeout-Einstellung auf 2 Sekunden gestellt und die Einstellung “Reset if no Monitor detected” deaktiviert, denn schließlich soll mein Minix Z100 immer ohne Monitor betrieben werden können.

Nach einem Neustart (und Abstecken des PCs) habe ich den Bootmanager noch ein zweites Mal gestartet und schließlich Debian ohne Zwischenfälle installiert.

Das Gerät macht einen sehr hochwertigen Eindruck. Wahrscheinlich trägt vor allem das massiv wirkende Metallgehäuse mit Kühlrippen auf der Oberseite dazu bei. Aber auch was die Stabilität und Funktion angeht, gibt es bisher nichts auszusetzen - ganz im Gegenteil: Ich bin begeistert von der deutlich höheren Prozessorleistung. Beeindruckend, was man einer 6-Watt CPU entlocken kann!

Wenn ich auf hohem Niveau meckern darf: Nur die Position der USB 3.2-Anschlüsse ist für meinen Einsatzzweck ziemlich ungünstig. Im Einsatz als normaler Mini PC mag es sinnvoll sein, die beiden USB 3.2 Anschlüsse an die Vorderseite zu packen. Für mich ist das eher nachteilig, weil Stromversorgung und Ethernet auf der Rückseite sowieso gebraucht werden und ich hier auch gerne mein USB 3.2 Kabel für mein SSD-RAID angesteckt hätte. So bleibt die Vorderseite aufgeräumt und ich muss das USB-Kabel nicht am Gehäuse entlang nach vorne ziehen. Auch auf die seitlich abstehenden WLAN-Antennenbuchsen könnte ich verzichten. Das ist aber nicht weiter schlimm und wohl dem geschuldet, dass das Gerät typischerweise anders verwendet werden dürfte.

Benchmarks

Ich habe mich eine ganze Weile damit beschäftigt, wie ich die Leistung meines neuen NAS bzw. Medienservers hier am aussagekräftigsten messen und beschreiben kann. Bisher bin ich noch nicht auf einen grünen Zweig gekommen spare diesen Bereich explizit aus, bis ich mit meiner Testmethodik zufrieden bin.

Lasst mich nur so viel sagen: Die Leistung ist für meine Zwecke mehr als ausreichend und es macht - anders als beim Rock64-basierten Vorgänger - Spaß, das Gerät einzurichten. Wartezeiten sind kurz und Installationsroutinen zügig. Die SSD-Controller sind nun auch mit USB 3.1 Gen 2 statt USB 3.1. Gen 1 angebunden. Damit also brutto mit 10 GBit/s statt 5 GBit/s. Und die integrierte NVMe SSD (statt einer Micro SD-Karte) für das OS bringt selbstverständlich auch nochmal einen gewaltigen Performancevorteil gegenüber dem alten Setup.

Sonstiges

Der Minix Z100 wird an der Oberseite relativ warm; deutlich über Handwarm. Aber nicht heiß. Zumindest habe ich ihn bisher noch nicht so gequält. Dass die Kühlrippen warm werden ist aber zu erwarten und beweist letztendlich ja auch nur, dass der Kühlkörper wie vorgesehen funktioniert.

Meinen NAS-Umzug habe ich auch gleich als Gelegenheit genutzt, meine Daten umzuorganisieren. Wie in diesem Beitrag erwähnt, habe ich erst vor ca 1 1/2 Jahren von einem ext4 Dateisystem auf ZFS mit Verschlüsselung umgestellt. Nun wollte ich einmal BRTFS im RAID-1 ausprobieren. Da BTRFS noch (?) keine native Verschlüsselung unterstützt, nutze ich darunter einen LUKS-Layer, um meine Daten nicht im Klartext auf die SSDs zu schreiben. Das Setup läuft bisher gut und trotz der Verschlüsselung schnell. Ich habe nichts auszusetzen. Zu BTRFS gibt es ja immer wieder einmal sehr kritische Stimmen und auch solche, die wohl schon herbe Datenverluste erlitten haben. RAID-1 scheint allerdings zu den sehr stabilen Features von BTRFS zu gehören, sodass ich mir keine Sorgen darum mache. Wir werden sehen, ob ich das Experiment bereuen werde … ;-)

Auch an der Software habe ich eine Änderung vorgenommen: Da das Musikplayback mit Jellyfin unter Android in letzter Zeit häufig Probleme bereitet hat, habe ich mir den Plex Mediaserver installiert und versuche es nun einmal damit. Erste Versuche damit sind sehr gut verlaufen und ich kann mit dem neuen NAS sogar problemlos FullHD Filme live transcodieren. Sogar 4k Material sollte möglich sein, da der Prozessor Intel QuickSync in einer modernen Version unterstützt und damit zahlreiche Videocodecs sehr effizient in HW transcodieren kann.

MakeMKV Flatpak aktualisieren

$
0
0

Wer schon einmal persönliche Sicherheitskopien von DVDs unter Linux angelegt hat, ist sicherlich mit dem Ripping-Tool MakeMKV vertraut. Das Tool kann in seiner Betaversion kostenlos genutzt werden. Der Nachteil: Dazu muss es monatlich neu aus dem Quellcode kompiliert und installiert werden.

Praktischerweise hat jemand ein FlatPak-Paket für MakeMKV angelegt, das es ermöglichst, das Programm sehr bequem auf dem FlatPak Store zu laden, ohne es selbst händisch kompilieren zu müssen. Doch leider wird das FlatPak Paket nicht automatisch aktualisiert. So kann es - wie in meinem Fall - vorkommen, dass das Programm nicht startet, weil die im FlatPak enthaltene Version schon älter als einen Monat ist und deshalb den Start verweigert.

Ich habe deshalb die FlatPak-Metadaten auf die neueste Version angepasst und bin so wieder zu einem funktionierenden MakeMKV-FlatPak gekommen. Den Prozess will ich hier kurz beschreiben:

  1. MakeMKV FlatPak Quellcode herunterladen
git clone https://github.com/flathub/com.makemkv.MakeMKV.git
cd com.makemkv.MakeMKV
  1. FlatPak Builder und Abhängigkeiten installieren
sudo dnf install flatpak-builder
flatpak install org.kde.Sdk/x86_64/5.15-23.08
flatpak install runtime/org.freedesktop.Sdk.Extension.openjdk8/x86_64/23.08

Die beiden hier erwähnten Abhängigkeiten KDE SKD und OpenJDK wurden mir ursprünglich nach dem späteren flatpak-builder Kommando als fehlende Abhängkeiten angezeigt.

  1. Buildverzeichnis anlegen
mkdir build
  1. In com.makemkv.MakeMKV.yaml Programmversionen und sha256-Hashsummen anpassen

Zum Beispiel:

 - name: makemkv-oss
[...]
sources:
- type: archive
url: https://www.makemkv.com/download/makemkv-oss-1.17.7.tar.gz
sha256: 762e552d46f9ec75a7c62dcb7d97c0fd9e6a15120d0ef6f5a080cee291d3a0ef
[...]

Im makemkv-bin Block ebenfalls:

 - name: makemkv-bin
[...]
sources:
- type: archive
url: https://www.makemkv.com/download/makemkv-bin-1.17.7.tar.gz
sha256: 8c5bc831bc952b1f873cc8450c64e392db0b2479b626d180f0ffc965668951d0
[...]
  • Gebt auf die YAML-typischen, 2 Leerzeichen breiten Einrückungen acht!
  • Bei mir war Version 1.17.7 aktuell. Prüft die aktuelle Version auf der MakeMKV Website!
  • Die SHA256 Hashsumme lässt sich mit dem sha256 Kommando und einem manuellen Download der erwähnten tar.gz Datei ermitteln.
  1. FlatPak bauen
flatpak-builder build com.makemkv.MakeMKV.yaml
  1. FlatPak installieren
flatpak-builder --user --install --force-clean build com.makemkv.MakeMKV.yaml
  1. … und starten
flatpak run com.makemkv.MakeMKV

Für die aktuelle Version habe ich hier einen Patch eingereicht: https://github.com/flathub/com.makemkv.MakeMKV/pull/87 Es ist allerdings nur eine Frage der Zeit, bis wieder nachgepatcht werden muss ;-)


Den statischen Hugo-basierten Blog mit Pagefind durchsuchen

$
0
0

Im Januar 2017 bin ich mit diesem Blog von Wordpress auf Hugo umgestiegen und habe damit auch die Suchfunktion verloren. Behelfsmäßig habe ich auf der 404-Fehlerseite ein DuckDuckGo Widget eingebaut, um verirrten Lesern zumindest irgendeine Art Suchfunktion an die Hand geben zu können. Besonders gefallen hat mir das aber nicht. Klar - irgendwie hat es schon funktioniert, aber so musste ich mich auch auf eine saubere Indizierung durch die DuckDuckGo Suchmaschine verlassen.

Vor zwei Wochen habe ich allerdings über die Hugo-Dokumentation eine sehr interessante Lösung für das Suchmaschinenproblem gefunden: Die Javascript-basierte Suchmaschine “Pagefind”.

Screenshot von Pagefind

Das Konzept ist ganz einfach: Nachdem eine Hugo-Site statisch generiert wurde, lässt man den Pagefind Indexer über die HTML-Dateien laufen. Dieser zieht sich alle möglichen Informationen aus dem Sourcecode und füttert damit einen eigenen Suchindex. Der Index kann dann vom Webbrowser der Leser bei Bedarf heruntergeladen werden und von einem kleinen Suchfeld-Javascript für eine Suche verwendet werden. Die Suche läuft also - wie man es von einer Static Site erwarten würde - ebenfalls clientseitig ab.

Index-Chunks für kurze Ladezeiten

Der Clou ist bei Pagefind allerdings, dass vom Suchscript nicht der gesamte Index heruntergeladen werden muss, sondern nur ein kleiner Teil / “Chunk”. Der Index ist auf mehrere Chunkdateien aufgeteilt und wird nur bruchstückhaft heruntergeladen. Vielleicht ist der Benutzer ja schon mit einem der ersten Suchtreffer zufrieden und benötigt keine weiteren Ergebnisse? So kann Pagefind sehr schnell und Datensparsam funktionieren.

Ein Beispiel: Der Gesamte Index (Deutsch und Englisch) für thomas-leister.de ist aktuell 3,4 MB groß und auf 87 Chunkdateien verteilt. Suche ich nach “Minix”, lädt mein Browser nur zwei kleine 41 kB Chunkdateien herunter, ehe die vorläufigen Suchergebnisse präsentiert werden. Fordere ich mehr Suchergebnisse an, werden weitere Indexchunks heruntergeladen. Selbstverständlich werden auch die Indexdateien vom Browser gecached. Einmal angeforderte Chunks müssen also nicht nochmal heruntergeladen werden.

Pagefind downloaden

Pagefind gibt es als NPX package oder auch als Binary zum Download. Die NPX Option war für mich wenig attraktiv, schließlich wollte ich nicht noch einen weiteren Paketmanager nutzen und schon gar keine gammelige und überkomplexe Javascript Buildumgebung. Die Entscheidung fiel also schnell für das Binary, welches übrigens Rust Quellcode entstammt. Das Binary habe ich einfach in mein Hugo Stammverzeichnis gelegt.

cd hugo/
wget https://github.com/CloudCannon/pagefind/releases/download/v1.1.0/pagefind-v1.1.0-x86_64-unknown-linux-musl.tar.gz -O pagefind.tar.gz
tar xf pagefind
rm pagefind.tar.gz

Suchseite anlegen

Als nächstes musste in meiner Hugo-Umgebung eine neue Seite für die Suche angelegt werden. Bei mir sollen es content/search.html bzw content/search.en.html sein. Der Inhalt sieht in etwa wie folgt aus:

+++
title = "Suche"
description = "Einen Beitrag auf thomas-leister.de suchen"
+++
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({ element: "#search", showSubResults: true });
});
</script>

Die beiden im Code erwähnten Dateien pagefind-ui.css und pagefind-ui.js werden beim Indizieren der Seite übrigens automatisch generiert. Darum müssen wir uns nicht selbst kümmern.

Danach wird der Hugo-Blog ein erstes Mal gebaut und indiziert (und die CSS- bzw. Javascript-Dateien generiert):

hugo --minify
pagefind --site public/

Die Seite kann hochgeladen werden und die Suchseite funktioniert bereits grundsätzlich. Nun noch ein paar Korrekturen und Optimierungen …

Einstellungen für den Dark Mode

Mein Blog schaltet automatisch zwischen einem normalen, hellen Modus und einem Dark Mode um. Damit der Dark Mode korrekt unterstützt wird, waren noch ein paar Korrekturen am CSS nötig. Glücklickerweise lassen sich mittels CSS Variablen sehr einfach Anpassungen an der Pagefind Eingabemaske und der Ergebnisliste durchführen:

Style Anpassung in meinem Theme main.css:

body {
[...]
/* Pagefind search box */
--pagefind-ui-scale: 0.9;
--pagefind-ui-font: "Geist";
}

Und für Dark Mode:

@media (prefers-color-scheme: dark) {
body {
[...]
/* Pagefind search box */
--pagefind-ui-text: #ffffff;
--pagefind-ui-background: #2d2d2d;
--pagefind-ui-border: #6a6a6a;
--pagefind-ui-primary: #dfdfdf;
}
[...]
}

Suchergebnisse verbessern

Mit den Suchergebnissen war ich anfangs noch nicht ganz zufrieden, wenn Pagefind indizierte nicht nur Artikel, sondern auch Hashtag-Indexseiten und ähnliches. Um die zu durchsuchenden Inhalte noch weiter einzugrenzen und die Suchmaschine nur auf Artikel loszulassen, habe ich noch das data-pagefind-body Attribut in mein Theme HTML eingebaut. Das Attribut wird in dem Tag ergänzt, welches den Artikeltext enthält (und möglichst keine anderen Elemente, wie Menüs, Footer etc):

zum Beispiel in der in der themes/<theme>/layouts/partials/article.html im main Tag:

<main data-pagefind-body> <!-- Main section -->
<article itemscope itemtype="http://schema.org/TechArticle">
<header>
<h1 class="article" itemprop="headline">Title</h1>
<div class="translations"></div>
</header>
{{ .Content }}
</article>
</main>

Ebenso in der themes/<theme>/layouts/partials/page.html

<main data-pagefind-body> <!-- Main section -->
<header>
[...]
</header>
{{ .Content }}
</main>

Außerdem kann man Pagefind helfen, das Veröffentlichungsdatum eines Artikels mithilfe des data-pagefind-sort="date" Attributs korrekt zu erkennen, z.B.:

<time itemprop="datePublished" datetime="{{ .Date.Format "2006-01-02" }}" data-pagefind-sort="date">{{ i18n "postdate" . }}</time>

Über das <html> Tag und das dahin enthaltene lang Attribut kann Pagefind die Sprache ermitteln. Das ist wichtig, wenn die Suchmaschine auch “Word-Stemming” beherrschen soll. Damit ist gemeint, dass eine Suche nach “laufen” auch “läuft” und “gelaufen” - also andere Formen von Wörtern - findet.

Die header.html des Themes sollte dazu etwa wie folgt aussehen:

<!doctype html>
<html lang="{{ .Lang }}">
<head>

Es gibt noch viele weitere Möglichkeiten, den Index zu verfeinern, Elemente auszublenden etc: https://pagefind.app/docs/indexing/ Für’s erste soll es das gewesen sein. Evtl. werde ich zu einem späteren Zeitpunkt noch einmal genauer mit Pagefind auseinandersetzen.

Probleme

… denn es gibt auch noch ein paar Baustellen und Punkte, die mir noch nicht so gut gefallen. Für einige wird sich vielleicht eine Lösung finden lassen:

  • Live-Modus von Hugo wird nicht unterstützt (hugo serve)
  • Artikelbilder werden manchmal nicht angezeigt, obwohl vorhanden. Siehe z.B. “Minix Z100-0dB” Artikel. Ursache: Grafik-URLs werden von Pagefind teilweise nicht korrekt ermittelt.
  • Suche ist sprachsensitiv und nutzt für die Ergebnisse nur die aktuelle Seitensprache. Ich würde gerne deutschsprachige und englischsprachige Artikel gleichzeitig durchsuchen
  • Artikel-Tags sollen bei der Indizierung explizit berücksichtigt werden

Ein Windows 10 USB Bootmedium unter Fedora / Linux erstellen

$
0
0

Kürzlich habe ich einen gebrauchten und betagten Acer Aspire E774 Laptop als Spende für den Computertruhe e.V. fertiggemacht. Selbstverständlich gehört dazu auch das überschreiben der Festplatten mit zufälligen Daten, sodass sich keine alten Daten mehr rekonstruieren lassen. Denn “gelöscht” ist nicht gleich “sicher gelöscht”. Zum sicheren Löschen habe ich das Tool shred in einer Fedora Live Umgebung genutzt. Mit beliebigen anderen Linux-Distributionen funktioniert das aber genauso gut.

Da ein Ubuntu auf dem Laptop nicht auf Anhieb lauffähig bzw. zumutbar war, weil das Touchpad nicht funktionierte und es beim Boot immer wieder merkwürdige Probleme gab, habe ich mich dazu entschlossen, nach meiner Löschaktion einfach wieder ein Windows 10 zu installieren. Ganz so, wie es zuletzt auf dem Laptop einwandfrei lief.

Kurzerhand habe ich also bei Microsoft ein WIndows 10 64-Bit Image heruntergeladen und wollte dieses auf meinen USB-Stick kopieren:

sudo dd if=~/Downloads/Win10.img of=/dev/sdc bs=8M

Nachdem das Image gebrannt war, habe ich den Stick am Laptop eingesteckt und über eine der F-Tasten beim Start den Bootmanager aufgerufen. Üblicherweise lässt sich das Bootmedium vor dem OS-start hier noch einmal anpassen, wenn die UEFI-Einstellung nicht schon korrekt ist. Schließlich wollte ich ja vom USB-Stick starten und nicht von einer der beiden verbauten Festplatten.

Doch: Nichts! Für den USB-Stick gab es keinen Eintrag. Ein Blick ins BIOS offenbarte, dass ein Start von einem USB-Medium zwar nicht die höchste Priorität hatte, aber selbst, nachdem ich das geändert hatte, zeigte mir das BIOS bzw. UEFI nur “No Boot Medium found”.

Na gut. Vielleicht war mein Bootmedium ja defekt. Ich versuchte es mit dem Fedora Media Writer - einem grafischen Tool, das standardmäßig in jeder Fedora-Installation enthalten ist. Über den Media Writer können Fedora Images auf USB-Sticks kopiert werden, aber auch beliebige andere Betriebssystemimages. Aber auch über den Fedora Media Writer gelangte ich zu keinem bootbaren USB-Stick: Selber Fehler. Auch das umstecken in andere USB-Slots brachte keinen Erfolg. Zuletzt versuchte ich sogar die manuelle Formatierung des USB-Sticks und ein anschließendes manuelles Kopieren der Dateien. Ich habe mich dabei nach dieser Anleitung gerichtet: “Windows 10 Bootstick erstellen”. Doch wieder kein Erfolg.

Schließlich kam ich über Umwege auf das Tool woeusb. Mir gefiel, dass es aus den Fedora Paketquellen installierbar war und - im Gegensatz zu Unetbootin und anderen Alternativen - über die Kommandozeile lief. Also auch in meiner Wayland-Umgebung (anders als Unetbootin!).

Mein Windows 10 Image habe ich also wie folgt auf meinen USB-Stick kopiert:

sudo woeusb --device ~/Downloads/Win10_22H2_German_x64v1.iso /dev/sda

… und hatte damit Erfolg!

Nun - wieso hat das funktioniert und meine vorherigen Versuche nicht? Das Log von woeusb liefert eine Erklärung:

WoeUSB v5.2.4
==============================
Info: Mounting source filesystem...
Info: Wiping all existing partition table and filesystem signatures in /dev/sda...
/dev/sda: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/sda: 8 bytes were erased at offset 0x734ffffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/sda: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
/dev/sda: calling ioctl to re-read partition table: Erfolg
Info: Ensure that /dev/sda is really wiped...
Info: Creating new partition table on /dev/sda...
Info: Creating target partition...
Info: Making system realize that partition table has changed...
Info: Wait 3 seconds for block device nodes to populate...
mkfs.fat 4.2 (2021-01-31)
mkfs.fat: Warning: lowercase labels might not work properly on some systems
Info: Mounting target filesystem...
Info: Copying files from source media...
Splitting WIM: 4900 MiB of 4900 MiB (100%) written, part 2 of 26%
Finished splitting "./sources/install.wim"
Info: Installing GRUB bootloader for legacy PC booting support...
i386-pc wird für Ihre Plattform installiert.
installation beendet. Keine Fehler aufgetreten.
Info: Installing custom GRUB config for legacy PC booting...
Info: Done :)
Info: The target device should be bootable now
Info: Unmounting and removing "/tmp/woeusb-source-20240930-085645-Sunday.0twgOL"...
Info: Unmounting and removing "/tmp/woeusb-target-20240930-085645-Sunday.2DBYAG"...
Info: You may now safely detach the target device

Interessant sind hier zwei Stellen:

  • Zum einen wird das USB-Stick offenbar mit FAT formatiert (mkfs.fat) und nicht - wie in anderen Anleitungen zu lesen - mit NTFS
  • Und: install.wim wird “gesplittet”?!

Der Split der install.wim Datei erklärt, wieso es mit den anderen Methoden bei mir nicht funktioniert hat: Mit diesem Schritt wird die große Imagedatei (> 4 GB) auf kleinere Dateien aufgeteilt. Das ist nötig, weil das FAT Dateisystem nur mit Dateien bis zu einer Größe von 4 GB umgehen kann.

Und warum liefert Microsoft in seinem Image, welches ich per dd zu kopieren versucht habe, eine so große Datei aus? Weil das Microsoft Windows 10 ISO NTFS-formatiert ist. Und nicht FAT-formatiert. NTFS kann mit so großen Dateien problemlos umgehen - daher wurde mir in diversen Anleitungen auch dazu geraten, den USB-Stick nicht mit FAT, sondern mit NTFS zu formatieren.

Allerdings kommt hier eine weitere Besonderheit zum Tragen: Das Acer Laptop unterstützt offenbar keine NTFS-Bootmedien. … Wie das sein kann?

Der Laptop kam ursprünglich mit Windows 8 auf dem Markt. Damals enthielt das Windows Bootmedium noch keine Dateien, die größer als 4 GB waren. Dementsprechend kam man mit dem FAT Dateisystem aus. Folglich unterstützt der Acer E774 Laptop nur FAT im UEFI und nicht NTFS. Das erklärt auch, wieso er problemlos von allen Live-Linux USB-Sticks bootete, die ich ihm gab. Hier wird noch FAT benutzt.

Zusammengefasst: mit dem woeusb Tool hatte ich letztendlich Erfolg, weil:

  • Der Dell-Laptop nur FAT Medien unterstützt
  • Das woeusb Tool FAT nutzt
  • Und dabei aber dafür sorgt, dass keine der Dateien größer als 4 GB ist!

Der Schlüssel ist also woeusb’s “split” Funktion.

Auf neueren Laptops sollte das Kopieren des Win10 Images auch einfach via dd funktionieren. Für meinen älteren Laptop habe ich mit woeusb aber eine tolle Lösung gefunden.

Faircamp als selbstgehostete Alternative zu Bandcamp

$
0
0

Meine Mastodon-Instanz metalhead.club hat mittlerweile schon einige Unterprojekte hervorgebracht: T-Shirts, Patches, Flyer, Plakate, Sticker und sogar ein Bier. Alles mit dem metalhead.club-Branding.

Nun ist aber ein ganz besonderes Projekt fertig geworden, an dem ich nicht direkt beteiligt war: Ein metalhead.club Song! Oder wohl eher: Eine Hymne!

Einige talentierte Member haben sich im Sommer 2024 zusammengeschlossen, um einen Song für und über ihre Lieblings-Mastodoninstanz zu schreiben - und das ist ihnen sehr gelungen! Das Ergebnis kann hier angehört und gegen eine kleine Spende auch heruntergeladen werden.

Bandcamp: Als Künstler nur mit PayPal

Nun galt es allerdings, den Song auf einer passenden Plattform zu veröffentlichen und interessierten Membern und Nutzern anderer Mastodon-Instanzen zum Download anzubieten. Mein erster Gedanke ging Richtung Bandcamp. Dort höre und kaufe ich seit vielen Jahren sehr gerne Musik. Der separate Künstleraccount war schnell angelegt, doch relativ schnell weichte die anfängliche Begeisterung der Ernüchterung: Bandcamp unterstützt (zumindest hier in Deutschland) einzig und allein den Zahlungsdienstleister PayPal.

Während man als normaler Bandcamp-Nutzer wenigstens noch auf die Zahlung via Kreditkarte ausweichen kann, bleibt bei Künstleraccounts nur die Verknüpfung mit einem PayPal Konto. Anders lassen sich Zahlungen für die entstandenen Einnahmen nicht entgegennehmen. Sehr schade, denn Sinn und Zweck der Veröffentlichung sollte (auch aus Sicht der Band!) sein, dass die beim Verkauf eingenommenen Gelder für die weitere Finanzierung der Mastodon-Instanz verwendet werden sollten. Da Bandcamp keinerlei Alternativen anbietet, war die Plattform für mich also eine Sackgasse. PayPal ist für mich seit dem Zwischenfall im Dezember 2022 ein No-Go!

Faircamp als Alternative?

@MiseryPath, der das Song-Projekt ins Leben gerufen hat, brachte dann aber relativ schnell das richtige Stichwort ins Spiel: “Faircamp”!

Bei Faircamp handelt es sich nicht etwa um eine Bandcamp-Kopie, sondern genau genommen nur um einen Static Site Generator, aus dem sich Künstler ihre eigenen kleinen Bandcamp-ähnlichen Websites generieren können. Das Projekt wurde von Simon Repp ins Leben gerufen und maßgeblich entwickelt. Von Faircamp habe ich vor einer Weile schon einmal gehört und auch schon einzelne Künstlerseiten gesehen, die darauf basieren.

Also habe ich mir das Tool selbst einmal genauer angesehen und nach ersten Gehversuchen damit war ich schnell begeistert! Faircamp eignet sich hervorragend für genau das, wofür es vorgesehen ist: In kurzer Zeit eine eigene Bandcamp-ähnliche Website an den Start zu kriegen! Und das geht so:

Je nach Plattform wird das Faircamp Binary auf dem System installiert. Unter Fedora Linux ist das mit wenigen Kommandos erledigt:

1sudo dnf install cmake ffmpeg-free gcc git opus-devel rust vips-devel
2git clone https://codeberg.org/simonrepp/Faircamp.git
3cd Faircamp
4cargo install --features libvips --locked --path .

Danach wird der sogenannte “Catalog” angelegt: Ein Verzeichnis, das die zu veröffentlichen Musikdateien und (je nach Anpassungsbedarf) auch ein paar Konfigurationsdateien enthält. Hat man seine Verzeichnisstruktur fertiggestellt und alle Musikdateien passend sortiert abgelegt, wird in diesem Verzeichnis nur noch das faircamp Executable aufgerufen.

Und fertig ist die Website. Das Executable nimmt sich alle Dateien, konvertiert die Songs in die vorgegebenen bzw. passende Dateiformate, bringt auf Wunsch Metadaten mit ein und erzeugt alle HTML-Dateien und Assets, die für den Betrieb einer kleinen Website erforderlich sind. Die Dateien können dann per FTP oder SSH auf einen beliebigen Webspace hochgeladen werden. Dieser muss nur das Minimum von dem Erfüllen, was ein Webspace erfüllen muss: Er muss Dateien ausliefern können. Datenbank, PHP, Python oder irgendwelche speziellen Einstellungen werden nicht benötigt - der Charme eines Static Site Generators eben …

Das Resultat kann dann beispielsweise so aussehen:

Screenshot von Faircamp unter music.metalhead.club

Screenshot von Faircamp unter music.metalhead.club

Das Basis-Design ist vorgegeben. Allerdings lassen sich je nach Geschmack auch einige Schaltflächen oder Sektionen ein- oder ausblenden, umbenennen und die Farben für Hintergrund und Akzentfarben anpassen. Ich habe mich für ein ziemlich schlichtes Design entschieden.

“Softes” Abkassieren

Mit dabei ist auch eine “Weiche Bezahlschranke” (“Soft paycurtain”). Soll heißen: Man kann für die angebotenen Musikdateien durchaus einen Preis angeben. Entweder einen Fixpreis oder einen Preisbereich. Der Nutzer kann dann im Rahmen dessen selbst entscheiden, wie viel er für einen Download zu zahlen bereit ist. Allerdings wird der Bezahlvorgang bei dieser “soft” Variante nicht verifiziert, denn ob der Nutzer für einen Download gezahlt hat (oder nicht!), können wir technisch nicht abfragen. Das liegt in der Natur einer statischen Website: Anders als bei anderen Modellen kann der Server hier im Hintergrund keine Logik verarbeiten und somit nicht überprüfen, ob tatsächlich eine Zahlung getätigt wurde. Auch eine clientseitige Verifizierung ist relativ sinnlos - diese lässt sich sehr einfach manipulieren.

Insofern ist man spätestens hier auf die Ehrlichkeit der Nutzer angewiesen: Wer an dem Track interessiert ist, wird mit der entsprechenden “Ich habe bereits gezahlt”-Schaltfläche hoffentlich zurecht quittieren, dass er oder sie zu dem Download berechtigt ist. Selbstverständlich lässt sich die Bezahlung kinderleicht umgehen, wenn man das will.

Das lässt sich aus meiner Sicht in diesem Fall aber absolut verkraften. Denn mit der Musik soll nicht Geld gescheffelt werden - es geht darum, potentiellen metalhead.club-Spendern für ihre Spende eine kleine Belohnung in Form des Song-Downloads zu geben. Und das kann mit Faircamp ganz wunderbar umgesetzt werden - und ganz ohne PayPal, ohne Gebühren für Zahlungsdienstleister und ohne Aufschläge in Form von Steuern, weil der Plattformanbieter beispielsweise in den USA sitzt … ;-)


Wer auch mit dem Gedanken spielt, seine Musik auf einer selbst gehosteten Website zum Kauf oder kostenlosen Download anzubieten, sollte sich Faircamp einmal ansehen. Zumindest dann, wenn eine Bezahlung nicht verifiziert werden muss und es keinen Nutzeraccount mit eigener Bibliothek braucht, kann Faircamp eine tolle Alternative zu den zentralen Plattformen wie Soundcloud oder Bandcamp sein.

Mastodon Media Storage: Woher kommt der Overhead im Minio S3 Speicher?

$
0
0

Gestern habe ich mir ein kleines Tool geschrieben, das ermittelt, wie die Dateigrößenverteilung in meinem metalhead.club S3 Storage ist.

Wichtig: Das bezieht sich auf den Storage-Layer von meinem Minio-Server - nicht direkt auf die Dateien, wie sie hochgeladen wurden. Minio legt noch Metadateien dazu bzw. integriert kleinere Dateien in eigene Strukturen und legt diese dann im Dateisystem ab. Siehe: Working with Small Objects in AI/ML workloads

Ich wollte trotzdem mal sehen, wie viel Speicher durch Blocksize-Overhead verloren geht.

root@s3 /root # time ./filesize-analyzer /mnt/s3storage1/metalheadclub-media
Dateigrößenstatistik:
>= 1024K: 136138 Dateien
< 1K: 913982 Dateien
< 2K: 53357 Dateien
< 4K: 99581 Dateien
< 8K: 182476 Dateien
< 16K: 383095 Dateien
< 32K: 646094 Dateien
< 64K: 711153 Dateien
< 128K: 419150 Dateien
< 256K: 389334 Dateien
< 512K: 260465 Dateien
< 1024K: 134658 Dateien
Theoretische Gesamtgröße der Dateien kleiner als 4K: 766435063 Bytes
Tatsächliche Gesamtgröße der Dateien kleiner als 4K: 4370104320 Bytes
./filesize-analyzer /mnt/s3storage1/metalheadclub-media 100,41s user 1157,35s system 21% cpu 1:36:57,26 total

Das Ergebnis: 1.066.920 Dateien sind kleiner als die Dateisystem-Blocksize von 4K (ext4). Diese brauchen also beim Ablegen im Dateisystem mehr Speicher, als sie eigentlich groß sind. Das kommt daher, dass für jede Datei - auch wenn sie kleiner ist - im Dateisystem mindestens 4 KB (Kilobyte, 4 x 1024 bytes) reserviert werden. All diese Dateien erzeugen also einen Overhead.

Durch den Vergleich in den letzten beiden Zeilen wird klar, wie groß der Overhead ist: Eigentlich sind die betreffenden Dateien nur 766435063 Bytes (= 731 MB) groß, doch der Blocksize-Overhead im Dateisystem führt dazu, dass diese real 4370104320 Bytes (= 4167 MB) belegen. Das ist 570 % ihrer eigentlichen Größe.

Übrigens ist das nicht die ganze Geschichte: Mein Tool berücksichtigt im Moment noch keine Bruchstücke von Dateien, die größer oder gleich 4K sind. Eigentlich kommt nämlich auch bei den größeren Dateien noch Overhead durch die Blocksize dazu. Nämlich dann, wenn ihre Größe nicht genau ein vielfaches von 4K ist. Im Fall der größeren Dateien spielt der Overhead im Verhältnis zur abgespeicherten Datenmenge allerdings kaum mehr eine Rolle, daher habe ich ihn bei der Auswertung ignoriert. Besonders dramatisch Fällt der Overhead bei den ganz kleinen Dateien aus.

Wie ihr an der letzten Zeile der Ausgabe erkennen könnt, hat die Auswertung des metalhead.club Media Storage knapp über 1,5 Stunden gedauert. Das ist nicht besonders schnell - allerdings liegen im Storage auch etwa 4,3 Millionen Dateien, die einzeln auf ihre Größe untersucht wurden - dazu noch verschachtelt in relativ tiefen Verzeichnishierarchien.

Mein Tool zur Analyse ist bisher nur ein Proof of concept. Womöglich habe ich noch den ein oder anderen Bug in meinem Code. Die Berechnung sieht realistisch aus, doch ich hoffe, noch bessere Performance herauskitzeln zu können. Da gibt es sicherlich noch Verbesserungspotential. Daher habe ich es noch nicht veröffentlicht. Sollte ich aber einmal zufrieden damit sein, steht einer Veröffentlichung natürlich nichts im Wege. Interessanterweise konnte ich auf die Schnelle kein solches Tool finden, das einem eine kleine Statistik über die Dateigrößen in einem Verzeichnis liefert. Vielleicht habe ich auch nur nicht genau genug gesucht …

Auf das Thems bin ich übrigens gekommen, nachdem das Monitoring meines S3 Servers Alarm schlug, da nicht mehr besonders viel Speicher auf dem SSD-Speicher frei war. Ich war verwundert und habe (zu einem früheren Zeitpunkt) kurz die Größenangaben der verschiedenen Systeme verglichen.

  • Mastodon sagt: 603 GiB Medien gespeichert
  • Minio S3 sagt: 692 GiB Medien gespeichert
  • Filesystem sagt: 763 GiB Medien gespeichert

Da die Angaben zum Speicherverbrauch so stark abwichen, begab ich mich auf Spurensuche. Wie konnte es sein, dass Mastodon der Meinung war, dass es nur 603 GiB an Medien besaß, aber das Dateisystem mit 763 GiB deutlich mehr belegt war? Ich hoffte, darauf eine Antwort zu finden. Da Mastodon sehr viele sehr kleine Dateien abspeichert (Vorschaubilder, Avatare, …), lag mein Verdacht auf dem Blocksize-Overhead. Wie ihr seht, ist dieser im Verhältnis zur Originaldateigröße beträchtlich. Aber nicht bedeutend genug, als dass er zumindest die 763-692 GiB = 71 GiB Unterschied zwischen Minio und Filesystem erklären könnte. Denn bezogen auf meinen konkreten Fall bedeutet das nur, dass ich nur ca 3.4 GB mehr belege, als theoretisch nötig - nicht 71 GiB.

Und dann wäre da noch der Unterschied zwischen Mastodon und Minio. Der könnte evtl. an verwaisten Dateien liegen, die zwar noch im Minio S3 Speicher liegen, aber von denen Mastodon aus irgendeinem Grund nichts mehr weiß. Ich habe zwar ein tootctl media remove-orphans ausgeführt, das den Speicher um solch verwaiste Dateien eigentlich bereinigen sollte. Doch dabei sind auch nur 2.8 GB Speicher frei geworden.

Falls sich jemand tiefergehend in der Materie auskennt: Schreibt mich gerne an! Entweder auf Mastodon (thomas@metalhead.club) oder per E-Mail. Würde mich freuen, wenn ich mir die Unterschiede erklären könnte.

Ein eigenes kleines CDN für meine Mastodon-Instanz metalhead.club

$
0
0

Seit der großen Twitter-Welle, die das Mastodon-Netzwerk bzw. das Fediverse im weiteren Sinne im Herbst und Winter 2022 geflutet hat, spielen internationale User für den metalhead.club eine größere Rolle. Der Service ist vollständig in Deutschland gehostet und das war bis vor kurzem auch noch so. Doch mit der steigenden Zahl von internationalen Mitgliedern kommen auch neue Herausforderungen: Beispielsweise die der zügigen Content-Auslieferung.

Solange sich die Nutzer hauptsächlich in Deutschland und Europa befinden, sind die Latenzzeiten zum “Full Metal Server” in Frankfurt gering. Anders sieht es aber beispielsweise für die Nutzer aus Kanada, aus den USA und aus Australien aus, von denen es eine nicht zu unterschätzende Zahl im metalhead.club gibt. Für diese Nutzer war die Nutzung von metalhead.club manchmal ein kleines Geduldsspiel, denn vor allem Videos und größere Bilder erschienen nur mit einer kleinen Verzögerung auf der Website. Ich kann die Situation nur im Browser simulieren, doch bereits ein Ping von mehr als 200 ms verdirbt einem an so mancher Stelle den Spaß am Durchscrollen der Timeline.

Vor kurzem hat mich ein US-Nutzer auf ein konkretes Problem beim Abspielen eines Videos innerhalb der USA aufmerksam gemacht. Das iFixit Video konnte von ihm nur mit ständigen Unterbrechungen gestreamt werden. Grund für mich, mir endlich einmal Gedanken zu einem CDN zu machen - einem Content Delivery Network.

Sinn und Zweck eines solchen Netzwerks ist, die Inhalte, die einem Nutzer einer Plattform oder Website zur Verfügung stehen, gewissermaßen näher an den Nutzer zu rücken. Das ist nicht nur metaphorisch gemeint, sondern tatsächlich auch eine physische Angelegenheit: Selbst wenn man annimmt, dass durch Internetknotenpunkte, Signalrepeater und sonstiges Equipment keine zusätzlichen Latenzen auf der Strecke von hier in die USA ins System eingebracht werden, ist das Licht in den Meereskabeln bekanntermaßen nicht unendlich schnell. Allein dieser Umstand führt dazu, dass man die Webinhalte tatsächlich (physisch) näher am Kunden anbieten muss. Ein CDN besteht also aus vielen Datacenter-Standorten bzw. Servern, die in der Nähe der Nutzer installiert werden - idealerweise in Ballungsräumen, wo man zusätzlich von einer gut ausgebauten Netzinfrastruktur profitieren kann. Je nach Use Case in gewissen Weltregionen oder sogar auf der ganzen Welt. Die Inhalte werden auf alle (oder einige) der Server gespiegelt - je nach Bedarf. Ein US-Nutzer wird beim Anfordern der Inhalte nicht an einen EU-Server weitergeleitet, sondern ruft die Inhalte automatisch von einem Server in seiner Nähe (einem US-Datacenter) ab. Dieser Vorgang bleibt vor dem Nutzer idR transparent. Um zu erkennen, von welchem Standort aus ein Inhalt abgerufen wird (und welcher der zuständige Server ist), gibt es zwei Verfahren, die sich etabliert haben:

  • Anycast-basierte CDNs
  • “GeoIP”-basierte CDNs

Anycast-basierte CDNs

Ersterer CDN-Typ ist der “Goldstandard” und ermöglicht ein sehr elegantes (und genaues) Routing: Im Prinzip wird mehreren weltweit verstreuten Servern dieselbe öffentliche IP-Adresse zugeordnet. Welcher Server dann bei einer Anfrage tatsächlich angesprochen wird, wird durch das Routingprotokoll BGP entschieden. Dieses legt gewissermaßen die Pfade fest, die ein IP-Paket nehmen muss, um ans Ziel zu gelangen. Meistens gibt es mehrere Wege, ans Ziel zu kommen. Welcher mit den geringsten Kosten (oder der geringsten Latenz!) verbunden ist, weiß das Routingsystem. Insofern gibt man die Arbeit an das Routingsystem ab und verlässt sich darauf, dass es den schnellsten Weg durch’s Internet findet. Denn dafür ist es da - dafür wurde es entwickelt. Der Weg muss nicht immer der geografisch kürzeste sein. Wenn die Latenz auf Strecke A zu Server A kleiner ist als Strecke B zu Server B, werden die IP-Pakete automatisch zu Server A geleitet. Dieser antwortet dann. Die übrigen Server, die unter derselben IP-Adresse erreichbar sind, bekommen von der Anfrage nichts mit. So ein Anycast steckt übrigens auch hinter allen global verfügbaren DNS-Resolvern wie z.B. Google DNS, Quad9 oder Cloudflare DNS. Die IP-Adresse ist immer dieselbe - doch im Hintergrund wird man zum nächstgelegenen Server weitergeleitet, ohne etwas davon mitzubekommen.

GeoIP-basierte CDNs

Hinter dem Begriff GeoIP versteckt sich eine große Datenbank - eine GeoIP-Datenbank, wie sie beispielsweise von Unternehmen wie MaxMind angeboten werden (und wie sie beispielsweise in meinem Projekt watch.metalhead.club zum Einsatz kommt). Die Datenbank wird aus öffentlich verfügbaren Routinginformationen gespeist und speichert, welche IP-Adressbereiche (und damit ASNs) welchem Unternehmen zugeordnet sind. Sobald die Inhaberschaft einer IP-Adresse klar ist, lassen sich über das zugeordnete Unternehmen Rückschlüsse auf das geografische Nutzungsgebiet ziehen. IPv6-Adressen, die dem 2003::/19 Netz kommen (also 2003:: bis 2003:1fff:ffff:ffff:ffff:ffff:ffff:ffff) gehören beispielsweise der Deutschen Telekom. Damit ist auch klar, woher ein Benutzer kommt, der so eine IP-Adresse hat und einen Inhalt damit anfragt: Aus Deutschland. GeoIP Datenbanken speichern diese Zusammenhänge, sodass man sie dann befragen kann: “Woher kommt ein Nutzer mit der IP-Adresse xxx?”. Die Datenbank liefert dann eine mehr oder weniger genaue Antwort.

Die Genauigkeit von GeoIP-basierten CDNs

Meine Erfahrung mit den kostenlosen Angeboten solcher Datenbanken hat mir gezeigt: Auf Länderebene funktioniert das noch ganz gut - aber bis auf die Stadt genau kann man Nutzer meistens nicht verorten. Und das ist vielleicht auch ganz gut so. Es gibt bessere und schlechtere Datenbanken. Die genauen Datenbanken befinden sich meistens hinter einem kostspieligen Abomodell. Denn: GeoIP-Datenbanken müssen gepflegt werden. IP-Adressen bzw. Subnetze werden vor allem in Zeiten von IPv4-Adressmangel gehandelt und verkauft und an andere Anbieter verkauft. Ein Adressbereich, der gerade noch einem afrikanischen Mobilfunkanbieter gehört hat, kann morgen schon einem kleinen asiatischen Unternehmen gehören. Werden die Datenbanken nicht regelmäßig gepflegt, sind die Informationen veraltet und wertlos.

Und genau hier liegt der große Nachteil von GeoIP. Während sich Anycast-basierte CDNs darauf stützen, dass Routingsysteme bereits die kürzesten Strecken (im Sinne von Latenz) kennen, verlässt man sich bei GeoIP auf weitere gesammelte Informationen. Die Zuordnung IP-Adresse <=> Besitzer ist nicht schwer zu ermitteln und öffentlich. Die Schwierigkeit dürfte viel eher darin liegen, Art der Verwendung und Verwendungsregion für einen Besitzer korrekt zu ermitteln. Es kann sich schließlich um ein kleines Unternehmen handeln, aber auch im einen ISP, der die Adressen nur an seine Kunden weiterverteilt. Wie so eine Verteilung stattfindet und wie groß das geografische Verwendungsgebiet ist, weiß dann nur der Besitzer bzw. ISP. Um diese Zuordnung akkurater zu gestalten, können zusätzliche Trackinginformationen verwendet werden. So könnten vor allem dank Smartphones beispielsweise GPS-Standpunkt und verwendete IP-Adresse von einem Onlinewerbeunternehmen kombiniert werden, um den Aufenthaltsort eines Nutzers genauer zu bestimmen. Werden diese GPS-Informationen dann an den GeoIP-Anbieter verkauft, kann dieser den Verwendungsort der IP-Adresse viel genauer bestimmen. Aber das ist eine andere Geschichte …

Wie läuft nun die Weiterleitung eines Nutzers an den richtigen Server ab, wenn kein Anycast genutzt wird? Ganz einfach: Über den Verzeichnisdienst des Internets - das DNS.

Will man ein GeoIP-basiertes CDN bauen, braucht man DNS-Server, der dem Nutzer die passende IP-Adresse für seine Regio zurückgibt. Bei jeder Anfrage an den DNS-Service wird die IP-Adresse ermittelt und im Hintergrund der passende Zielhost basierend auf einer GeoIP-Datenbank ermittelt. Ich schreibe bewusst “die IP-Adresse”, denn welche IP-Adresse genau untersucht wird, kommt darauf an …

Resolver-IP oder EDNS Client Subnet (ECS)?

Man könnte nun meinen, es würde die Adresse des anfragenden Nutzers untersucht, beispielsweise die öffentliche IP-Adresse des Smartphones oder des Laptops - dem ist aber nicht so. Denn: Der Nameserver, der für meine Domain z.B. metalhead.club zuständig ist, bekommt diesen Client (fast) nie zu Gesicht. Es sind nämlich die DNS-Resolver, die für den Client die korrekte IP für einen Service anfragen - nicht die Clients selbst. Alles, was der GeoIP-Nameserver also auswerten kann, ist die IP-Adresse des Resolvers.

In vielen Fällen hat man so zwar nicht den Nutzer selbst geortet, ist aber in der Lage, seinen DNS-Resolver zu identifizieren. In den meisten Fällen ist das bei Privatkunden der Standard-DNS-Resolver des Kunden-ISPs, also beispielsweise der Deutschen Telekom. Der Standort des DNS-Resolvers stimmt in den meisten Fällen also ungefähr mit dem Standort des Kunden überein - zumindest, wenn man die Sache auf Länderebene betrachtet. Sieht mein Nameserver also eine IP, die einem Deutsche Telekom Resolver zugeordnet werden kann, kann ich idR. davon ausgehen, dass der eigentlich anfragende Nutzer ebenfalls aus Deutschland kommt.

Dann gibt es da aber noch Sonderfälle: Nämlich global verfügbare und ISP-unabhängige, offene DNS-Resolver. Beispielsweise:

  • Cloudflare DNS
  • Google DNS
  • Quad9
  • DNS4EU

Diese lassen häufig keine Rückschlüsse auf ein bestimmtes Land zu. Damit man den eigentlich anfragenden Kunden hinter dem DNS-Request dennoch ungefähr orten kann, gibt es eine EDNS-Erweiterung namens “ECS” (EDNS Client Subnet), die es erlaubt, zusätzliche Informationen an meinen Nameserver zu schicken: Nämlich das Subnetz des ursprünglichen Nutzers. Der Resolver kennt diese Client-IP naturgemäß und leitet sie in anonymisierter Form (nur Subnetz statt genauer IP-Adresse) an meinen Nameserver weiter. Dieser kann nun das per ECS weitergeleitete Subnetz statt der Resolver-IP für seine GeoIP-Datenbank verwenden und erhält ein präziseres Ergebnis zum geografischen Ursprung der Anfrage.

ECS wird nicht von allen Public Resolvern unterstützt - von DNS4EU wird ECS beispielsweise noch nicht unterstützt - auch, wenn das Feature bei dem Betreiber auf der Todo-Liste steht:

DNS4EU currently does not support EDNS, but we want to add support for this feature. Unfortunately, we do not have a timeline on when this should be done. Please check the project page https://www.joindns4.eu/learn for updates.

Was also nutzen? CDN-Provider oder selbst hosten?

Anycast-Systeme sind teuer. Sehr teuer. Und für jemanden wie mich nicht umzusetzen. Denn dazu müsste man einen eigenen IP-Adressbereich besitzen, für den man selbst BGP Routen definieren kann. Somit war das Thema für mich schnell erledigt. Anycast? Betreibe ich nicht selbst. Nun gibt es da aber noch eine andere Möglichkeit: Schließlich kann man sich ja in bestehende Anycast-Systeme einmieten oder CDNs einfach mieten. Auch das mieten von Anycast-IPs ist kostspielig und wird von den Hostern, die ich nutze bzw. nutzen will, nicht in der Form angeboten.

Frei mietbare CDNs gibt es jedoch wie Sand am Meer: BunnyCDN, Akamai, Cloudflare, KeyCDN, … sie sind nur einige populäre Beispiele. Oftmals werden diese CDNs auch in Kombination mit DNS-Services, S3 Speicher oder DDoS-Schutz verkauft. Mit dabei sind auch einige Angebote, die man sich als kleiner Plattformbetreiber im Internet gut leisten kann. Mein Interesse galt vor allem BunnyCDN. Mit ca. 20-25 € im Monat könnte ich die etwa 2.5 TB an metalhead.club Medien im Monat weltweit ausspielen lassen. Doch es gibt zwei Haken, die mich derzeit noch daran stören:

  1. Ich muss die TLS-Terminierung aus der Hand geben und die Medien fremd hosten lassen. Meine Nutzer verlassen sich allerdings darauf, dass ich alle Daten in meiner eigenen Hand habe
  2. Das Ökostrom-Problem: Es ist einfacher geworden, Serverhoster zu finden, die mit reinem Ökostrom arbeiten. Bei CDN-Anbietern sieht es leider noch nicht so gut aus: Nur Cloudflare scheint 100 % Ökostrom einzusetzen.

Damit fällt also auch das Einmieten in einem bestehenden CDN (erst einmal?) weg.

Ein eigenes Geo-IP basiertes CDN basteln

Bleibt also nur: Selbst basteln!

Meine Ansprüche sind relativ niedrig. Mir ist natürlich bewusst, dass ein eigenes GeoIP-basiertes CDN schnell an seine Grenzen bzw. Effizienz, Genauigkeit und Performance kommt. Ganz besonders dann, wenn man - wie ich - vor hat, nur sehr wenig Geld dafür zu investieren. Mir geht es in erster Linie darum, die Nutzererfahrung für einige meiner metalhead.club Member besser zu machen. Die perfekte Lösung werde ich auf diese Art und Weise nicht erreichen.

Da die Versorgungssituation innerhalb Europas in Ordnung ist und Latenzmessungen über Online-Tools (dazu später mehr!) ein zufriedenstellendes Ergebnis geliefert haben, habe ich mich vor allem zwei Standorten gewidmet: Dem amerikanischen Kontinent und dem asiatischen. Damit wäre mit insgesamt 3 Medienservern für media.metalhead.club der ganze Globus grob abgedeckt.

Über das “Ping Simulation” Tool habe ich mir angesehen, mit welchen Latenzen ich global in etwas rechnen kann, wenn ich die Server so verteile, wie ich es mir vorgestellt habe. Nämlich so, wie es mir der Serverhoster Hetzner durch seine Cloud erlaubt. Einen Hetzner-Account habe ich bereits und kenne mich mit den Produkten aus - zudem erfüllen sie meine Anforderungen (Deutsches Unternehmen, DSGVO-konform, 100 % Ökostrom). Und auch, was die Standorte angeht, hat Hetzner genau das im Angebot was ich will. Denn es gibt neben europäischen Rechenzentren auch zwei in den USA und eines in Singapur.

Die Simulation ergab, dass ich weite Teile der Welt mit etwa 100 ms oder weniger erreichen kann. Natürlich auch immer abhängig vom lokalen Provider, aber das Gesamtbild war einigermaßen gleichmäßig und nur an wenigen Standorten waren die Paketlaufzeiten deutlich höher als 150 ms. Also habe ich bei Hetzner kurzerhand einen Cloudserver in Ashburn (VA, USA) und einen in Singapur gebucht.

Implementierung

Was die vServer angeht, habe ich zu den kleinsten und günstigsten Modellen gegriffen, die Hetzner anbietet. Ich wollte klein starten und das Ganze erst einmal ausprobieren. Wie sich gezeigt hat, reichen die CPX11 Cloudserver bisher völlig aus. Sie müssen nur statische Dateien cachen und ausliefern - mehr nicht.

Nginx Caching Proxy

Auf den Servern läuft nur eine Nginx-Instanz, die

  • … Anfragen an media.metalhead.club entgegennimmt
  • nachsieht, ob die angeforderten Inhalte schon lokal vorliegen
  • (falls nicht, werden diese vom original S3 Bucket angefragt und gecached)
  • an den Client zurückliefert

Meine Konfiguration für Nginx:

proxy_cache_path /tmp/nginx-cache-metalheadclub-media levels=1:2 keys_zone=s3_cache:10m max_size=30g
inactive=48h use_temp_path=off;
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name media.metalhead.club;
include snippets/tls-common.conf;
ssl_certificate /etc/acme.sh/media.metalhead.club/fullchain.pem;
ssl_certificate_key /etc/acme.sh/media.metalhead.club/privkey.pem;
client_max_body_size 100M;
# Register backend URLs
set $minio_backend 'https://metalheadclub-media.s3.650thz.de';
root /var/www/media.metalhead.club;
location / {
access_log off;
try_files $uri @minio;
}
#
# Own Minio S3 server (new)
#
location @minio {
limit_except GET {
deny all;
}
resolver 9.9.9.9;
proxy_set_header Host 'metalheadclub-media.s3.650thz.de';
proxy_set_header Connection '';
proxy_set_header Authorization '';
proxy_hide_header Set-Cookie;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Access-Control-Allow-Methods';
proxy_hide_header 'Access-Control-Allow-Headers';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header x-amz-meta-server-side-encryption;
proxy_hide_header x-amz-server-side-encryption;
proxy_hide_header x-amz-bucket-region;
proxy_hide_header x-amzn-requestid;
proxy_ignore_headers Set-Cookie;
proxy_pass $minio_backend$uri;
# Caching to avoid S3 access
proxy_cache s3_cache;
proxy_cache_valid 200 304 48h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_revalidate on;
expires 1y;
add_header Cache-Control public;
add_header 'Access-Control-Allow-Origin' '*';
add_header X-Cache-Status $upstream_cache_status;
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
add_header X-Served-By "cdn-us.650thz.de";
}
}

Übrigens: Das Hinzufügen des X-Served-By Headers erleichtert das Debuggen bzw. Überprüfen der Funktion. Für jedes Bild, das von media.metalhead.club geladen wird, lässt sich so sehr einfach feststellen, welcher der drei Hosts die Datei denn tatsächlich übertragen hat. Einsehbar ist der Header beispielsweise in den Entwicklertools jedes Webbrowsers.

GeoIP-basierte DNS-Zonen

Fehlt nun noch der GeoIP-Anteil, damit die metalhead.club Member an den jeweils passenden Server weitergeleitet werden. An dieser Stelle hätte ich einen eigenen GeoIP-/GeoDNS-fähigen Nameserver konfigurieren können (wie ich es in einem eigenen Kundenprojekt schon getan habe), doch für mein Experiment wollte ich nicht zu viel Aufwand betreiben. Also habe ich mich etwas umgesehen und bei Scaleway ein sehr interessantes Angebot gefunden. Dort kann man nämlich Nameserver mieten, die zum einen das GeoIP Feature beherrschen und sich zum anderen auch mit externen Domains nutzen lassen (also Domains, die man nicht bei Scaleway gebucht hat). Je nach Nutzung kostet ein solcher Service nur wenige Cent bis niedrige einstellige Eurobeträge für meinen Fall. Genau kann ich es sagen, wenn mein CDN eine Weile gelaufen ist ;-)

Allerdings wollte ich Scaleway nicht meine 650thz.de Root Zone überlassen. Diese sollte nach wie vor von Core-Networks.de gehostet werden. Stattdessen habe ich eine eigene Zone “cdn.650thz.de” angelegt und die Scaleway-Nameserver als autoritative Nameserver festgelegt. Zuvor musste allerdings die Eigentümerschaft von cdn.650thz.de gegenüber Scaleway bewiesen werden. Die NS-Einträge habe ich also noch nicht gesetzt, sondern zuerst einen Verifizierungseintrag in der metalhead.club Zone angelegt:

_scaleway-challenge IN TXT 3600 "verifizierungs-string"

Nach ca. 30 Minuten war bestätigt, dass ich die Kontrolle über die Domain habe und ich konnte anfangen, die Scaleway-Nameserver für cdn.650thz.de in der 650thz.de Zone bei Core-Networks.de einzutragen:

cdn 86400 NS ns0.dom.scw.cloud.
cdn 86400 NS ns1.dom.scw.cloud.

(den _scaleway-challenge Eintrag habe ich wieder gelöscht)

Übrigens: Die 650thz.de Domain nutze ich für sämtliche Infrastruktur im Hintergrund, da metalhead.club zum 650thz.de Projekt gehört. Lasst euch davon nicht verwirren.

In der cdn.650thz.de Zone habe ich dann schließlich den GeoIP-Eintrag für metalheadclub-media.cdn.650thz.de hinzugefügt:

Screenshot Eingabemaske DNS bei Scaleway

Der default-Eintrag zeigt auf s3.650thz.de - den bisherigen S3-Medienserver in Frankfurt. Sollte jedoch ein Clientstandort in Nord- oder Südamerika erkannt werden, wird stattdessen der CDN-Server cdn-us.650thz.de genutzt. Wird ein Standort in Asien oder im pazifischen Raum erkannt, wird auf den CDN-Server cdn-ap.650thz.de verwiesen.

Essentiell ist übrigens der abschließe Punkt hinter jedem der FQDNs in der Eingabemaske! Wird kein Punkt gesetzt, wird ein Eintrag in derselben Zone referenziert - das kann nicht funktionieren.

Die Verkettung von DNS-Records mittels CNAME hat sich im Nachhinein übrigens als ungünstig für weit entfernte Benutzer herausgestellt. Mehr dazu unter “Weitere Verbesserungen: DNS-Optimierung”

Umschaltung auf CDN-Betrieb

Um nun alle Anfragen an media.metalhead.club mit dem passenden Medienserver zu beantworten, war noch noch eine Änderung nötig: Der Eintrag für media.metalhead.club musste im metalhead.club Zonefile auf metalheadclub-media.cdn.650thz.de verweisen:

media IN CNAME 3600 metalheadclub-media.cdn.650thz.de.

(auch hier wieder auf den abschließenden Punkt achten!)

Um das CDN nach der DNS-Umstellung (und der zuvor eingetragenen TTL) zu testen, habe ich kurzerhand von jedem der Server aus einen nslookup auf media.metalhead.club gemacht:

  • Von s3.650thz.de aus: Gibt eigene IP zurück
  • Von cdn-us.650thz.de aus: Gibt eigene IP zurück
  • Von cdn-ap.650thz.de aus: Gibt eigene IP zurück

Die dynamische Zuteilung funktionierte also!

Wie gut funktioniert das?

Um nun noch ein breiteres Bild zu bekommen, habe ich noch weitere Tests durchgeführt, zum Beispiel mit folgenden Tools:

Auch metalhead.club User habe ich dazu befragt. Ein Nutzer aus Australien hatte zuvor diese Ping-Zeiten zu media.metalhead.club:

64 bytes from 5.1.72.141: icmp_seq=0 ttl=38 time=369.765 ms
64 bytes from 5.1.72.141: icmp_seq=1 ttl=38 time=350.830 ms
64 bytes from 5.1.72.141: icmp_seq=2 ttl=38 time=469.047 ms
64 bytes from 5.1.72.141: icmp_seq=3 ttl=38 time=379.882 ms
64 bytes from 5.1.72.141: icmp_seq=4 ttl=38 time=400.998 ms

(also im Mittel knapp 400 ms!)

Danach waren die Latenzen deutlich niedriger, weil er korrekterweise zu dem US-Server statt dem EU-Server weitergeleitet wurde:

64 bytes from 5.223.63.2: icmp_seq=0 ttl=50 time=150.384 ms
64 bytes from 5.223.63.2: icmp_seq=1 ttl=50 time=230.931 ms
64 bytes from 5.223.63.2: icmp_seq=2 ttl=50 time=360.083 ms
64 bytes from 5.223.63.2: icmp_seq=3 ttl=50 time=142.986 ms
64 bytes from 5.223.63.2: icmp_seq=4 ttl=50 time=138.416 ms
64 bytes from 5.223.63.2: icmp_seq=5 ttl=50 time=300.561 ms

(im Mittel 264 ms).

Nicht weltbewegend, aber dennoch bedeutend weniger. Dass dieser Benutzer trotzdem eine relativ hohe Latenz zum Medienspeicher hat, kommt daher, das die Strecke von Australien nach Singapur trotzdem nicht zu vernachlässigen ist. Hier zeigt mein kleines CDN gleich seine Schwäche: Durch nur 3 Standorte kann man keine Bestperformance erreichen - aber man kann immerhin eine kleine Verbesserung herbeiführen.

Für einen anderen australischen User hat sich die Latenz von durchschnittlich 346 ms auf 108 ms verbessert!

Und für den User aus den USA, den ich zu Beginn erwähnt habe? Der konnte nach der Umstellung das fragliche Video problemlos und flüssig abspielen.

Mit dem BunnyCDN Test habe ich einen Vorher-Nachher-Vergleich angestellt:

Vorher:

BunnyCDN Screenshot zeigt Latenzzeiten vor der Umstellung auf ein CDN

Nachher:

BunnyCDN Screenshot zeigt Latenzzeiten nach der Umstellung auf ein CDN

Ziel erreicht!

Für die meisten Standorte rund um die USA, Australien, Korea und Japan haben sich die Latenzzeiten deutlich verbessert. Europa ist wie erwartet unverändert. Es gibt aber auch einige unerwartete Ausreißer, die wohl einer falschen IP-Lokalisierung zum Opfer gefallen sind, beispielsweise in Singapur selbst (interessant!), in der Türkei, Indien, im US-Bundesstaat Texas und Chile.

Selbstverständlich hängen die Ergebnisse auch stark damit zusammen, mit welchen IPs getestet wurde und wie die Anbindung vor Ort aussieht. Womöglich wurde die Türkei schon als “Asiatischer Raum” gewertet und der Standort, von dem aus in Texas getestet wurde, wurde womöglich als europäisch erkannt.

Ich werde die Ergebnisse weiter beobachten. Da hier vor allem von Rechenzentren aus gemessen wurde (und nicht von Privathaushalten aus entsprechenden ISP-Bereichen), spiegeln die Ergebnisse womöglich nicht die ganze Wahrheit wider.

Man kann sich also zurecht fragen: …

Ist auf GeoIP Verlass?

Da ich bei der Prüfung meines CDNs mit den oben erwähnten Test-Websites festgestellt habe, dass der kontaktierte Server nicht immer korrekt dem Teststandort entsprach (siehe Ergebnis des Singapur-Standorts!), habe ich meine Nutzer befragt. Denn die Ergebnisse dieser Web-Tools sind vermutlich nicht besonders genau. Ich schätze, dass der Mechanismus für Endkunden-Adressbereiche viel besser funktionieren als für Datacenter-Adressbereiche. Schließlich können für Privatkunden-IPs sehr viel einfacher GPS- und andere Standortinformationen “hinfusioniert” werden.

Und tatsächlich: Die Nutzer bekamen in fast allen Fällen den korrekten Medienserver aufgrund ihrer Herkunft zugeteilt. Siehe: https://metalhead.club/@thomas/114676148254141233 (Den Post hätte ich wohl eher mit einer Umfrage-Option versehen sollen … )

Die letzte manuelle Zählung am 15.06.2025 hat ergeben:

  • Für 83 Benutzer (allerdings die meisten davon aus DE), hat die Zuteilung funktioniert
  • Für 4 Benutzer hat diese nicht funktioniert. 3 davon wurden in die USA weitergeleitet statt Frankfurt - ein Benutzer wurde sogar nach Singapur verbunden, obwohl er in Deutschland war.

Ich habe den Scaleway Support dazu befragt, welche GeoIP Datenbank genutzt wird und wie häufig diese aktualisiert wird. Folgende Antwort habe ich erhalten:

There can occasionally be discrepancies, such as the one you experienced with the Singapore data center being identified as European. It can take time for GeoIP databases to reflect changes like IP reallocation. Our GeoIP database provider involve frequent updates, often on a weekly or bi-weekly basis. While we don’t publicly disclose the specific third-party GeoIP database provider we use, we rely on a reputable industry-standard provider to ensure the highest possible accuracy for our customers. We understand that accurate GeoIP routing is crucial for latency-sensitive applications. If you encounter significant and persistent inaccuracies, we encourage you to report them to our support team so we can investigate further.

Außerdem habe ich nachgehakt, ob bei Scaleway GeoIP nur die Resolver-Adresse für die Ortung genutzt wird, oder auch eine Auswertung des EDNS ECS Felds erfolgt. Das würde die Genauigkeit verbessern:

Our product team has got back to us to indicate that you are right. First we use the EDNS/ECS feature and if it’s not available we use resolver’s IP address.

Wie geht’s weiter?

Mit einem Anycast-basiertem CDN könnte man sicherlich deutlich bessere Ergebnisse erzielen, aber das ist für mich aus den oben genannten Gründen ja erst einmal keine implementierbare Lösung. Da mein kleines GeoIP CDN zwar nicht in allen, aber in den meisten Fällen eine Verbesserung erreicht, werde ich das Experiment weiterlaufen lassen. Vielleicht gibt es noch die ein oder andere Stellschraube, mit der man die Lokalisierung verbessern kann.

Ansonsten spiele ich auch mit dem Gedanken, evtl. doch auf ein professionelleres, fremd gehostetes und AnyCast CDN umzusteigen. Dann wäre Cloudflare allerdings der einzige Anbieter, auf den ich mich stützen könnte. Denn das “100 % Green Energy” Label meiner Dienste bin ich nicht bereit für ein CDN aufzugeben.

Insgesamt kann man sagen, dass mein GeoIP-CDN hilft, den Zugriff für die meisten weit entfernten Benutzer zu verbessern. Es gibt aber auch Einschränkungen:

  • Nicht in allen Fällen wird der passende CDN-Server zugewiesen (Ungenauigkeit der GeoIP Datenbank)
  • Für einen Latenzvorteil muss ein bestimmter Inhalt bereits von einem anderen Benutzer aus der Region abgerufen worden sein (Inhalte werden (noch?) nicht über alle CDN-Server synchron vorgehalten => Mehr Speicherbedarf)
  • Nur Medieninhalte werden vom CDN vorgehalten. API-Services und Webfrontend werden nach wie vor nur in der EU gehostet.

Weitere Verbesserungen: DNS-Optimierung

Nachdem ich mein CDN ausgerollt habe, habe ich durch Performancetools festgestellt, dass die DNS-Namensauflösung vor allem für weit entfernte Benutzer einen großen Teil der gesamten Ladezeit einnimmt. Wie sich das verbessern lässt, habe ich in diesem Folgeartikel beschrieben: “Globale DNS-Auflösung durch Verzicht auf CNAMES beschleunigen

Globale DNS-Auflösung durch Verzicht auf CNAMES beschleunigen

$
0
0

Wie beispielsweise in meinem Artikel “Ein eigenes kleines CDN für meine Mastodon Instanz metalhead.club” gezeigt, nutze ich gerne CNAMEs, um meine DNS-Einträge zu organisieren. Üblicherweise lege ich für jeden Host einen DNS-Record an, der seinen Hostnamen auf die IP mappt. Dann verwende ich einen oder mehrere CNAME-Einträge, um gewisse (Sub-) Domains je nach Dienst und Verwendungszweck auf diese CNAMES zu verlinken. Das hilft bei der Übersicht und kann die Organisation erleichtern - vor allem, wenn einmal IP-Adressen für Hosts ausgetauscht werden müssen. Dank der Verkettung der Einträge muss nur das letzte Mapping von Server-Hostname zu IP angepasst werden, wenn die IP-Adresse des Zielhosts sich einmal ändern sollte.

Anhand des Beispiels aus dem vorher erwähnten Artikel kann eine CNAME-Kette beispielsweise so aussehen:

[thomas@thomas-nb]~% dig media.metalhead.club
;; ANSWER SECTION:
media.metalhead.club. 1800 IN CNAME metalheadclub-media.cdn.650thz.de.
metalheadclub-media.cdn.650thz.de. 21600 IN CNAME s3.650thz.de.
s3.650thz.de. 3600 IN A 5.1.72.141

Es wird also zunächst media.metalhead.club aufgelöst, dann metalheadclub-media.cdn.650thz.de und schließlich s3.650thz.de. Erst dann steht die IP-Adresse für den Zielhost fest. Das gefällt mir im Sinne der Ordnung, weil die Gliederung für mich im Kopf Sinn ergibt. Wie ich allerdings bei der Analyse meines kleinen CDNs für metalhead.club feststellen müsste, ist das für die Performance nicht besonders förderlich. Denn der Gebrauch von CNAMES hat aber auch einen bedeutenden Nachteil: Die Zeit für eine DNS-Auflösung verlängert sich mit jedem CNAME in der Kette bis zur IP-Adresse.

Allein die Auflösung der zweiten Zeile aus der “Answer Section” bedeutet beispielsweise: Erst .de auflösen (DNS-Rootserver befragen), dann .de Nameserver nach 650thz.de befragen, dann 650thz.de Nameserver nach cdn befragen, schließlich die cdn-Nameserver nach metalhead.club-media befragen. Erst dann steht fest: Es muss s3.650thz.de aufgelöst werden. Das Spiel beginnt erneut, bis am Ende schließlich die IP-Adresse feststeht.

Es dürfte klar sein, dass diese Kette aus zwei CNAME-Einträgen durchaus Zeit braucht, bis sie vom DNS-Resolver des Nutzers aufgelöst ist. Für regionale Nutzer, die sich in Reichweite dieser Nameserver befinden, fällt der Aufwand nicht so sehr ins Gewicht, da das Auflösen jedes Kettenglieds in der Regel nur einige zehntel Millisekunden braucht.

Anders sieht es aber für Nutzer aus, die (wie im Fall des metalhead.club) beispielsweise aus den USA oder aus Australien auf Nameserver in Europa zugreifen müssen. Denn:

  • Rootserver: Global
  • .de-Nameserver: Global
  • 650thz.de: Regional, EU
  • cdn.650thz.de: Regional, EU

In meiner Kette sind 4 Zonen auf 4 (virtuellen) Nameservern involviert. Jeder muss einzeln befragt werden und nur zwei davon sind global aufgestellt, d.h. sie sind weltweit mit minimalen Latenzzeiten erreichbar. Alle regionalen Server sind für alle, die sich nicht in der EU aufhalten, nur mit deutlich erhöhten Laufzeiten erreichbar. Dass gleich 2x (aus ihrer Sicht “langsame”) regionale DNS-Nameserver im Fall eines US-Nutzers abgefragt werden müssen, sorgt dann dafür, dass sich die erhöhten Latenzen sehr schnell aufaddieren.

Aus Sicht eines US-Nutzers oder eines Nutzers aus Asien ist es also besonders wichtig, dass Auflösungsketten möglichst kurz gehalten werden. Besonders deutlich wird das Bild, wenn man ein Tool wie z.B. globalping.io nutzt und sich die globalen Auflösungszeiten im DNS-Modus einmal ansieht:

Screenshot globalping.io ohne Optimierung

Während EU-Nutzer media.metalhead.club (im Screenshot media-old.metalhead.club) nach etwa 0,004 - 0,070 Sekunden aufgelöst bekommen, muss sich ein Japaner deutlich länger gedulden: Nämlich ca 1,5 Sekunden! Erst dann kann sein Browser anfangen, Kontakt zum endgültigen Zielserver aufzunehmen.

Das gilt selbstverständlich nur für die erste DNS-Anfrage nach Ablauf der TTL. Kurz darauf folgende Anfragen werden vom DNS-Resolver üblicherweise aus dem Cache beantwortet, sodass diese um Größenordnungen schneller beantwortet werden. Nach Ablauf der TTL dauert der erste Request allerdings wieder 1,5 Sekunden! Allerdings kann bei den wenigen Nutzern in einigen Regionen nicht angenommen werden, dass diese einen gemeinsamen DNS-Resolver nutzen. Daher ist es mir wichtig, dass auch ungecachte Requests zügig beantwortet werden.

Also habe ich mich entschieden, Auf CNAME-Ketten ganz zu verzichten. Und das ging so:

  • In der metalhead.club Zone habe ich die Subdomain media.metalhead.club direkt an den GeoIP-Nameserver von Scaleway delegiert
  • Der Scaleway-Nameserver löst dann direkt zu einer IP-Adresse auf, die zur Region des Nutzers passt

Es sind also keine CNAMEs mehr an der Auflösung beteiligt: Die gesamte Auflösung basiert nur noch auf Zonendelegation.

Das Ganze ließe sich durch folgende Maßnahmen nochmal verbessern:

  • Keine Trennung von “normalem” DNS-Nameserver (Core-Networks.de) und GeoIP Nameserver (Scaleway): Dann könnte auf die “media”-Zonendelegation verzichtet werden
  • Globale Erreichbarkeit aller Nameserver (ist allerdings ein nicht unbedeutender Kostenfaktor, außerdem neuer DNS-Anbieter erforderlich)

Aber auch ohne diese beiden Verbesserungen konnte ich alleine durch den Verzicht auf CNAMES auf Auflösungszeit deutlich verkürzen, wie dieser Screenshot zeigt:

Screenshot von globalping.io: Zeigt Auflösungszeit von Japan. Jetzt 676 ms statt 1,884 Sekunden

Das ist doch schon viel besser: Nach der Optimierung durch Verzicht auf CNAMES dauert die Auflösung von media.metalhead.club in diesem Fall nur noch rund 0,7 Sekunden statt 1,9 Sekunden!

Übrigens: Die Hauptdomain metalhead.club löst technisch bedingt sowieso gleich auf IP-Adressen auf. Hier ist keine Optimierung nötig gewesen.