Container Updates


CEW4

Member
Hallo,


ich habe das Konzept von Containern (vs. VMs) noch nicht so recht verstanden, speziell die Methoden, sowas zu aktuell zu halten:


Erstens:

Offenbar sind Container prinzipbedingt nicht darauf ausgelegt, geupdatet zu werden. Statt dessen soll man den alten Container verwerfen und anhand eines neuen Base-Images - welches die Updates der installierten Programm enthält - einen neuen Container generieren. (Ich hoffe doch, daß man das wenigstens irgendwie automatisieren kann.)

Aber bedeutet das nicht, daß der Container laufend (alle paar Tage) mindestens gestoppt und neu gestartet werden muß?


Zweitens:

Wenn der Container selbst ein Wegwerf-Artikel ist, dann müssen die verwendeten Daten (Konfigurationsdateien, Datenbanken etc.) ja unabhängig davon abgelegt sein. Wie geht das? Mit einer Art zweiten (virtuellen) Festplatte, welche die sich ändernden Daten enthält und die an den richtigen Stellen im Container-Filesystem eingehängt ist? Aber wie finde ich heraus, wo eine Anwendung überall hinschreibt? Die eine Anwendung legt ihre Daten nach /etc, die nächste nach /opt, wieder andere nach /usr oder /var... Wie soll man darüber die Übersicht behalten und all das korrekt auf die persistente Platte verlinken? Zumal es sich nur teilweise um ganze Verzeichnisse, sondern manchmal auch nur um einzelne Dateien "irgendwo" handelt?


MfG
 
Last edited:
Zu 1.: ja, das stimmt. Man updated nicht die Anwendung, sondern den Container und spawnt diesen neu. Ja, man kann diesen Prozess automatisieren, hierzu empfiehlt sich https://containrrr.dev/watchtower/ , ggfalls kombiniert mit einem Messenger Container, z.B. https://hub.docker.com/r/poma/docker-telegram-notifier .
Manche Container werden öfter aktualisiert, manche nicht so häufig. Sieh es so: mit der Updateautomatisierung bist Du deutlich aktueller als Du das mit manuellen Updates jemals sein könntest, oftmals ohne einen Finger zu rühren. Ein Neustart eines Containers ist doch für die Anwendung kein Problem, bzw. sollte es nie sein.

Im Endeffekt braucht ein Container immer das gesamte "Kochrezept", bis die Applikation läuft. Das kann manchmal einfacher und manchmal hochkomplex sein.

Container können auf verschiedene Arten erstellt werden, auf jeden Fall sollte man sich mit den Kommandozeilenbefehlen vertraut machen. "docker run" ist zwar vermeintlich einfach, bei komplexeren Containern sind aber "docker-compose" Files sinnvoller, da man hier deutlich komplexere Systeme quasi in einem Konfigurationsfile zusammenbauen und dann mit einem einfachen Befehl starten kann.

zu 2.: Ja, Container sind Wegwerf Artikel. Ja, die Daten müssen unabhängig davon abgelegt werden. Hierfür können entweder Ordner im Container auf Ordner im Host FS gemappt werden oder es werden Docker "volumes" angelegt, d.h. in etwa das was Du unter einer "virtuellen Festplatte" verstehst. Hierfür ist der "mount" Befehl bei Docker verantwortlich.
Du selbst brauchst in der Regel nicht herausfinden, wo welche mounts benötigt werden, sondern wenn Du einen fertigen Container verwendest, wirst Du das im Docker Hub oder Github beschrieben finden. Man kann Ordner oder auch nur einzelne Files in den Container mounten.
Ansonsten verwendet man eigentlich für nahezu jeden Container ein Basisimage (z.B. ein Debian, Ubuntu oder auch ganz klein ein Alpine Linux) und baut dann darauf auf, d.h. installiert dann die Anwendung von da ausgehend. Die Daten vieler Anwendungen sind ausschließlich in Datenbanken, d.h. die DB muss dann persistent rausgelegt werden. Bei anderen gibt es auch lokale Files im Filesystem, dann werden eben diese Ordner/Dateien rausgelegt.
Volumes verwaltet Docker selbst und legt die immer in einem Ordner ab, das Rausmounten ins Filesystem hat man selbst unter Kontrolle, hier macht es natürlich Sinn, alles in einer sinnvollen Hierarchie abzulegen. Wirklich kein Problem.

Meine Standard-Umgebung besteht übrigens aus:
- https://www.portainer.io/
- https://nginxproxymanager.com/ (NPM)
- https://containrrr.dev/watchtower/
- https://hub.docker.com/r/poma/docker-telegram-notifier

Damit hat man eine gute Basis beisammen und insbesondere der NPM kann einem die lästige Arbeit der Zuordnung eines DNS Records (anwendung.domain.tld), des Portmappings (Port 8234 (oder welcher Port auch immer) -> 443 (SSL)) und der Absicherung durch Let's Encrypt Zertifikate abnehmen.
 
