Warum du deine MTU Einstellungen prüfen solltest, wenn du Docker Swarm auf virtualisierter (OpenStack oder Qemu) Hardware (z.B. Hetzner Cloud) betreibst

Es gibt so Momente in der Karierre als Entwickler/DevOp, bei denen man sich fragt, ob man nicht mehr ganz Dicht ist und überhaupt keine Ahnung hat, was man eigentlich tut.

Genau so einen Moment hatte ich diese Woche mit einem Kollegen, als unser Docker Swarm Stack ein sehr komisches Verhalten zeigte. Wir haben eine simple Webapp mit einer extra API, Datenbanken, Storage usw. dahinter und lassen die Systeme mittels Nginx Reverse Proxy miteinander reden. All das funktionierte wunderbar, aber aus unerfindlichen Gründen wurde von der React Website nur der HTML Teil ausgeliefert, aber das Laden der komprimierten CSS und JS schlug fehl. Natürlich schießt einem da gleich in den Kopf, dass es vielleicht ein Permission Problem ist, oder dass die Dateien in einem Unterordner liegen. Vielleicht ist Nginx hinter einem anderen Nginx ein Problem usw. Wir haben sogar den Webserver ausgetauscht weil uns die Lösung nicht einfallen wollte.

Irgendwann kam ich auf die Idee, die funktionierende index.html Datei einfach in den Dateinamen der komprimierten CSS sowie der komprimierten JS umzubenennen und auf einmal konnte ich beide Dateien ohne Probleme abrufen. Sprich, es konnte nicht am Dateityp oder Pfad liegen, sondern es war relativ schnell klar, dass die Größe der Datei scheinbar einen Einfluss auf das Verhalten hat.

Nach etwas Google Magic hatten wir dann herausgefunden, dass scheinbar die MTU der Netzwerkinterfaces das Problem verursacht.

Kurzer Exkurs:
Die MTU ist eine Abkürzung für “Maximum Transmission Unit” und steht für die maximale Größe eines Pakets, das über ein Netzwerk gesendet werden kann, ohne fragmentiert zu werden. In einfacheren Worten bedeutet dies, dass MTU die maximale Größe eines Datenpakets ist, das über das Netzwerk gesendet werden kann, ohne in kleinere Teile aufgeteilt zu werden. Wenn ein Paket größer als die MTU-Einstellung ist, muss es in kleinere Pakete aufgeteilt werden, um über das Netzwerk gesendet zu werden. Zum Beispiel beträgt die Standard-MTU für Ethernet 1500 Bytes. Wenn ein Paket größer als 1500 Bytes ist, wird es in kleinere Pakete aufgeteilt, um über das Netzwerk gesendet zu werden.

Problematisch wird es, wenn Netzwerkinterfaces unterschiedliche MTU verwenden, in unserem speziellen Fall die virtuellen Netzwerk Interfaces von Docker (Swarm) sowie die Netzwerkschnittstellen des Servers selbst. Denn bei virtualisierter Hardware, wie sie viele Cloud Anbieter nutzen (speziell wenn OpenStack oder auch Qemu zum Einsatz kommt), kann es sein, dass die MTU dieser virtuellen Netzwerkschnittstellen geringer als die Default 1500 Bytes ist. Ich habe es so verstanden dass die virtualisierung sich hier einfach 50 Bytes nimmt und diese für Verschlüsselung usw verwendet.

Prüfen kann man das einfach mittels

ip link

auf der Linux Shell und bekommt dann folgende Ausgabe:

1: lo:  mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1400 qdisc pfifo_fast qlen 1000
link/ether 00:0f:ea:91:04:07 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 00:0f:ea:91:04:08 brd ff:ff:ff:ff:ff:ff

Wenn ihr so eine Konstellation vorfindet, also dass die MTU eures Netzwerkinterfaces (eth0 in diesem Fall) KLEINER ist als der Wert, der bei docker0 steht, dann könnte ein Problem bestehen.

Da das virtuelle Docker Interface auf der “echten” Hardware aufsetzt, muss dessen MTU gleich oder kleiner der MTU des Netzwerk-Interfaces sein.

