wake-up-neo.com

Grundlegendes zur Anweisung "VOLUME" in DockerFile

Unten ist der Inhalt meiner "Dockerfile"

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app

# change working dir to /usr/src/app
WORKDIR /usr/src/app

VOLUME . /usr/src/app

RUN npm install

EXPOSE 8080

CMD ["node" , "server" ]

In dieser Datei erwarte ich die Anweisung "VOLUME./Usr/src/app", um den Inhalt des aktuellen Arbeitsverzeichnisses im Host bereitzustellen, der im Ordner/usr/src/app des Containers bereitgestellt werden soll.

Bitte lassen Sie mich wissen, ob dies der richtige Weg ist.

78
refactor

Das offizielle Docker-Tutorial sagt:

Ein Daten-Volume ist ein speziell festgelegtes Verzeichnis in einem oder mehreren Containern, das das Union File System umgeht. Datenvolumes bieten verschiedene nützliche Funktionen für persistente oder gemeinsam genutzte Daten:

  • Volumes werden beim Erstellen eines Containers initialisiert. Wenn das Basisimage des Containers Daten am angegebenen Einhängepunkt enthält,
    dass vorhandene Daten von Volume zu Volume in das neue Volume kopiert werden
    Initialisierung. (Beachten Sie, dass dies beim Mounten eines Hosts nicht zutrifft
    Verzeichnis.)
  • Datenmengen können gemeinsam genutzt und zwischen Containern wiederverwendet werden.

  • Änderungen an einem Datenvolumen werden direkt vorgenommen.

  • Änderungen an einem Datenvolumen werden nicht berücksichtigt, wenn Sie ein Image aktualisieren.

  • Datenmengen bleiben bestehen, auch wenn der Container selbst gelöscht wird.

In Dockerfile können Sie nur das Ziel eines Volumes angeben innerhalb eines Containers. z.B. /usr/src/app.

Wenn Sie Ihren Container z. docker run --volume=/opt:/usr/src/app my_image Sie können müssen aber nicht den Einhängepunkt (/opt ) auf dem Host-Rechner angeben. Wenn Sie kein Argument --volume Angeben, wird der Einhängepunkt automatisch ausgewählt.

47
Bukharov Sergey

Kurz gesagt: Nein, Ihre Anweisung VOLUME ist nicht korrekt.

Dockerfiles VOLUME gibt ein oder mehrere Volumes an, die container-seitige Pfade haben. Der Bildautor kann jedoch keinen Host-Pfad angeben. Auf der Host-Seite werden die Volumes mit einem sehr langen, ID-ähnlichen Namen im Docker-Stammverzeichnis erstellt. Auf meinem Computer ist dies /var/lib/docker/volumes.

Hinweis: Da der automatisch generierte Name extrem lang ist und aus menschlicher Sicht keinen Sinn ergibt, werden diese Volumes häufig als "unbenannt" oder "anonym" bezeichnet.

Ihr Beispiel mit einem '.' Zeichen werden nicht einmal auf meinem Computer ausgeführt, egal ob ich den Punkt zum ersten oder zweiten Argument mache. Ich erhalte folgende Fehlermeldung:

andockfenster: Fehlerantwort vom Dämon: oci Laufzeitfehler: container_linux.go: 265: Start des Containerprozesses verursacht "process_linux.go: 368: Container-Init verursacht \" open/dev/ptmx: keine solche Datei oder kein solches Verzeichnis\"".

Ich weiß, dass das, was zu diesem Punkt gesagt wurde, für jemanden, der versucht, VOLUME und -v Zu verstehen, wahrscheinlich nicht sehr wertvoll ist, und es bietet mit Sicherheit keine Lösung für das, was Sie erreichen möchten. In den folgenden Beispielen werden diese Probleme hoffentlich näher beleuchtet.

Minitutorial: Festlegen von Volumes

In Anbetracht dieser Dockerfile:

FROM openjdk:8u131-jdk-Alpine
VOLUME vol1 vol2

(Für das Ergebnis dieses Minitutorials spielt es keine Rolle, ob wir vol1 vol2 Oder /vol1 /vol2 Angeben - fragen Sie mich nicht, warum)

Baue es:

docker build -t my-openjdk

Lauf:

docker run --rm -it my-openjdk

Führen Sie im Container ls in der Befehlszeile aus, und Sie werden feststellen, dass zwei Verzeichnisse vorhanden sind. /vol1 Und /vol2.

Durch das Ausführen des Containers werden auch zwei Verzeichnisse oder "Volumes" auf der Host-Seite erstellt.

Wenn der Container läuft, führen Sie docker volume ls Auf dem Host-Rechner aus und Sie werden so etwas sehen (der Kürze halber habe ich den mittleren Teil des Namens durch drei Punkte ersetzt):

DRIVER    VOLUME NAME
local     c984...e4fc
local     f670...49f0

