Docker ermöglicht es Container an sogenannte Volumes zu mounten. Dadurch ergibt sich die Möglichkeit, Daten vom Container abzukapseln und somit über die Lebensdauer des Containers hinweg aufrecht zu erhalten.
Ein weiter Vorteil ist der, dass unterschiedliche Container auf solch ein Volume Zugriff haben können, sodass ein und die selben Daten für zwei unterschiedliche Container zur Verfügung stehen.
Zuletzt sei noch erwähnt, dass Volumes komplett von Docker gehandhabt werden, d.h. für jedes Volume wird von Docker ein neues Verzeichnis innerhalb dessen Speicherverzeichnisses auf der Hostmaschine angelegt (Linux bspw. /var/lib/docker/volumes/).
Es gibt 2 Möglichkeiten solche Volumes zu mounten
Docker empfiehlt neuen Benutzern --mount zu verwenden. Das liegt daran, dass man mit --mount explizit den Typen (bind, volume, tmpfs) mit angeben muss. Mit -v hingegen ist allein die Syntax des Befehls ausschlaggebend dafür, welcher Typ verwendet wird. Diese feinen Nuancen in der Syntax führen nun aber oft zu Leichtsinnsfehlern, die eben mit --mount nicht passieren können.
Wir betrachten daher im Folgenden nur den --mount Befehl.
Wir werden einen postgres alpine Container starten und diesen an ein von uns spezifiziertes Volume namens myvol mounten.
Zunächst lassen wir uns jedoch auf unserem Computer (=Hostmaschine) alle bisherigen Volumes ausgeben
apoehlmann $ docker volume ls
DRIVER VOLUME NAME
local d2b7c9c12cafe507265d84e180d1d1a80de01c684d6963b898adcb12426c8aff
local e9c8d47f1aa39674699d6e78ad279526e19110cf4dde87e5b7bb8e42b6502727
Nun starten wir einen postgres:alpine Container namens test und zwar mit interaktiver Shell. Wir spezifizieren, dass im Container das Verzeichnis /mydata an ein Volume namens myvol gemounted wird (insofern diese nicht bereits existieren, werden sie neu erstellt). Mit --rm geben wir an, dass der Container gelöscht wird, sobald wir die Shell schließen:
apoehlmann $ docker run --rm -it \
--name test1 \
--mount source=myvol,target=/mydata \
postgres:alpine sh
In meinem Fall lag noch kein postgres:alpine Image vor, daher sieht der Output wie folgt aus
Unable to find image 'postgres:alpine' locally
alpine: Pulling from library/postgres
ff3a5c916c92: Already exists
a503b44e1ce0: Pull complete
211706713093: Pull complete
ea28caf317dd: Pull complete
a9b37749335b: Pull complete
f7e94ebc5400: Pull complete
77dd3a51253d: Pull complete
633a37734b12: Pull complete
872e6b940bbb: Pull complete
Digest: sha256:89c84fcccc147d403916f4d9964bc4d83297ea7c782d79b05dad5b6c172a7dce
Status: Downloaded newer image for postgres:alpine
/ #
Das /# in der letzten Zeile signalisiert, dass wir uns in der Shell des postgres Containers befinden. Mittels ls können wir nun checken, ob das /mydata Verzeichnis vorliegt
/ # ls
bin docker-entrypoint.sh lib mydata run sys var
dev etc media proc sbin tmp
docker-entrypoint-initdb.d home mnt root srv usr
/ #
was tatsächlich der Fall ist. Mit docker -ps auf der Hostmaschine lassen wir uns alle aktuell laufenden Container ausgeben
apoehlmann $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89c449b4bc63 postgres:alpine "docker-entrypoint.s…" 1 second ago Up 4 seconds 5432/tcp test1
Nun überprüfen wir auf unseren Host PC nochmals alle vorhandenen Volumes
apoehlmann $ docker volume ls
DRIVER VOLUME NAME
local 78a6c197848b36afb5d47bae967e2628e3ac2cf25c08adc4b4e1002359ac38e6
local d2b7c9c12cafe507265d84e180d1d1a80de01c684d6963b898adcb12426c8aff
local e9c8d47f1aa39674699d6e78ad279526e19110cf4dde87e5b7bb8e42b6502727
local myvol
und wir erkennen, dass von Docker ein neues Volumen namens myvol kreiert wurde.
Zum Testen stellen wir eine neue Datei namens persistent.txt im Container her. Anschließend schließen wir die Shell (CTRL + D). Wegen dem --rm Flag wird der Container gelöscht.
/ # touch /mydata/persistent.txt
/ # ls /mydata/
persistent.txt
Wir werden nun versuchen, die Datei einem neuen Container zur Verfügung zu stellen.
Um die Datenpersistenz zu überprüfen, bauen wir nun einen neuen, zweiten Container namens test2. Der Befehl ist bis auf --name test2 der selbe
docker run --rm -it \
--name test2 \
--mount source=myvol,target=/mydata \
postgres:alpine sh
/ #
Dieses Mal wurden keine Images gedownloaded. Auch überprüft Docker automatisch, ob das als source spezifizierte Volume bereits existiert. Da dies der Fall ist, wird dieses bereits bestehende Volume verwendet. docker -ps auf der Hostmaschine liefert
apoehlmann $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d7377a064d17 postgres:alpine "docker-entrypoint.s…" 47 seconds ago Up 50 seconds 5432/tcp test2
und wir erkennen anhand der ID und dem Namen, dass es sich hierbei in der Tat um einen neuen Container handelt.
In der Shell des Containers lassen wir uns nun den Inhalt des /mydata Verzeichnisses ausgeben
/ # ls /mydata/
persistent.txt
/ #
Die Datei, die wir im vorherigen Container hergestellt haben ist trotz Löschung des alten Containers immer noch vorhanden. Wenn wir auf den Host ein Linux Betriebssystem benutzen, können wir uns den Inhalt des neu erstellten Docker Volumes mydata ausgeben lassen (auf macOS etc. ist dies nicht ohne weiteres möglich, da Docker hinter einer Virtualisierung läuft. Für mehr Info siehe https://stackoverflow.com/questions/38532483/where-is-var-lib-docker-on-mac-os-x)
apoehlmann $ ls /var/lib/docker/volumes/myvol/_data/
persistent.txt
Wie erwartet befindet sich die Datei im _data Verzeichnis des neu erstellten Docker Volumes.
Wir wissen nun, wie man ein Verzeichnis innerhalb eines Containers an ein externes Docker Volume mounten kann, und haben dies auch schon für ein selbst gewähltes Verzeichnis anhand zweier Beispiele gesehen.
Doch was, wenn wir sämtliche Postgres Daten innerhalb unseres Containers an ein Docker Volume mounten möchten?
Dafür müssen wir den Dateipfad kennen, unter dem postgres sämttliche Daten abspeichert, um dieses Verzeichnis als target angeben zu können. Unter Linux ist dies /var/lib/postgresql/data.