... dann lag ich ja so ungefähr richtig, vielen Dank schon einmal.

Es fällt mir immer noch schwer, mir das in der Praxis vorzustellen. Wenn wir als ganz einfaches Beispiel mal einen MQTT-Broker hernehmen:


Erstens, Betrieb:

Ich erstelle also ein Template, in dem folgendes definiere:

Kernel = ubuntu (z.B.)
zusätzliches Pkaet = mosquitto (plus ggf. Abhängigkeiten)
include volume, mount the following:
/etc/mosquitto (Verzeichnis, rekursiv)
/var/log/mosquitto (Verzeichnis, nicht rekursiv)
/etc/systemd/system/multi-user.target.wants/mosquitto.service (File)

Die Pakete (Kernel ebenso wie mosquitto und seine Abhängigkeiten) sollten über die Verwaltungs-Infrastruktur (darüber will ich im Detail noch gar nicht nachdenken; wird dann schon irgendwie gehen) bei jedem Erzeugen des Containers automatisch aktuell bezogen werden können.

Stimmt das im Prinzip?


Zweitens, Update:

Aber jetzt muß ich doch diesen Container regelmäßig stoppen und neu erzeugen (wahrscheinlich automatisch, z.B. nachts). Damit startet auch der Broker immer wieder von neuem, und ist währenddessen natürlich immer für gewisse Zeit off-line. Je nach dem wie schnell dieser Vorgang geht (mit was muß man denn da rechnen? Sind das Sekunden oder Minuten?) kann man nur hoffen, daß währenddessen nichts passiert, wobei er vermisst wird. Für einen MQTT-Broker mag das sogar noch gehen (da gibt es retries), für einen Syslog-Daemon wäre es nichts.

Im Gegensatz dazu eine VM: Das apt-get startet nur die Dienste neu, die gerade aktualisiert wurden, und das ohne neues booten der ganzen VM. Diese Ausfallzeit ist für einen Client kaum wahrzunehmen.


Sind das ganz einfach die Nachteile, die man für die niedrige Resourcenlast und das unkomplizierte Redeployment (wenn ich den Container sowieso immer wieder neu generiere, kann ich das morgen auch auf einer anderen Maschine tun als gestern) in Kauf nimmt?


MfG
 
Zu 1.:
Öhm nein, stimmt so nicht wirklich oder ich versteh was falsch. Schau Dir doch einfach mal einige Container an und guck Dir die Doku durch:

https://docs.docker.com/get-started/https://hub.docker.com/
dann verstehst Du das Prinzip schnell. Für fast jede Anwendung gibt es bereits fertige Container im Docker Hub, die man ggfalls anpasst und in seine Umgebung einpasst. Daher musst Du Dir das Template gar nicht selbst komplett schreiben, sondern eigentlich nur ein vorhandenes Template nehmen und anwenden.

Das Template selbst bauen ist der schwierigere Weg:
(idealerweise packt man das folgende in ein öffentliches oder privates Docker Repository, damit mans mit Docker ziehen kann)
Im Prinzip macht man für eine Anwendung, die als Umgebung Ubuntu haben soll:
im "Dockerfile":
- Nimm "https://hub.docker.com/_/ubuntu" als Basis
- Installier die Anwendung mit diesem Vorgang (dann fügt man alle Befehle ein, die bis zu einer lauffähigen Anwendung nötig sind)

Dann nimmt man darauf im docker run Befehl oder eben mit einem docker-compose.yml Bezug darauf.

DEUTLICH einfacher ist es aber, gleich die Anwendung als Basis zu nehmen, bei Mosquitto also https://hub.docker.com/_/eclipse-mosquitto

Dann hast Du nämlich schlicht mit
Code:
docker run -it -p 1883:1883 -p 9001:9001 -v mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto

ein lauffähiges Mosquitto installiert. Als mosquitto-compose.yml könnte das so ähnlich aussehen: https://github.com/vvatelot/mosquitto-docker-compose/blob/master/docker-compose.yaml . Ggfalls würde es auch gleich sinn machen, diesen Container mit allem was sonst noch zum Setup gehört zusammenzupacken.

Zu 2. Das dauert Sekunden. Es wird nur der Container neu gespawned, wenn auch wirklich ein Update da ist, nicht "jede Nacht". Mit Watchtower kann man einstellen, zu welcher Zeit die Container aktualisiert werden. Probiers aus, ist eine Non-Issue.
 
Ahja - an fertige Container hatte ich gar nicht gedacht. Das wäre natürlich auch eine Möglichkeit; das muß ich mir nochmal in Ruhe vornehmen.

Danke für die Erläuterungen, hat mir geholfen! :)

MfG
 
Ahja - an fertige Container hatte ich gar nicht gedacht. Das wäre natürlich auch eine Möglichkeit; das muß ich mir nochmal in Ruhe vornehmen.
Jo, das ist eigentlich der Standardmodus wie Docker meistens verwendet wird. Man bewege sich möglichst weit zum Ziel hin und passe den Rest an. Die Arbeit wird hier halt von der Anwendungsinstallation selbst zur Konfiguration der Dockerumgebung verschoben.

