Webserver mit Docker Container umziehen, Theorie und Praxis
Wer einen virtuellen Server oder Cloud-Server mietet, muss diesen für ein neues Betriebssystem von Zeit zu Zeit austauschen. Spätestens, wenn für das Betriebssystem keine Updates mehr zur Verfügung gestellt werden, ist ein Serverwechsel unausweichlich. Vor einiger Zeit habe ich alle Websites auf Docker-Container umgestellt, was den Umzug beschleunigen soll. Zunächst etwas Theorie zu meinen Überlegungen, dann wie mein Serverumzug zuletzt vonstattenging. Zusätzlich habe ich zu meinem letzten Serverumzug ein Video erstellt, siehe YouTube-Video. Wer die Theorie und meine Überlegungen überspringen will, kann direkt zum Bereich Praxis meines letzten Serverumzugs wechseln.
Warum Docker?
Ursprünglich brachte ein neuer Server auch eine neue PHP und MySQL-Version mit sich und nicht selten musste ich bei einem Serverwechsel so manches an den Websites und den Servereinstellungen nachbessern, damit diese wieder ordentlich funktionierten. Mit Docker bleiben diese Überraschungen aus, da sich die Container auf allen Hosts identisch verhalten. Die Container können unabhängig vom Betriebssystem gepatcht werden. Zudem kann mit Docker sehr einfach ein Reverse Proxy für das Hinzufügen von https SSL Zertifikaten verwendet werden, siehe: sichere https Verbindung: Traefik Reverse Proxy + Let´s Encrypt.
Für den Serverwechsel ergeben sich, auch mit Docker, zwei Herausforderungen:
- das Übertragen der Daten von einem Server zum anderen
- das Ändern des Zugriffs auf den neuen Server
Docker Images oder die eigentlichen Container übertragen: muss in meinem Fall nicht sein.
Ich habe für jede Website ein docker-compose.yml -File erstellt. Die Files beinhalten alle Informationen für das Herunterladen der notwendigen Docker-Images und das Erstellen der Container. Für das Übersiedeln der Webservices muss ich die Images oder Container also nicht übertragen, da diese über die docker-compose.yml-Files neu erstellt werden können. Anders bei den Container-Ordnern oder Volumes, diese beinhalten alle persistenten Daten meiner Container und spiegeln dessen Datenstand wider.
rsync und Bind-Mounts
Wer keine Volumes, sondern Bind-Mounts verwendet, kann die Ordner am Host-Dateisystem ablegen und mit dessen Tools einsehen und verwalten. Für Linux-basierte Betriebssysteme können dessen Ordner mit rsync sehr einfach und schnell auf einen anderen Host übertragen werden. Mit den richten Parametern überträgt rsync beim ersten Aufruf alle Daten und bei weiteren Aufrufen nur noch die geänderten Daten, was die Zeit erheblich minimiert. Bei meinem letzten Server konnte ich damit ca. 20GByte an Daten in unter einer Minute abgleichen.
Hier ein Beispiel um Ordner mittels rsync zu kopieren:
sudo rsync -rltD --delete -e ssh root@IPalterServer:/var/mydockerfolders/ /var/mydockerfolders
Entsprechende Berechtigungen vorausgesetzt, können Volumes aus dem Ordner "/var/lib/docker" wahrscheinlich auch problemlos mit rsync übertragen werden. Ich muss allerdings zugeben, dass ich dies noch nicht getestet habe. Da /var/lib/docker von Docker verwaltet wird, ist der direkte Zugriff darauf nicht der richtige Weg.
Alternative: Shared Storage
Eine Alternative zum Übertragen der Daten wäre der zentrale Zugriff auf eine Storage oder NAS. Wer beispielsweise einen zentralen Netzwerkspeicher, wie NFS, einsetzt und die Daten der Container darauf ablegt, benötigt für den neuen Server einzig einen Zugriff auf das Filesystem. Die Container könnten somit am alten Server gestoppt und einfach am neuen Server erstellt und gestartet werden.
GlusterFS
Bei meiner Recherche nach einer alternativen Variante zu einer Shared Storage bin ich auch kurz bei GlusterFS hängen geblieben. GlusterFS kann bestimmte Ordner permanent und synchron zwischen Hosts über das Netzwerk replizieren und bildet damit sowas wie eine verteilte Shared Storage.
Vorsicht bei Datenbanken: nicht im laufenden Betrieb kopieren
mysqldump
Datenbankfiles sollten nicht einfach im laufenden Betrieb kopiert werden, da die Kopie unter Umständen nicht konsistent ist, siehe: Docker Backup
Docker Container stoppen, dann erst kopieren
Eine sehr universelle Variante um alle Daten konsistent zu übertragen, ist das Stoppen der Container am Quellserver und das Kopieren in ausgeschaltetem Zustand. Natürlich entsteht mit dieser Variante eine kurze Downtime. Für das Übersiedeln von Websites könnte diese Downtime eventuell in Kauf genommen werden, da die Services am alten Server für den Switch auf den neuen Server eventuell ohnehin kurz gestoppt werden müssen. Für ein Backup ist diese Variante weniger geeignet, da dieses regelmäßig stattfinden soll: Es muss ja nicht jeder mitbekommen, dass die Website gerade gesichert wird.
Ändern des Zugriffs auf den Server
Für das Ändern des Zugriffs vom alten auf den neuen Server gibt es mehrere Möglichkeiten:
DNS-Änderung
Die wohl einfachste Variante ist das Ändern der DNS-Einträge auf die IP-Adresse des neuen Servers:
Siehe auch: Datenverkehr auf einen anderen Server weiterleiten. Die nächsten beiden Varianten habe ich selbst nicht im Einsatz, der Vollständigkeit halber sollten sie dennoch nicht fehlen:
IP-Adresse auf den neuen Server anwenden
Bestimmte Provider bieten eine Möglichkeit die IP-Adresse von einem Server auf den anderen zu ändern: Floating IP.
Alternativ kann eine IP je nach Provider eventuell auf einen neuen Server übertragen werden:
Load Balancer vorschalten
Wird ein Load Balancer eines Providers eingesetzt, verteilt dieser die Zugriffe auf verschiedene Server. Da der Zugriff auf den Load Balancer stattfindet, bleibt dessen IP-Adresse beim Austausch der Server bestehen.
Docker Swarm
Bevor ich dieses Mal meinen Webserver übersiedelt habe, habe ich Docker Swarm getestet. Mit Docker Swarm werden die Container über eine gemeinsaem Netzwerkverbindung auf verschiedene Hosts verteilt. Zum Beispiel könnte ein Webcontainer auf einem Host laufen, dessen Datenbank-Container auf einem anderen. Docker Swarm löst das Problem mit dem Zugriff, da alle Container über alle Hosts eines gemeinsamen Swarms erreichbar sind. Docker Swarm kümmert sich dabei nur um das Netzwerk, nicht aber um die verwendeten Volumes. Aus diesem Grund kann Swarm für Container eingesetzt werden die entweder keine persistenten Daten speichern müssen, oder in Kombination mit einem zentralen Netzwerkspeicher: Shared Storage, NAS oder verteilter zentraler Speicher, wie GlusterFS. Da ich bisher mit einem virtuellen Server auskomme, würde Docker-Swarm in meinem Fall mehr Overhead produzieren als Nutzen, auch wenn das gemeinsame Netzwerk beim Übersiedeln des Servers durchaus hilfreich sein könnte. Auf der Suche nach einer alternativen Möglichkeit um Zugriffe von einem Server auf den anderen zu leiten bin mit Traefik fündig geworden, siehe: Traefik: Datenverkehr auf einen anderen Server weiterleiten. Für das Übertragen der Daten habe ich einfach den rsync-Befehl eingesetzt.
Siehe: Docker Compose vs. Docker Swarm: verwenden und verstehen
Weiterleitung des Datenverkehrs
Eine weite Möglichkeit den Zugriff auf einen anderen Server zu ändern, ist das Weiterleiten sämtlicher Anfragen auf den neuen Server, siehe: Traefik: Verkehr an einen anderen Server weiterleiten. Um die Zeit für den Serverwechsel möglichst gering zu halten, habe ich bei meinem letzten Serverumzug den Datenverkehr weitergeleitet, bis die DNS-Änderung überall bekannt war.
In der Praxis: mein letzter Serverumzug
Mein einfaches Setup: Ordner (Bind-Mounts) und keine Volumes
Bei meinem Setup habe ich den Ordner /var/web am Server angelegt und darunter die einzelnen Webservices in eigenen Unterordnern.
Als Beispiel:
- /var/web/libe.net und
- /var/web/script-example.com
Im Root-Ordner der Webservices befindet sich das jeweilige docker-compose-File der jeweiligen Site und darunter die Ordner mit den persistenten Daten:
- /var/web/libe.net/docker-compose.yml
- /var/web/libe.net/www ... Ordner mit den Daten der Website
- /var/web/libe.net/db .. Ordner mit den Datenbank-Files
Mit dieser Ordnerstruktur befinden sich alle relevanten Daten zentral unterhalb des Ordners /var/web. Da /var/web der einzige schützenswerte Ordner auf dem Server ist, reicht es diesen zu backupen. Aber auch beim Übersiedeln des Servers reicht es alle Container zu stoppen und einzig den Ordner /var/web auf den neuen Server zu übertragen und dort mit docker-compose wieder alle Services zu starten. Den Zugriff vom Internet auf die einzelnen Websites steuert der Traefik Reverse Proxy.
Die Downtime möglichst gering halten
Bereits meine ursprüngliche Idee für den Datentransfer war, den Inhalt aller Container-Ordner einfach mit dem Befehl rsync vom alten Server auf den neuen zu kopieren und die Container im Anschluss am neuen Server zu starten. Mit dieser Variante könnte ich ohne Downtime alle Websites am neuen Server parallel starten und im Anschluss den DNS auf den neuen Server ändern. Die Vorgehensweise hat aber Nachteile: Erstens ist das einfache Kopieren von Datenbanken im laufenden Betrieb etwas problematisch, siehe: mysql. Zweitens gibt es, solange der DNS nicht überall aktiv ist, eine gewisse Übergangszeit, in der die Anfragen auf beiden Servern landen würden.
Auf Kosten der Downtime, habe ich diesmal alle Container gestoppt, dann die Services übertragen und im Anschluss alle Zugriffe des alten Webservers auf den neuen weitergeleitet.
Ich habe mir folgende Vorgehensweise für den Umzug überlegt:
Bestehender Server | neuer Server |
---|---|
1) alle Docker-Container stoppen | 3) alle Container stoppen |
2) TCP-Umleitung starten | 4) alle geänderten Daten kopieren |
5) alle Container starten |
Um am Quellserver alle Container zu stoppen, kann folgender Befehl verwendet werden:
docker kill $(docker ps -q)
Das Weiterleiten aller Anfragen kann mit folgendem Setup erfolgen: Traefik: Datenverkehr auf einen anderen Server weiterleiten.
Zuletzt habe ich am Zielserver sicherheitshalber alle Container gestoppt, die letzten Änderungen der Ordner mit rsync übertragen und dann alle Container gestartet. Damit die Downtime möglichst gering gehalten werden kann, hab ich die Befehle in eine Bash-Datei geschrieben:
File: target-actions.sh
#!/bin/bash
docker kill $(docker ps -q)
rsync -rltD --delete -e ssh root@IPalterServer:/var/web/ /var/web
cd /var/web/traefik
docker compose up -d
cd /var/web/website1
docker-compose -d
cd /var/web/website2
docker-compose -d
Start der Bash-Datei:
chmod +x target-actions.sh
. /target-actions.sh
Nach dem Ausführen der Bash-Datei laufen die Container am neuen Server. Damit der Zugriff direkt auf den neuen Server stattfindet, kann jetzt der DNS-Eintrag auf die IP-Adresse des neuen Servers geändert werden. Wenn alle Container auf dem neuen Server laufen und über den alten Container keine relevanten Zugriffe mehr umgeleitet werden, kann der alte Server ausgeschaltet und gelöscht werden.
Video
neuer Server: meine zusätzlichen Pakete
Zusätzlich zu Docker verwende ich am Webserver eine Überwachung mit Glances und EARLYOOM.
Überwachung
Systeme überwachen: Monitoring in HomeAssistant mit Glances
Einsatz eines Webserver mit relativ wenig Arbeitsspeicher: RAM
Sollte ein Linux-Server an die Grenze des Arbeitsspeichers kommen, kann eventuell EARLYOOM diesen dennoch am Leben halten. EARLYOOM kann bestimmte Dienste stoppen, wenn der Speicher zur Neige geht: Wenn Ubuntu nicht mehr reagiert: Linux Memory Leak.
Fazit
Mit mehreren Servern und einem entsprechenden Cluster-Setup könnten die Container sicherlich noch einfacher verschoben werden, dafür sind aber auch mehr Ressourcen und ein aufwendigeres und kostenintensiveres Setup notwendig. Wer nur einen Server für seine Websites verwendet, kann bestehende Docker-Container mit einem einfachen rsync-Befehl auf einen anderen Server übertragen und muss im Anschluss nur den Zugriff darauf ändern. Rsync kann zudem als einfaches Backup verwendet werden, siehe auch: Docker Backup
{{percentage}} % positiv