Zurück im Container führen Sie touch /vol1/weird-ass-file Aus (erstellt eine leere Datei an dieser Stelle).

Diese Datei ist jetzt auf dem Host-Computer in einem der unbenannten Volumes verfügbar. Es dauerte zwei Versuche, da ich zuerst das erste aufgelistete Volume ausprobiert hatte, aber schließlich fand ich meine Datei auf dem zweiten aufgelisteten Volume mithilfe des folgenden Befehls auf dem Host-Computer:

Sudo ls /var/lib/docker/volumes/f670...49f0/_data

Ebenso können Sie versuchen, diese Datei auf dem Host zu löschen, und sie wird auch im Container gelöscht.

Hinweis: Der Ordner _data Wird auch als "Einhängepunkt" bezeichnet.

Verlassen Sie den Container und listen Sie die Volumes auf dem Host auf. Sie sind weg. Wir haben beim Ausführen des Containers das Flag --rm Verwendet und diese Option löscht nicht nur den Container beim Beenden, sondern auch die Volumes.

Führen Sie einen neuen Container aus, geben Sie jedoch ein Volume mit -v An:

docker run --rm -it -v /vol3 my-openjdk

Dies fügt hinz ein drittes Volume und das gesamte System hat drei unbenannte Volumes. Der Befehl wäre abgestürzt, wenn wir nur -v vol3 Angegeben hätten. Das Argument muss ein absoluter Pfad innerhalb des Containers sein. Auf der Host-Seite ist der neue dritte Datenträger anonym und befindet sich zusammen mit den beiden anderen Datenträgern in /var/lib/docker/volumes/.

Es wurde bereits erwähnt, dass Dockerfile keinem Hostpfad zugeordnet werden kann, was für uns ein Problem darstellt, wenn versucht wird, Dateien vom Host zum Container zur Laufzeit zu übertragen. Eine andere Syntax -v Löst dieses Problem.

Stellen Sie sich vor, ich habe einen Unterordner in meinem Projektverzeichnis ./src, Den ich mit /src Im Container synchronisieren möchte. Dieser Befehl macht den Trick:

docker run -it -v $(pwd)/src:/src my-openjdk

Beide Seiten des Zeichens : Erwarten einen absoluten Pfad. Die linke Seite ist ein absoluter Pfad auf dem Host-Computer, die rechte Seite ist ein absoluter Pfad innerhalb des Containers. pwd ist ein Befehl, der "Aktuelles/Arbeitsverzeichnis drucken". Wenn Sie den Befehl in $() einfügen, wird der Befehl in Klammern angegeben, in einer Subshell ausgeführt und der absolute Pfad zu unserem Projektverzeichnis zurückgegeben.

Nehmen wir an, wir haben ./src/Hello.Java Mit folgendem Inhalt in unserem Projektordner auf dem Host-Rechner:

public class Hello {
    public static void main(String... ignored) {
        System.out.println("Hello, World!");
    }
}

Wir bauen dieses Dockerfile:

FROM openjdk:8u131-jdk-Alpine
WORKDIR /src
ENTRYPOINT javac Hello.Java && Java Hello

Wir führen diesen Befehl aus:

docker run -v $(pwd)/src:/src my-openjdk

Dies druckt "Hallo, Welt!".

Das Beste daran ist, dass wir die .Java-Datei mit einer neuen Nachricht für eine andere Ausgabe bei einem zweiten Durchlauf ändern können - ohne das Image neu erstellen zu müssen =)

Schlussbemerkungen

Ich bin ein Neuling in Docker, und das oben genannte "Tutorial" spiegelt Informationen wider, die ich bei einem dreitägigen Kommandozeilen-Hackathon gesammelt habe. Ich schäme mich fast, dass ich keine Links zu eindeutigen englischsprachigen Dokumentationen zur Verfügung stellen konnte, die meine Aussagen stützen, aber ich bin ehrlich, dass dies auf einen Mangel an Dokumentation und nicht auf persönlichen Aufwand zurückzuführen ist. Ich weiß, dass die Beispiele mit meinem aktuellen Setup funktionieren, das "Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce" ist.

Das Tutorial löst nicht das Problem "Wie geben wir den Pfad des Containers in der Docker-Datei an und lassen den Befehl run nur den Host-Pfad angeben". Es könnte einen Weg geben, den ich einfach nicht gefunden habe.

