# Docker verstehen



## JUUNND (4. Apr 2020)

Hallo,

ich beschäftige neuerdings mit Docker und mir sind ein paar Sachen evtl. noch nicht wirklich klar bzw. ich weiß nicht genau ob ich dies richtig verstehe. Nachdem ich es geschafft habe ein Microservice, ein Frontend (ReactJs)  und eine Datenbank (MySql) über Docker in 3 verschiedene Container laufen zu lassen, wollte ich diese Container testweise mal auf einem anderen System ausrollen. Hierfür habe ich die Container auf Docker Hub commited und auf dem anderen Host über *pull *ausgerollt. 

Das hat zumindest bei dem Microservice und dem Frontend gut funktioniert. Allerdings hat das bei der Datenbank nicht so wirklich funktioniert.Zunächst habe ich gedacht, dass alle Daten aus der Datenbank im Container vorhanden sein sollten, dem ist aber anscheinend nicht so. Kann ich die Daten irgendwie im Container integrieren ? MySql müsste ja auf dem anderen Host nicht installiert sein, da dies ja im Container bereits installiert ist, richtig ? Auf dem Originalsystem habe ich für die Erstellung der Images ein entsprechende docker-compose Datei ausgeführt. Auf dem anderen Host müsste dies ja nicht nochmal ausgeführt werden, oder ? Schließlich sollten diese Informationen doch eigentlich in dem Image (welches ich quasi kopiert habe) vorhanden sein ? 

So habe ich ein Image für die Datenbank angelegt



> db:
> image: mysql:5.7
> ports:
> - "3307:3306"
> ...



Schönes Wochenende noch


----------



## httpdigest (4. Apr 2020)

Du musst zuerst einmal unterscheiden zwischen einem Image und einem Container. Ein Docker Image ist im Großen und Ganzen nichts anderes als ein bestimmter Snapshot/Stand eines Dateisystems zum Zeitpunkt des Bauens des Images. Ein Image kann z.B. von einem Basisimage ableiten und durch ein Layered File System neue Dateien hinzufügen, Dateien löschen oder Dateien ändern. Zusätzlich kann ein Image noch Dinge tun wie etwa das Arbeitsverzeichnis festzulegen oder den per Default zu startenden Prozess mit seinem Argumenten beim Start des Containers zu definieren. 
Ein Container ist eine laufende Instanz eines Images mit zusätzlichen Umgebungsparametern wie zwischen Host und Container gemappte IP-Ports, gemappte Volumes, Umgebungsvariablen und Netzwerke. Das heißt, ein Image ist das Dateisystem, mit welchem ein Container gestartet wird und ein Container kann zusätzlich weitere Dateisysteme "mounten". Diese zusätzlichen Dateisysteme gehören aber nicht zum Image des Containers, sondern werden durch den Host persistiert.
Auf Docker Hub kann man also z.B. keine Container hochladen, sondern nur Images. Genauso wie du auf Maven Central auch keine laufenden JVM Prozesse hochladen kannst, sondern nur Jar-Dateien.
Docker Compose wiederum erlaubt dir, mehrere Container mit ihren jeweiligen Images zu definieren und z.B. in ein gemeinsames Netzwerk zu starten oder gemeinsame durch den Host bereitgestellte Dateisysteme zu mounten.
Mit Hilfe von Docker Compose selbst kannst du auch kein Image erstellen. Images werden durch Dockerfile Dateien erzeugt, die angeben, welche Dateien/Verzeichnisse beim Bauen eines Images per Docker in das Image kopiert werden sollen.
Docker Compose bietet zwar die Möglichkeit, mit "build" Docker Images zu bauen, aber dabei wird lediglich an das Dockerfile der einzelnen Services delegiert (sofern vorhanden) und es werden also für jeden Service dann entsprechend sein Docker Image gebaut.


----------



## JUUNND (4. Apr 2020)

Hallo,

vielen Dank für die Ausführung. Analog zu OOP kann ein Image als eine Klasse gesehen werden und eine Container als eine Instanz dieser Klasse !? 



> Docker Compose wiederum erlaubt dir, mehrere Container mit ihren jeweiligen Images zu definieren und z.B. in ein gemeinsames Netzwerk zu starten oder gemeinsame durch den Host bereitgestellte Dateisysteme zu mounten.
> Mit Hilfe von Docker Compose selbst kannst du auch kein Image erstellen. Images werden durch Dockerfile Dateien erzeugt, die angeben, welche