Man kann nun einfach versuchen, die MTU mittels

ip link set dev eth0 mtu 1500

setzen und das Problem ist gelöst. In meinem Fall ging das aber nicht, da das virtuelle Netzwerkinterface keinen höheren Wert erlaubte.

Also mussten wir anders heran gehen und Docker dazu zwingen, nicht seinen Default Wert von 1500 zu nehmen, sondern einen geringeren Wert. Dazu gibt es zwei Punkte, an denen man ansetzen kann.

Zum einen kann man in der Datei /etc/docker/daemon.json den Wert

{
"mtu": 1400
}

setzen, den Docker Service neu starten und wird dann sehen, dass die MTU von docker0 sich entsprechend geändert hat. Dies bringt aber nur etwas, wenn man Docker als Single Node betreibt.

Wenn man aber Docker Swarm einsetzt (evtl passiert das auch im Kubernetes Umfeld, da bin ich nicht ganz sicher), dann reicht das nicht aus. Denn dann verwendet Docker virtuelle Netzwerke, die jeweils eigene virtuelle Interfaces anlegen. Ausserdem sind zwei Netzwerke standardmäßig auf allen Nodes vorhanden:

ingress
docker_gwbridge

Wir haben es folgendermaßen gelöst:
Bevor eine Node dem Docker Swarm beitritt, löschen wir ihr docker_gwbridge und setzen es so neu auf:

docker network create \
--subnet 10.11.0.0/16 \
--opt com.docker.network.bridge.name=docker_gwbridge \
--opt com.docker.network.bridge.enable_icc=false \
--opt com.docker.network.bridge.enable_ip_masquerade=true \
docker_gwbridge

Wichtig: “docker_gwbridge” wird zwar vom Typ lokal angezeigt, es ist aber DAS Netzwerk über welches die Swarm Nodes/Docker Daemons miteinander sprechen!

Anschließend lassen wir die Nodes dem Docker Swarm beitreten. Nachdem alle Nodes drin sind, haben wir auf der Swarm Manage Node das Ingress Netzwerk gelöscht (wahrscheinlich reicht es auch, dass der Swarm erstmal initialisiert wurde) und dann mittels folgendem Befehl neu angelegt:

docker network create -d overlay --ingress --opt com.docker.network.driver.mtu=1400 ingress

Dieses neue Ingress Netzwerk ist dann erstmal nur auf der Master Node des Swarms sichtbar, wird auf den anderen Nodes aber dymanisch erzeugt, sobald der erste Container darauf deployed wird. Wenn aktuell kein Container aus dem Swarm auf dieser Node läuft, verschwindet das Ingress Netzwerk wieder von der Node bzw. wird von ihr gelöscht!

Anschließend haben wir den Swarm Stack deployed und schon konnten alle Services wieder miteinander sprechen und auch größere Datein konnten ohne Probleme ausgetauscht werden.

Wokwi – eine all in one Simulation von Microcontroller plus weitere Hardware

Wenn man ein bisschen mit Microcontrollern wie dem esp8266, esp32 oder Arduino herumspielt, dann kommt man schnell an den Punkt, dass das ausprobieren neuer Schaltungen und Komponenten relativ zeitaufwendig und auch frustrierend werden kann. Eine Abhilfe schafft hier wokwi. Die Software simuliert microcontroller, LEDs/Displays/Servos/Sensoren usw. Und man kann gleichzeitig seinen eigenen Code auf der simulierten Hardware laufen lassen. Das ganze geht sowohl nativ mit c Code als auch mit Micropython.

Beispiel: ein esp32 mit dht22 Temperatur Sensor, der mittels micropython die Werte an mqtt schickt:

https://wokwi.com/projects/322577683855704658

Wie man Neodymium Magneten aus Festplatten von ihrer Basis befreit

Durch meine Elektronik-Spielereien schaue ich auch immer in alten Geräten, was man so an Ersatzteilen gewinnen kann. Bei alten Festplatten sind das zb extrem starke Neodymium Magneten. Das Problem: sie sind in der Regel extrem fest auf ein Stück Metall geklebt. Und mit diesem Video habe ich eine relativ einfache Lösung gefunden, wie man sie abbekommt:

Wie man einen eigenen Collabora Server für Nextcloud in Docker und hinter einem Nginx Reverse Proxy korrekt startet

Um innerhalb von Nextcloud so eine Art Google-Docs betreiben zu können, benötigt man zum einen die Nextcloud-Office-App, aber auch einen extra Server (Collabora), der quasi als Backend für das Office dient. Letztendlich handelt es sich da um eine Headless Open-Office Instanz, wenn ich das richtig verstanden habe.

Im besten Fall betreibt man Nextcloud ohnehin auf dem eigenen Server innerhalb von Docker, dann sollte die Kommunikation mit dem Collabora Server kein Problem sein.

Da in meinem Beispiel die Nextcloud Instanz von Hetzner gehosted wird, war es notwendig, dass der Collabora Server über das Internet kontaktiert wird und eben nicht im gleichen Netzwerk hängt. Und da ist die Dokumentation etwas schwammig bzw. einfach falsch. Denn in der Dokumentation, die man so im Netz finden kann, wird gesagt, dass man eine Environment-Variable namens “domain” definieren muss, die dann ein Regex der Nextcloud Domain enthält (damit nur Requests von dieser Domain bearbeitet werden).

Der entsprechende Code wurde aber geändert, sodass mehrere Nextcloud Server auf Collabora zugreifen können. Statt “domain” muss die Environmentvariable “aliasgroup1” heißen und sie enthält die komplette Domain inkl Portangabe (Durch Komma getrennt kann man mehrere URLs übergeben).

Beispiel:

aliasgroup1=https://meine.domain.com:443

In meinem konkreten Beispiel habe ich die Konfiguration, dass auf dem Server für Colabora Docker mit dem allseits bekannten Lets Encrypt Nginx Companion läuft. Das nette an diesem Setup ist, dass man allen weiteren Docker Containern auf diesem Server nur drei Env-Variablen mitgeben muss und schon erstellt und verwaltet das System automatisiert die Lets Encrypt Zertifikate.

In dieser Kombination ist es wichtig, Collabora mitzuteilen, dass es sich nicht um SSL kümmern soll, was man mit der Env-Variable

extra_params=--o:ssl.enable=false --o:ssl.termination=true

erreicht. Ausserdem muss der Collabora Container im privileged Modus laufen!

Mein Setup in Ansible sieht dann letztenlich so aus:

- docker_container:
    name: collabora
    image: collabora/code
    state: started
    detach: yes
    restart_policy: always
    privileged: yes
    expose:
      - 9980
    env:
      aliasgroup1: https://nextcloud.domain.com:443
      VIRTUAL_HOST: office.domain.com
      LETSENCRYPT_HOST: office.domain.com
      VIRTUAL_PORT: "9980"
      LETSENCRYPT_EMAIL: ich@meins.de
      extra_params: "--o:ssl.enable=false --o:ssl.termination=true"

Das Equivalent auf der Console sollte dann so aussehen:

docker run --name=collabora --env=aliasgroup1=https://nextcloud.domain.com:443 --env=VIRTUAL_HOST=office.domain.com --env=LETSENCRYPT_HOST=office.domain.com --env=VIRTUAL_PORT=9980 --env=LETSENCRYPT_EMAIL=ich@meins.de --env='extra_params=--o:ssl.enable=false --o:ssl.termination=true' --privileged --expose=9980 --restart=always --detach=true collabora

Wenn das erledigt ist und der Server läuft, kann man in Nextcloud den Admin Bereich öffnen und dort auf den Punkt “Office” gehen. Hier stellt man den Collabora Server ein und klickt auf Speichern:

Anschließend geht man in Nextcloud auf den “Dateien” Tab und klickt dann oben auf das Plus, um eine neue Datei zu erzeugen:

Anschließend öffnet man das Dokument und sollte nun die Office Oberfläche sehen. Falls eine Fehlermeldung kommt, und man es “später nochmal versuchen soll”, geht auf den Server wo Collabora läuft und schaut in die Logs des Containers.

Wenn ihr da irgendwas von