Schließlich habe ich das gute Gefühl, dass die Angabe von VOLUME in der Docker-Datei keine Seltenheit ist, aber es ist wahrscheinlich eine bewährte Methode, VOLUME niemals zu verwenden. Aus zwei Gründen. Der erste Grund, den wir bereits identifiziert haben: Wir können den Host-Pfad nicht angeben - was eine gute Sache ist, da Docker-Dateien sehr unabhängig von den Besonderheiten eines Host-Computers sein sollten. Der zweite Grund ist, dass die Benutzer möglicherweise vergessen, die Option --rm Zu verwenden, wenn der Container ausgeführt wird. Man könnte daran denken, den Behälter zu entfernen, aber vergessen, das Volumen zu entfernen. Selbst mit dem besten menschlichen Gedächtnis kann es eine entmutigende Aufgabe sein, herauszufinden, welche anonymen Volumes sicher entfernt werden können.

190

Wenn Sie eine VOLUME -Zeile in einer Docker-Datei angeben, werden einige Metadaten in Ihrem Bild konfiguriert. Es ist jedoch wichtig, wie diese Metadaten verwendet werden.

Was haben diese beiden Zeilen zuerst gemacht:

WORKDIR /usr/src/app
VOLUME . /usr/src/app

Die WORKDIR -Zeile dort erstellt das Verzeichnis, falls es nicht vorhanden ist, und aktualisiert einige Bildmetadaten, um alle relativen Pfade anzugeben. Das aktuelle Verzeichnis für Befehle wie RUN befindet sich an diesem Speicherort. Die VOLUME Zeile dort gibt zwei Datenträger an, einer ist der relative Pfad ., und der andere ist /usr/src/app, beide sind zufällig dasselbe Verzeichnis. In den meisten Fällen enthält die VOLUME -Zeile nur ein einziges Verzeichnis, kann jedoch wie bisher mehrere enthalten, oder es kann sich um ein json-formatiertes Array handeln.

Sie können in der Docker-Datei keine Volume-Quelle angeben : Wenn Sie in einer Docker-Datei Volumes angeben, wird häufig versucht, die Laufzeitsyntax einer Quelle mit der einer zu vergleichen Ziel zum Zeitpunkt der Imageerstellung, das wird nicht funktionieren. Die Docker-Datei kann nur das Ziel des Volumes angeben. Es wäre ein trivialer Sicherheits-Exploit, wenn jemand die Quelle eines Volumes definieren könnte, da er ein allgemeines Image auf dem Docker-Hub aktualisieren könnte, um das Stammverzeichnis in den Container einzubinden, und dann einen Hintergrundprozess im Container als Teil eines Eintrittspunkts starten könnte Fügt Anmeldungen zu/etc/passwd hinzu, konfiguriert systemd so, dass beim nächsten Neustart ein Bitcoin-Miner gestartet wird, oder durchsucht das Dateisystem nach Kreditkarten, SSNs und privaten Schlüsseln, die an einen Remotestandort gesendet werden sollen.

Was macht die VOLUME-Zeile? Wie bereits erwähnt, werden einige Bild-Metadaten gesetzt, um anzuzeigen, dass ein Verzeichnis im Bild ein Volume ist. Wie werden diese Metadaten verwendet? Jedes Mal, wenn Sie einen Container aus diesem Image erstellen, erzwingt Docker, dass dieses Verzeichnis ein Volume ist. Wenn Sie in Ihrem Befehl run kein Volume angeben oder keine Datei erstellen, besteht die einzige Option für docker darin, ein anonymes Volume zu erstellen. Hierbei handelt es sich um ein lokales, benanntes Volume mit einer langen eindeutigen ID für den Namen und keinem weiteren Hinweis darauf, warum es erstellt wurde oder welche Daten darin enthalten sind (anonyme Volumes, auf denen Daten verloren gehen). Wenn Sie das Volume überschreiben und auf ein benanntes oder Host-Volume verweisen, werden Ihre Daten stattdessen dorthin verschoben.

VOLUME bricht Dinge: Sie können ein in einer Docker-Datei definiertes Volume nicht deaktivieren. Und was noch wichtiger ist: Der Befehl RUN in docker wird mit temporären Containern implementiert. Diese temporären Container erhalten ein temporäres anonymes Volume. Dieses anonyme Volume wird mit dem Inhalt Ihres Bildes initialisiert. Alle Schreibvorgänge in den Container von Ihrem Befehl RUN werden auf diesen Datenträger ausgeführt. Wenn der Befehl RUN ausgeführt wird, werden Änderungen am Image gespeichert und Änderungen am anonymen Volume werden verworfen. Aus diesem Grund empfehle ich dringend, kein VOLUME im Dockerfile. Dies führt zu unerwartetem Verhalten für nachgeschaltete Benutzer Ihres Images, die das Image mit anfänglichen Daten an der Position des Volumes erweitern möchten.

Wie soll ein Volume angegeben werden? Um anzugeben, wo Sie Volumes in Ihr Image aufnehmen möchten, geben Sie ein docker-compose.yml. Benutzer können dies ändern, um den Speicherort des Volumes an ihre lokale Umgebung anzupassen. Außerdem werden andere Laufzeiteinstellungen wie das Veröffentlichen von Ports und das Netzwerk erfasst.