Ich habe hier mal ein Beispiel aus einem Tutorial (Quelle: https://www.callicoder.com/spring-boot-mysql-react-docker-compose-example/)



> Docker Compose file Reference (https://docs.docker.com/compose/compose-file/)
> 
> version: '3.7'
> 
> ...



Wenn ich dich richtig verstehe, dann findet im Bezug auf den Service *app-server* das Bauen des Images lediglich in diesem Teil statt. 



> build:
> context: polling-app-server # Use an image built from the specified dockerfile in the `polling-app-server` directory.
> dockerfile: Dockerfile



Hier wird auf das jeweilige Dockerfile referenziert, welches für das Erstellen des Images vorhanden ist. Bei dem MySql-Image wird dann einfach von einem bereits vorhanden Image abgeleitet. 

Der Part der danach kommt bezieht sich dann nur auf die Instanz dieses Images 



> ports:
> - "8080:8080" # Forward the exposed port 8080 on the container to port 8080 on the host machine
> restart: always
> depends_on:
> ...



Diese Informationen sind dann also nicht an dem Image gebunden, sondern beziehen sich nur auf den Container. Die Abhängigkeiten untereinander (*depends_on: db*) sind also gebunden an den Container und nicht an ein Image. Right ?


----------



## Dukel (4. Apr 2020)

Gerade wenn es um Datenbanken in Docker geht musst du dir auch um Persistenz gedanken machen.
Ein Container verliert nach dem restart alle Daten. Wenn die Daten benötigt werden brauchst du eine Möglichkeit die Daten außerhalb des Containers zu speichern.


----------



## JUUNND (4. Apr 2020)

Hallo,



> Wenn die Daten benötigt werden brauchst du eine Möglichkeit die Daten außerhalb des Containers zu speichern


Wenn ich mich nicht nicht täusche können diese Daten in Volumes hinterlegt werden (ist derzeit ja der Fall) und bei einem Restart des Containers werden diese Daten dann aus diesem Volume geladen. Oder wie was wäre dein Vorgehen ?


----------



## mihe7 (4. Apr 2020)

Das Volume ist außerhalb des Containers und befindet sich ganz normal im Dateisystem.


----------



## Flown (4. Apr 2020)

Volumes sind genau dazu da. Das ist das richtige Vorgehen. Nur wichtig zu beachten ist, diese Volumes auch zu sichern.


----------



## JUUNND (5. Apr 2020)

mihe7 hat gesagt.:


> Das Volume ist außerhalb des Containers und befindet sich ganz normal im Dateisystem.


Das heißt im Grunde wenn ein Container eines Images auf ein Volume zugreift welches die Daten für einen MySql Service bereit hält...Dann müsste ich, wenn ich das Image auf einem anderen Host ausrollen möchte und die Daten behalten möchte, einfach nur das Volume irgendwie auf den anderen Host übertragen ?



Flown hat gesagt.:


> Nur wichtig zu beachten ist, diese Volumes auch zu sichern.


Was meinst du in dem Kontext genau mit "sichern" ? Das Volume selbst dient doch zur Sicherung (eben dass die Daten beim Restart nicht verloren gehen) ?


----------



## mihe7 (5. Apr 2020)

JUUNND hat gesagt.:


> Das heißt im Grunde wenn ein Container eines Images auf ein Volume zugreift welches die Daten für einen MySql Service bereit hält...Dann müsste ich, wenn ich das Image auf einem anderen Host ausrollen möchte und die Daten behalten möchte, einfach nur das Volume irgendwie auf den anderen Host übertragen ?


Exakt. Es gibt bind mounts und Volumes. Bind mounts sind Verzeichnisse des Hostdateisystems, die auf das Container-Dateisystem gemappt werden. Zum Beispiel `-v /opt/myapp/data:/var/lib/mysql`: hier wird /opt/myapp/data des Hostdateisystems im Container unter /var/lib/mysql bereitgestellt. In dem Fall musst Du also /opt/myapp/data sichern bzw. übertragen. 

Die andere Möglichkeit sind Volumes. Ohne Anwendung von Plugins führt z. B. `-v app-mysql:/var/lib/mysql` zu einem ähnlichen Ergebnis, nur dass hier ein von Docker verwaltetes Volume "app-mysql" verwendet wird, das ebenfalls im Dateisystem des Hosts abgelegt wird. Den Speicherort findet man mit docker volume inspect app-mysql. 

Anleitung zu Volumes, auch zu deren Backup, gibts unter https://docs.docker.com/storage/volumes/


----------



## Flown (5. Apr 2020)

JUUNND hat gesagt.:


> Was meinst du in dem Kontext genau mit "sichern" ? Das Volume selbst dient doch zur Sicherung (eben dass die Daten beim Restart nicht verloren gehen) ?


Naja es gibt viele Tutorials im Internet wie man Datenbanken/Volumes aus Docker aus sichert (das Hostsystem kann ja sterben, bzw. die Festplatte)


----------



## JUUNND (6. Apr 2020)

@Flown 
Ah ok in dem Sinne meinst du das. 


@mihe7 
Danke für die Erklärung

Ich wollte mein Problem zunächst erstmal ein wenig versuchen zu abstrahieren und erstmal nur versuchen einen neuen MySql-Container aus dem jeweiligen Image zu erstellen. Ob die bereits vorhanden Daten dabei verloren gehen oder nicht spielt erstmal keine Rolle. 

Ich habe meine ganzen Images und Volumes und nochmal die Docker-Compose ausgeführt. Nach der Ausführung schaut es so aus: 
Container

Volume:



Ich habe über meine Spring Boot Anwendung einen entsprechenden Endpunkt definiert, welches mir Daten aus der Datenbank zurückliefern soll. Nachdem ich Daten eingepflegt habe, funktioniert das auch wunderbar. 
Nun stoppe ich den MySql Container der durch Docker Compose bereits erstellt wurde, und versuche dann in Powershell über 


> docker run -d --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7


einen neuen Container zu erstellen. 

Container





Volume


Die Daten wurden hierbei aus der Datenbank entfernt und ich pflege nun wieder neue Daten ein. Zur Überprüfung lasse ich mir über 


> docker exec -it bf15ae3506e5 -uroot -p
> use db;
> select * from tab;



die Daten anzeigen -> sind vorhanden. 

Wenn ich allerding über meine Spring Boot Anwendung den entsprechenden Link öffne, dann scheint es so, dass die Verbindung nicht hergestellt werden kann. 




Woran könnte das liegen ?


----------



## mihe7 (6. Apr 2020)

Der MySQL-Container ist nicht mit dem Netzwerk verbunden, mit dem Deine Anwendung verbunden ist. 

Du könntest den Container stoic_morse mit dem Netzwerk backend verbinden mit:
`docker network connect backend stoic_morse`

Bzw. beim Ausführen:

```
docker run -d --network=backend --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7
```

Die Netzwerke kannst Du (vorher) mit

```
docker network ls
```
auflisten


----------



## JUUNND (6. Apr 2020)

Hallo,
"backend" ist bei mir nicht als Netzwek registriert. Dies ist unter dem Namen services_backend gelisitet. 



Wenn der "Originale" MySql Container läuft und ich mir   über _*docker network inspect services_backend*_ mehr Informationen anzeigen lassen möchte, schaut es so aus


Wenn ich nun den Container stoppe und über 


> docker run  -d --name=test_123 --network=services_backend --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7


einen neuen Container namens test_123 erzeuge, sieht das ähnlich aus. 



Allerdings kann mein Spring Boot Container immer noch nicht mit dem neu erstellten MySql Container kommunizieren. Habe ich evtl. irgendwas vergessen oder nicht bedacht ?


----------



## mihe7 (6. Apr 2020)

JUUNND hat gesagt.:


> Allerdings kann mein Spring Boot Container immer noch nicht mit dem neu erstellten MySql Container kommunizieren. Habe ich evtl. irgendwas vergessen oder nicht bedacht ?


Du hast im Docker-Compose-File angegeben, dass der MySQL-Host via "db" angesprochen wird. Den Namen musst Du mitgeben, ansonsten ist der Hostname gleich dem Container-Namen. Probier mal
`docker run -d --hostname=db --name=test_123 --network=services_backend --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7`

Unabhängig davon: es kann auch sein, dass kein reconnect versucht wird, wenn Du die Verbindung einmal gekappt hast. Das wäre dann aber kein Docker-spezifisches Problem, sondern eine Frage der Konfiguration des JDBC-Treibers bzw. des Connection Pools.


----------



## JUUNND (6. Apr 2020)

mihe7 hat gesagt.:


> Probier mal
> `docker run -d --hostname=db --name=test_123 --network=services_backend --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7`


Auch das hat leider nicht funktioniert.



> Unabhängig davon: es kann auch sein, dass kein reconnect versucht wird, wenn Du die Verbindung einmal gekappt hast. Das wäre dann aber kein Docker-spezifisches Problem, sondern eine Frage der Konfiguration des JDBC-Treibers bzw. des Connection Pools.


Hmm...Wie kann ich denn am besten herausfinden ob ich von Spring Boot aus auf mein MySql Container zugreifen kann ? Also über MySql Workbench gibt es beim Verbindungsaufbau zumindest keine Probleme.




So wird in Spring Boot übrigens die Verbindung aufgebaut


----------



## mihe7 (6. Apr 2020)

JUUNND hat gesagt.:


> So wird in Spring Boot übrigens die Verbindung aufgebaut


Mich wundert, dass das überhaupt funktioniert haben soll: localhost wäre ja der eigene Container und nicht der MySQL-Container.


----------



## JUUNND (6. Apr 2020)

mihe7 hat gesagt.:


> localhost wäre ja der eigene Container und nicht der MySQL-Container.


Wie genau meinst du das ? Also im Docker Compose definiere ich ja den MySql Container als "db" und dass dieser über Port 3307 erreichbar sein soll. So steht es ja auch in der URL in der Spring Boot Konfiguration


----------



## thecain (6. Apr 2020)

statt localhost müsstest du in den properties den hostname eintragen.

localhost von deinem Host aus geht nur, weil da ein Mapping via Docker passiert


----------



## mihe7 (6. Apr 2020)

JUUNND hat gesagt.:


> Wie genau meinst du das ? Also im Docker Compose definiere ich ja den MySql Container als "db" und dass dieser über Port 3307 erreichbar sein soll. So steht es ja auch in der URL in der Spring Boot Konfiguration


Richtig, dann müsstest Du vom SpringBoot-Container aber auch via "db" auf den MySQL-Container zugreifen und nicht via "localhost", wie Du es in Deinem Kommentar #15 gezeigt hast.


----------



## JUUNND (6. Apr 2020)

Ahh ne, das war bei mir schon richtig. Ich hatte ein Screenshot aus einer veralteten Konfigurationsdatei erstellt. 
Die entsprechende Datasource Url lautet


> spring.datasource.url = jdbc:mysql://db:3307/db?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false


(Der Rest ist unverändert)
Das sollte so passen.

Und nochmal zu dem Punkt


> es kann auch sein, dass kein reconnect versucht wird, wenn Du die Verbindung einmal gekappt hast. Das wäre dann aber kein Docker-spezifisches Problem, sondern eine Frage der Konfiguration des JDBC-Treibers bzw. des Connection Pools.


Ich denke das kann ausgeschlossen werde, da wenn ich den originalen Container nochmal starte (_docker start ID_) gibt es keine Probleme.


----------



## mihe7 (6. Apr 2020)

JUUNND hat gesagt.:


> Ich hatte ein Screenshot aus einer veralteten Konfigurationsdatei erstellt.
> Die entsprechende Datasource Url lautet


Aha.



JUUNND hat gesagt.:


> Ich denke das kann ausgeschlossen werde, da wenn ich den originalen Container nochmal starte (_docker start ID_) gibt es keine Probleme.


OK. Wenn der neue Container mit gleichem Hostnamen im gleichen Netz platziert wird, sehe ich keinen Grund, warum das nicht funktionieren sollte... Schau mal mit docker logs nach, ob Du der Exception mehr Info entnehmen kannst.


----------



## JUUNND (6. Apr 2020)

Hmm ich glaube so langsam kommen wir der Sache näher...Zumindest konnte ich hier  bei den Logs Abweichungen zwischen dem funktionierenden Container und dem fehlerhaften Containers feststellen. Hier mal ein Ausschnitt aus den Logs der glaube ich ganz interessant ist. 
Ich habe im Anhang die kompletten Logs mal vorsichtshalber beigefügt. 

Funktionierender Container


> 2020-04-06T09:55:50.946353Z 0 [Note] mysqld: ready for connections.
> Version: '5.7.29'  socket: '/var/run/mysqld/mysqld.sock'  port: 0  MySQL Community Server (GPL)
> 2020-04-06 09:55:51+00:00 [Note] [Entrypoint]: Temporary server started.
> Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
> ...



Fehlerhafter Container


> 020-04-06 17:25:39+00:00 [Note] [Entrypoint]: Temporary server started.
> Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
> Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
> Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
> ...



Kannst du mir den Informationen was anfangen und weiß was zu tun ist ? Hatte mal versucht nach dem Starten des Containers die Berechtigungen anzupassen, möglicherweise gibt es da ja irgendeinen Zusammenhang. Hat aber nichts gebracht. 



> -> docker exec -it ID bin/bash
> -> mysql -uroot -p
> -> ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'root';
> -> GRANT ALL PRIVILEGES ON *.* TO 'root' WITH GRANT OPTION;
> ->FLUSH PRIVILEGES;


----------



## mihe7 (6. Apr 2020)

Ich meinte die Logs des SpringBoot-Containers  (dort tritt ja die Exception auf).


----------



## JUUNND (7. Apr 2020)

Also ich habe mal mitgeloggt bei dem Versuch auf die ensprechende Ressource der Spring Boot Anwendung zuzugreifen. Dabei erhalte ich eine _UnknownHostException_.



Aus einem anderen Thread bei dem jemand ein ähnliches Problem hatte, konnte dies durch den Zusatz *autoReconnect=true* in der Spring URL gelöst werden. Bei mir hatte dies leider keine spürbare Auswirkung.

ich hatte jetzt jedoch gesehen, dass über _*docker inspect *_auch detaillierte Informationen über die Container angezeigt werden können. Das habe ich bei den MySql Containern (beim fehlerfreien und fehlerhaften) mal gemacht und mal stumpf verglichen. Ich habe mal die Zeilen wo es Abweichungen gibt (und wo ich denke, dass es dort keine Abweichungen geben darf oder ich mir nicht sicher war) fett markiert. Ich denke insb. die Binds in der Hostconfig und die Labels sind auffällig.
Da scheint wohl etwas nicht korrekt zu funktionieren. 
Was meinst du ?

Bei dem fehlerfreien Contaiern wird als Hostname die entsprechende Container ID verwendet. Das sollte aber glaube keinen Unterschied machen, oder ? Ich habe zumindest auf die Schnelle keine Möglichkeit gefunden um den Hostname während der Laufzeit auf die Container ID zu ändern um das mal zu testen.

Fehlerfhaft


> [
> {
> "Id": "ea4e60e6f4ff62bdc52ac30dd2204d92daaf9c5d550c730d4a1791fb4dc85080",
> "Created": "2020-04-06T21:45:37.165073469Z",
> ...




Fehlerfrei


> [
> {
> "Id": "cd907d16156f151506ecf54eaba7a644196ee2af01b8da12d76c596fd23fc568",
> "Created": "2020-04-06T09:55:42.725256041Z",
> ...


----------



## mihe7 (7. Apr 2020)

JUUNND hat gesagt.:


> Bei mir hatte dies leider keine spürbare Auswirkung.


Nein, bei Dir findet er den Host nicht...

Hab das grad mal getestet... hostname interessiert den DNS-Server nicht. Wenn Du aber den Container erst startest und dann per `network connect --alias db services_backend <container-name>` den Container <container-name> mit dem Netzwerk services_backend unter dem Alias db verbindest, dann ist der Container unter "db" von den anderen Containern im Netzwerk aus erreichbar.


----------



## JUUNND (7. Apr 2020)

Oh ja, es funktioniert! Vielen Dank!   Dann kann ich das morgen ja mit dem push/pull auf dem anderen Host versuchen, aber sollte ja hoffentlich nicht mehr die Schwierigkeit sein!

Mal aus Interesse: Hast du das mit der nachträglichen Verbindung in das Netzwerk "einfach nur mal so" probiert ob das daran liegt, oder konntest du das aus den Logs herauslesen ?


----------



## mihe7 (7. Apr 2020)

JUUNND hat gesagt.:


> Hast du das mit der nachträglichen Verbindung in das Netzwerk "einfach nur mal so" probiert ob das daran liegt, oder konntest du das aus den Logs herauslesen ?


Nein, aus den Logs war nur zu entnehmen, dass der Container im Netz nicht unter dem angegebenen Hostnamen zu finden ist. Naja, und dann gibt es etwas wie Dokumentation -> Test -> für gut befunden


----------