Terminating connection. Error: No acceptable WOPI hosts found matching the target host [nextcloud.domain.com] in config.

seht, dann habt ihr die Domain in der Environment Variable nicht korrekt übergeben. Überprüft vor allem, ob es sich überhaupt um die gleiche Domain wie in der Fehlermeldung handelt. Diese ist in jedem Fall die korrekte Domain und sollte so auch in der “aliasgroup1” drin stehen!

[Quicktip] Geteilte Fotoalben erscheinen nach iPhone-Wechsel als leer

Nachdem ich auf ein neues iPhone gewechselt bin, sind in meiner Fotos App ein paar meiner geteilten Fotoalben einfach leer geblieben. Ich glaube sogar alle waren auch von mir erstellt worden. Alles, was man sehen konnte, war das “Plus Zeichen” zum Hinzufügen von neuen Bildern, so als ob man das Album gerade erst erstellt hat. Auf allen meinen anderen Geräten mit dem gleichen iCloud Account waren sie weiterhin da.

Um das Problem (hoffentlich) zu beheben, kann man folgendes versuchen:

  • Fotos App beenden (richtig beenden, mittels Task Manager!)
  • Iphone Homescreen –> Einstellungen –> oben auf euren Namen tippen –> iCloud –> Fotos
  • “Geteilte Alben” ausschalten
  • auf den Homescreen wechseln
  • eine Minute warten
  • Iphone Homescreen –> Einstellungen –> oben auf euren Namen tippen –> iCloud –> Fotos
  • “Geteilte Alben” wieder einschalten
  • Fotos App öffnen

Die geteilten Alben sollten nun zeitnah erscheinen und zumindest bei mir waren dann auch alle wieder mit Bildern gefüllt.

MacOS Update Probleme auf Monterey mit APFS

Zu meiner Schande muss ich gestehen, dass mein privates Macbook irgendwann auf MacOS Mojave hängen geblieben ist. Ich hatte vor ein paar Monaten mal ein Upgrade auf Big Sur probiert, allerdings war der Rechner danach unbrauchbar, weil er nicht mehr gebootet hat. Nach einem ca. 8h oder so andauernden Recovery mittels Timemachine war er wieder einsatzbereit, aber mir war die Lust auf Updates vergangen.

Dieses Problem wollte ich nun endgültig mit dem Umstieg auf MacOS Monterey angehen und lösen. Also habe ich den Installer runtergeladen und wollte die Installation starten – als ich direkt die Fehlermeldung bekam, dass für die Installation ein Datenträger mit APFS notwending ist.
APFS ist ein alternatives Dateisystem, welches von Apple mit MacOS High Sierra verteilt wurde. Eigentlich sollte in Mojave der Datenträger automatisch dahin konvertiert werden (was sogar on-the-fly) gehen sollte, aber irgendwie ist das bei mir nicht passiert. Warum auch immer.

Um die Conversion nachträglich machen zu können, muss man den Mac im Recovery Modus starten, dann das Disk-Utility/Festplattendienstprogramm starten. Dort wirft man die Mac Festplatte aus, markiert sie und klickt dann auf Edit/Bearbeiten und “convert to apfs”. Anschließend wird das System umgestellt. Blöd nur, dass genau das den Fehler verursacht, dass man anschließend nicht mehr booten kann (Stichwort: “Running bless to place boot files failed”). Man könnte MacOS nach der Konvertierung nochmal drüber installieren, aber da Mojave nicht mehr installiert werden kann ging auch das nicht mehr für mich 😂

Die Lösung habe ich dann hier gefunden: https://www.tecklyfe.com/boot-failures-after-converting-macos-ssd-to-apfs/