Jemand sollte das dokumentieren! Sie haben. Docker enthält Warnungen zur Verwendung von VOLUME in Dokumentation zur Docker-Datei zusammen mit dem Hinweis, die Quelle zur Laufzeit anzugeben:

  • Ändern des Volumes aus der Docker-Datei heraus: Wenn Build-Schritte die Daten innerhalb des Volumes ändern, nachdem sie deklariert wurden, werden diese Änderungen verworfen.

...

  • Das Host-Verzeichnis wird zur Container-Laufzeit deklariert: Das Host-Verzeichnis (der Mountpoint) ist von Natur aus Host-abhängig. Dies dient der Wahrung der Image-Portabilität, da nicht garantiert werden kann, dass ein bestimmtes Host-Verzeichnis auf allen Hosts verfügbar ist. Aus diesem Grund können Sie in der Docker-Datei kein Host-Verzeichnis bereitstellen. Die Anweisung VOLUME unterstützt nicht die Angabe eines Host-dir Parameter. Sie müssen den Mountpunkt angeben, wenn Sie den Container erstellen oder ausführen.
15
BMitch

Der Befehl VOLUME in einem Dockerfile ist durchaus legitim, völlig konventionell, absolut in Ordnung und wird in keiner Weise als veraltet angesehen. Ich muss es nur verstehen.

Wir verwenden es, um auf Verzeichnisse zu verweisen, in die die App im Container viel schreibt. Wir verwenden VOLUME nicht, nur weil wir es wie eine Konfigurationsdatei zwischen Host und Container teilen wollen.

Der Befehl benötigt lediglich einen Parameter. Ein Pfad zu einem Ordner, relativ zu WORKDIR, falls festgelegt, innerhalb des Containers. Anschließend erstellt docker ein Volume in seinem Diagramm (/ var/lib/docker) und hängt es in den Ordner im Container ein. Jetzt muss der Container mit hoher Leistung irgendwo beschrieben werden. Ohne den Befehl VOLUME ist die Schreibgeschwindigkeit in den angegebenen Ordner sehr langsam, da der Container jetzt die Strategie copy on write Im Container selbst verwendet. Die Strategie copy on write Ist ein Hauptgrund, warum Volumes existieren.

Wenn Sie über den Ordner mounten, der mit dem Befehl VOLUME angegeben wurde, wird der Befehl nie ausgeführt, da VOLUME nur ausgeführt wird, wenn der Container gestartet wird, ähnlich wie ENV.

Grundsätzlich erhalten Sie mit dem Befehl VOLUME Leistung, ohne externe Datenträger bereitzustellen. Daten werden auch über Containerläufe ohne externe Bereitstellungen gespeichert. Wenn Sie fertig sind, montieren Sie einfach etwas darüber.

Einige gute Anwendungsbeispiele:
- Protokolle
- temporäre Ordner

Einige schlechte Anwendungsfälle:
- statische Dateien
- configs
- Code

14
mr haven

Um die Anweisung volume in dockerfile besser zu verstehen, lernen wir die typische Datenträgerverwendung in der Implementierung der offiziellen Docker-Datei von mysql kennen.

VOLUME /var/lib/mysql

Referenz: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile

Das /var/lib/mysql ist der Standardspeicherort von MySQL, in dem Datendateien gespeichert werden.

Wenn Sie den Testcontainer nur zu Testzwecken ausführen, können Sie den Montagepunkt nicht angeben, z.

docker run mysql:8

dann verwendet die mysql-Container-Instanz den Standard-Mount-Pfad, der in der Anweisung volume in dockerfile angegeben ist. Die Volumes werden mit einem sehr langen, ID-ähnlichen Namen im Docker-Stammverzeichnis erstellt. Dies wird als "unbenanntes" oder "anonymes" Volume bezeichnet. Im Ordner des zugrunde liegenden Host-Systems/var/lib/docker/volume.

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

Dies ist sehr praktisch für schnelle Testzwecke, ohne dass der Einhängepunkt angegeben werden muss. Sie können jedoch die beste Leistung erzielen, indem Sie Volume als Datenspeicher und nicht als Containerschicht verwenden.

Für eine formale Verwendung müssen Sie den Mount-Pfad angeben, indem Sie den Mount-Punkt überschreiben, um ein benanntes Volume zu verwenden, z.

docker run  -v /my/own/datadir:/var/lib/mysql mysql:8

Der Befehl hängt das Verzeichnis/my/own/datadir vom zugrunde liegenden Host-System als/var/lib/mysql im Container ein. Das Datenverzeichnis/my/own/datadir wird nicht automatisch gelöscht, auch der Container wird gelöscht.

Verwendung des offiziellen MySQL-Bildes: Referenz: https://hub.docker.com/_/mysql/

4
Li-Tian