Der Vorteil dabei: hat mans einmal, kann mans überall und beliebig oft mit dem gleichen Setup spawnen. Clusterbetrieb etc. liegt dann nicht mehr fern. Und ob der andere Container nun auf dem gleichen oder einem anderen Host ist, ist dank Overlay Network nicht wichtig.
Hier kann man mit Kubernetes wohl noch deutlich weiter gehen, aber das habe ich bisher (noch) nicht gebraucht.

Danke für die Erläuterungen, hat mir geholfen! :)
Freut mich. Ich habe das Dockerprinzip auch erst null verstanden und fands sehr umständlich. Aber eigentlich ists sehr sehr praktisch. Wenn man mal dahintergestiegen ist. Und dann macht man nix anderes mehr.

Ergänzung zu oben: https://hub.docker.com/r/linuxserver/duplicati gehört auch noch zu meinem Stack.
 
Ahja - an fertige Container hatte ich gar nicht gedacht. Das wäre natürlich auch eine Möglichkeit; das muß ich mir nochmal in Ruhe vornehmen.
Die wichtigste Grundregel der Container-Architekturen ist die gute alte UNIX Philosophie; "Keep it simple" und "Make each program do one thing well". Viele Online-Tutorials versuchen komplexe Basis-Images oder gar selbstinstallierte Container. Das mag funktionieren, entspricht aber nicht der Ideologie von Container. Wenn eine Sicherheitslücke/ein Bug in Mysql zutage kommt, willst du nicht deinen ganzen Webserver-Stack neu bauen und deployen (und ggf testen) müssen.

Veranschaulichung anhand eines einfachen Webserver mit Python3 und Flask und Mysql-Datenbank.
- Image docker.io/bitnami/mariadb:10.6
- Image docker.io/bitnami/nginx:latest
- Dockerfile mit python:3.9-alpine und einer pip-requirements Datei , startet einen gunicorn WSGI Server
Ich muss also im Beispiel lediglich meine Arbeitsumgebung (Python-Container) aktualisieren, Datenbank und Webserver brauchen lediglich eine initiale Konfiguration und manchmal einen Redeploy. Die anderen beiden Images werden keineswegs angefasst und erhalten nur Volume-Mounts für Config/Zertifikate (Readonly) sowie Datenspeicher.

Das Template selbst bauen ist der schwierigere Weg:
(idealerweise packt man das folgende in ein öffentliches oder privates Docker Repository, damit mans mit Docker ziehen kann)
Docker, Podman und die üblichen Kubernetes-Distros bringen allesamt eine eigene Image-Registry mit in welche man pushen und taggen kann.
Generell würde ich immer empfehlen, immer diese lokale Registry zumindest als Cache zu verwenden um zu verhindern dass bei einer Störung von Dockerhub oder sonstiger Ursache (Löschung, ...) deine Programme plötzlich nicht mehr hochkommen.

e nach dem wie schnell dieser Vorgang geht (mit was muß man denn da rechnen? Sind das Sekunden oder Minuten?)
Das hängt natürlich von deiner Applikation ab, bestenfalls hast du Aktiv-Aktiv Clustering) und damit keinen single-point-of-failure was Restarts vereinfacht. Wichtig ist dass das Container-Image oft nicht automatisch in deine lokale Registry preloaded ist so dass entweder dies manuell erfolgen muss oder die Container-Umgebung dies verwalten muss um zu verhindern dass du ggf minutenlang mal neue Container runterlädst wenn die Applikation bereits gestoppt ist.
Dies ist einfacher als es klingt, bei docker-compose wäre das Deploy-Skript ein Einzeiler: "docker-compose pull && docker-compose up -d --build --remove-orphans && docker image prune -f"


Das apt-get startet nur die Dienste neu, die gerade aktualisiert wurden, und das ohne neues booten der ganzen VM. Diese Ausfallzeit ist für einen Client kaum wahrzunehmen.
Ein Image ist keine volle VM sondern nur das Programm und seine Laufzeitumgebung. Klar, es gibt die ganzen Distro-Logik wie yum/apt/... und zugehörigen Strukturen aber diese spielen keinerlei Rolle. Der Container führt beim Start nur genau dein Entrypoint aus welches in aller Regel der Startbefehl der Applikation ist. Damit ist der Start eines Containers eigentlich nur die reale Startzeit der Applikation.
Kleine Anekdote; ich hatte mal versucht auf Kubernetes einen nach aussen präsentierten Datenbank-Cluster zu konfigurieren, mit Master-Slave Replikation, Quorum Container, ... und allem rundherum. Es hat sich rausgestellt dass Kubernetes und der Start eines einzelnen Redis-Servers um mehrere Grössenordnungen schneller sind als das ganze applikative Clustering; und zwar 2Sek vs 30Sek.
 
Back
Top