Ich vermute mal dass das Problem irgendwie mit UEFI oder so zusammen hängt, jedenfalls muss man ein Pre-Boot Environment von Hand anlegen, damit der Bootvorgang wieder reibungslos funktioniert. Und das geht so:

  • Den Mac mit gedrückter Command+R Tastenkombi starten
  • Utilities > Terminal
  • diskutil apfs list ausführen und schauen, welche Nummer und welche UUID die Festplatte eures Macs hat – sie sollte “Macintosh HD” oder so heißen
  • Anschließend (natürlich ohne die eckige Klammer, als z.B. disk2:
    diskutil apfs addVolume disk[Festplatten-NUMMER] apfs Preboot -role B
  • Anschließend dieses Verzeichnis anlegen, wobei die xxx… durch die UUID, die ihr euch in Punkt 3 angesehen habt hier eintragt:
    mkdir -p /Volumes/Preboot/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/System/Library/CoreServices
  • cp -RP /Volumes/MACINTOSH HD/System/Library/CoreServices /Volumes/Preboot/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/System/Library/CoreServices
  • Mittels folgendem Commands repariert ihr nun den Pre-Boot-Eintrag. Ersetz hier das disk2s1, falls deine Partition eine andere Nummer hat:
    diskutil apfs updatepreboot disk2s1
  • Abschließend kannst du nun mit Bless den Boot-Eintrag setzen lassen:
    bless --folder /Volumes/MACINTOSH HD/System/Library/CoreServices --bootefi --verbose
  • Neu starten

[Quicktip] Mac OS ignoriert die Reihenfolge von Netzwerkanschlüssen und benutzt immer nur Wifi

Meine beiden Macbooks – eins mit Mojave und eins mit Big Sur – haben an meinem Schreibtisch das gleiche Problem: Wifi/Wlan wird immer bevorzugt verwendet. Ich merke das mit spontanen Aussetzern in Videokonferenzen, oder wenn ich im Netzwerk nicht mit dem gewohnten Gigabit Speed Kopieraktionen machen kann. Meine Vermutung ist, dass es mit meinem verwendeten USB-C Hub mit Netzwerkanschluss zu tun hat.

Normalerweise hat man in Mac OS die Möglichkeit, die Priorität für Netzwerkanschlüsse festzulegen und damit eigentlich genau mein Problem zu lösen:

Wie man sehen kann ist das WLAN unterhalb vom Thunderbold und eben meinem USB Gigabit Adapter angesiedelt. Und trotzdem wird es immer wieder bevorzugt verwendet.

Ich könnte das Wlan natürlich einfach abschalten, was ich auch getan habe, aber dann kann ich das Macbook nicht mehr mittels Apple Watch entsperren und Airdrop funktioniert auch nicht.

Nun bin ich auf die Idee gekommen, mit den Netzwerkumgebungen zu spielen, und siehe da, man kann sie zumindest als Workaround nutzen. Die Netzwerkumgebungen unter Mac OS sind eine Möglichkeit, dass man verschiedene IP Settings in seinem Mac speichern kann, sodass man z.B. schnell zwischen den Settings für die Arbeit und daheim wechseln kann.

Ich habe mir also zwei Netzwerkumgebungen “Schreibtisch” und “Unterwegs” angelegt. In der für mich relevanten Gruppe “Schreibtisch” belasse ich das Ethernet wie es ist. Schlaue Leute würden nun einfach den WLAN Apapter in der “Schreibtisch” Umgebung rauswerfen, allerdings deaktiviert sich dann das WLAN komplett und ich kann eben nicht meine Apple Watch zum Entsperren und Airdrop nutzen.

Also habe ich den Adapter drin gelassen, aber bei den IP Einstellungen für das Wlan folgendes gemacht:

Diese Einstellungen bewirken, dass der Mac sich zwar zu dem Wlan verbindet oder es zumindest versucht, allerdings weder eine IPv4 noch IPv6 bekommt. Aber: die Wlan Funktion bleibt aktiviert. Mit diesem “Hack” wird dauerhaft nur die Ethernet-Verbindung verwendet, aber ich habe trotzdem die Vorzüge von Airdrop und Apple-Watch Entsperrung 👌

Übrigens: sobald man Netzwerkumgebungen angelegt hat, kann man diese über einen neuen Menüpunkt im “Apfelmenü” schnell umschalten:

 

Jetzt muss ich nur noch etwas Zeit finden, damit ich per Automator-Script erkenne, ob der Ethernet-Adapter verwendet wird oder nicht und dementsprechend die Umgebung automatisch umschalte…