So mounten Sie Host-Volumes während des Builds in Docker-Containern in Dockerfile

236

Ursprüngliche Frage: Wie verwende ich die VOLUME-Anweisung in Dockerfile?

Die eigentliche Frage , die ich lösen will , ist - wie Host - Volumes in Docker - Container in Dockerfile während Build zu montieren, dh die mit docker run -v /export:/exportwährender Fähigkeit docker build.

Der Grund dafür ist für mich, dass ich beim Erstellen von Dingen in Docker nicht möchte, dass diese ( apt-get install) Caches in einem einzigen Docker gesperrt sind, sondern dass sie freigegeben / wiederverwendet werden. Das ist der Hauptgrund, warum ich zu dieser Frage frage.

Neuestes Update:

Vor Docker v18.09 sollte die richtige Antwort folgende sein:

Es gibt eine Möglichkeit, ein Volume während eines Builds bereitzustellen, jedoch ohne Docker-Dateien.

Dies war jedoch eine schlecht formulierte, organisierte und unterstützte Antwort. Bei der Neuinstallation meines Docker-Inhalts bin ich auf folgenden Artikel gestoßen:

Dockerisieren Sie einen apt-cacher-ng-Dienst
https://docs.docker.com/engine/examples/apt-cacher-ng/

Das ist die Lösung des Dockers für diese / meine Frage, nicht direkt, sondern indirekt. Es ist die orthodoxe Art, wie Docker uns vorschlägt. Und ich gebe zu, es ist besser als das, was ich hier fragen wollte.

Ein anderer Weg ist die neu akzeptierte Antwort , z. B. das Buildkit in Version 18.09.

Wählen Sie, was zu Ihnen passt.


War: Es gab eine Lösung - Rocker, der nicht von Docker stammte, aber jetzt, da der Rocker eingestellt wird, setze ich die Antwort wieder auf "Nicht möglich" zurück.


Altes Update: Die Antwort lautet also "Nicht möglich". Ich kann es als Antwort akzeptieren, da ich weiß, dass das Problem unter https://github.com/docker/docker/issues/3156 ausführlich besprochen wurde . Ich kann verstehen, dass Portabilität für Docker-Entwickler von größter Bedeutung ist. Aber als Docker-Benutzer muss ich sagen, dass ich über diese fehlende Funktion sehr enttäuscht bin. Lassen Sie mich mein Argument mit einem Zitat aus der oben genannten Diskussion schließen: " Ich möchte Gentoo als Basis-Image verwenden, möchte aber definitiv nicht, dass sich> 1 GB Portage-Baumdaten in einer der Ebenen befinden, sobald das Image erstellt wurde. Sie könnte einige schöne, kompakte Container haben, wenn nicht der gigantische Portage-Baum während der Installation im Image erscheinen müsste."Ja, ich kann wget oder curl verwenden, um alles herunterzuladen, was ich brauche, aber die Tatsache, dass ich aufgrund einer Portabilitätsüberlegung jetzt gezwungen bin, jedes Mal, wenn ich ein Gentoo-Basis-Image erstelle,> 1 GB Portage-Baum herunterzuladen, ist weder effizient noch benutzerfreundlich Darüber hinaus befindet sich das Paket-Repository IMMER unter / usr / portage und ist daher unter Gentoo IMMER TRAGBAR. Auch hier respektiere ich die Entscheidung, aber bitte erlauben Sie mir, in der Zwischenzeit auch meine Enttäuschung auszudrücken. Danke.


Ursprüngliche Frage im Detail:

Von

Freigeben von Verzeichnissen über Volumes
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/

Es heißt, dass die Datenvolumenfunktion "seit Version 1 der Docker Remote-API verfügbar ist". Mein Docker ist von Version 1.2.0, aber ich habe festgestellt, dass das im obigen Artikel angegebene Beispiel nicht funktioniert:

# BUILD-USING:        docker build -t data .
# RUN-USING:          docker run -name DATA data
FROM          busybox
VOLUME        ["/var/volume1", "/var/volume2"]
CMD           ["/usr/bin/true"]

Wie kann in Dockerfile Host-gemountete Volumes über den Befehl VOLUME ordnungsgemäß in Docker-Container eingebunden werden?

$ apt-cache policy lxc-docker
lxc-docker:
  Installed: 1.2.0
  Candidate: 1.2.0
  Version table:
 *** 1.2.0 0
        500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
        100 /var/lib/dpkg/status

$ cat Dockerfile 
FROM          debian:sid

VOLUME        ["/export"]
RUN ls -l /export
CMD ls -l /export

$ docker build -t data .
Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon 
Step 0 : FROM          debian:sid
 ---> 77e97a48ce6a
Step 1 : VOLUME        ["/export"]
 ---> Using cache
 ---> 59b69b65a074
Step 2 : RUN ls -l /export
 ---> Running in df43c78d74be
total 0
 ---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
 ---> Running in 8e4916d3e390
 ---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551

$ docker run data
total 0

$ ls -l /export | wc 
     20     162    1131

$ docker -v
Docker version 1.2.0, build fa7b24f
xpt
quelle
Anscheinend aktuellere Feature-Anfrage (nicht, dass ich davon ausgehe, dass sie implementiert wird, aber nur für den Fall): Docker / Docker # 14080
Jesse Glick
In der Tat gibt es eine ausführliche Diskussion, dass es nicht erlaubt sein sollte, ein Host-Verzeichnis und ein Container-Verzeichnis während des Builds zu verknüpfen, dh so etwas wie VOLUME ~/host_dir ~/container_dir. Die Diskussion ist ziemlich umfangreich, gibt es eine kurze Möglichkeit, um zusammenzufassen, was der Grund ist?
Charlie Parker

Antworten:

33

Um zu antworten: "Warum funktioniert das nicht VOLUME?" Wenn Sie VOLUMEin der Docker-Datei ein definieren , können Sie nur das Ziel definieren, nicht die Quelle des Volumes. Während des Builds erhalten Sie nur ein anonymes Volume. Dieses anonyme Volume wird bei jedem RUNBefehl bereitgestellt, mit dem Inhalt des Abbilds vorab ausgefüllt und am Ende des RUNBefehls verworfen . Es werden nur Änderungen am Container gespeichert, keine Änderungen am Volume.


Seit diese Frage gestellt wurde, wurden einige Funktionen veröffentlicht, die möglicherweise hilfreich sind. Das erste sind mehrstufige Builds, mit denen Sie einen ineffizienten Speicherplatz in der ersten Stufe erstellen und nur die erforderliche Ausgabe in die letzte Stufe kopieren können, die Sie ausliefern. Und die zweite Funktion ist Buildkit, das die Art und Weise, wie Bilder erstellt werden, dramatisch verändert und dem Build neue Funktionen hinzufügt.

Bei einem mehrstufigen Build würden Sie mehrere FROMZeilen haben, von denen jede die Erstellung eines separaten Bildes startet. Standardmäßig ist nur das letzte Bild markiert. Sie können jedoch Dateien aus früheren Phasen kopieren. Die Standardverwendung besteht darin, eine Compiler-Umgebung zum Erstellen eines binären oder anderen Anwendungsartefakts und eine Laufzeitumgebung als zweite Stufe zu haben, die über dieses Artefakt kopiert. Du könntest haben:

FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin

FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]

Dies würde zu einem Build führen, der nur die resultierende Binärdatei und nicht das vollständige / export-Verzeichnis enthält.


Buildkit kommt am 18.09 aus dem Experiment heraus. Es handelt sich um eine vollständige Neugestaltung des Erstellungsprozesses, einschließlich der Möglichkeit, den Frontend-Parser zu ändern. Eine dieser Parser-Änderungen hat die RUN --mountOption implementiert, mit der Sie ein Cache-Verzeichnis für Ihre Ausführungsbefehle bereitstellen können. Beispiel: Hier ist eines, das einige der Debian-Verzeichnisse bereitstellt (mit einer Neukonfiguration des Debian-Images könnte dies die Neuinstallation von Paketen beschleunigen):

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache \
    --mount=target=/var/cache/apt,type=cache \
    apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      git

Sie würden das Cache-Verzeichnis für jeden Anwendungs-Cache anpassen, z. B. $ HOME / .m2 für Maven oder /root/.cache für Golang.


TL; DR: Antwort ist hier: DamitRUN --mount Syntax können Sie auch schreibgeschützte Mount-Verzeichnisse aus dem Build-Kontext binden. Der Ordner muss im Build-Kontext vorhanden sein und darf nicht dem Host oder dem Build-Client zugeordnet werden:

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...

Beachten Sie, dass das Verzeichnis, da es über den Kontext bereitgestellt wird, auch schreibgeschützt bereitgestellt wird und Sie Änderungen nicht auf den Host oder Client zurückschieben können. Wenn Sie erstellen, möchten Sie eine 18.09 oder neuere Installation und aktivieren Sie das Buildkit mitexport DOCKER_BUILDKIT=1 .

Wenn Sie die Fehlermeldung erhalten, dass das Mount-Flag nicht unterstützt wird, bedeutet dies, dass Sie entweder das Buildkit mit der obigen Variablen nicht aktiviert haben oder dass Sie die experimentelle Syntax mit der Syntaxzeile oben in der Docker-Datei zuvor nicht aktiviert haben alle anderen Zeilen, einschließlich Kommentare. Beachten Sie, dass die Variable zum Umschalten des Buildkits nur funktioniert, wenn in Ihrer Docker-Installation Buildkit-Unterstützung integriert ist, für die Docker Version 18.09 oder höher sowohl auf dem Client als auch auf dem Server erforderlich ist.

BMitch
quelle
2
Leider wird unter Windows Buildkit in Version 18.09
Wesley
1
Es sieht so aus, als ob "armhf" auch "mount" nicht unterstützt.
Mike
2
Ich erhalte "Fehlerantwort vom Daemon: Dockerfile Parse Fehlerzeile xx: Unbekanntes Flag: Mount" unter OSX
ChristoKiwi
1
Die Unterstützung für Docker-Compose ist noch nicht vorhanden, aber Sie müssen Compose nicht erstellen, um Images zu erstellen.
Problem
116

Es ist nicht möglich, die VOLUMEAnweisung zu verwenden, um Docker mitzuteilen, was gemountet werden soll. Das würde die Portabilität ernsthaft beeinträchtigen. Diese Anweisung teilt Docker mit, dass der Inhalt in diesen Verzeichnissen nicht in Bildern gespeichert ist und über den --volumes-fromBefehlszeilenparameter von anderen Containern aus aufgerufen werden kann. Sie müssen den Container mit ausführen, -v /path/on/host:/path/in/containerum vom Host aus auf Verzeichnisse zuzugreifen.

Das Mounten von Host-Volumes während des Builds ist nicht möglich. Es gibt keinen privilegierten Build, und das Mounten des Hosts würde auch die Portabilität ernsthaft beeinträchtigen. Vielleicht möchten Sie versuchen, mit wget oder curl alles herunterzuladen, was Sie für den Build benötigen, und es zu installieren.

Andreas Steffan
quelle
2
Vielen Dank. Frage überarbeitet. Die eigentliche Frage, die ich lösen möchte, lautet: Wie werden Host-Volumes während des Builds in Docker-Containern in Dockerfile bereitgestellt? Vielen Dank.
xpt
2
Nicht möglich. Siehe überarbeitete Antwort.
Andreas Steffan
3
Ich kann die "möglichen" negativen Nebenwirkungen der Portabilität einschätzen, aber es gibt auch einen gültigen Anwendungsfall für diese Option. In meinem Fall würde ich gerne in der Lage sein, Benutzern zu sagen, dass sie in das Verzeichnis wechseln und den Befehl 'docker run' ausführen sollen, wobei $ (PWD) in einem Containerverzeichnis bereitgestellt ist. $ (PWD) stellt sicher, dass die Portabilität erhalten bleibt. Dies mag zwar ein Eckfall sein, aber es würde mir immens helfen, wo ich Laufzeitumgebungen für vom Benutzer bereitgestellte Skripte verteile.
ntwrkguru
64

UPDATE: Jemand wird einfach kein Nein als Antwort nehmen, und ich mag es sehr, besonders auf diese spezielle Frage.

GUTE NACHRICHTEN, jetzt gibt es einen Weg -

Die Lösung ist Rocker: https://github.com/grammarly/rocker

John Yani sagte : "IMO, es löst alle Schwachstellen von Dockerfile und macht es für die Entwicklung geeignet."

Rocker

https://github.com/grammarly/rocker

Mit der Einführung neuer Befehle möchte Rocker die folgenden Anwendungsfälle lösen, die bei einfachem Docker schmerzhaft sind:

  1. Mounten Sie wiederverwendbare Volumes in der Build-Phase, sodass Tools für das Abhängigkeitsmanagement möglicherweise den Cache zwischen den Builds verwenden.
  2. Teilen Sie SSH-Schlüssel mit Build (zum Abrufen privater Repos usw.), ohne sie im resultierenden Image zu belassen.
  3. Erstellen und Ausführen von Anwendungen in verschiedenen Images, einfache Übergabe eines Artefakts von einem Image an ein anderes, idealerweise in einer einzigen Docker-Datei.
  4. Markieren / Verschieben von Bildern direkt aus Docker-Dateien.
  5. Übergeben Sie Variablen aus dem Shell-Build-Befehl, damit sie durch eine Docker-Datei ersetzt werden können.

Und mehr. Dies sind die kritischsten Probleme, die unsere Einführung von Docker bei Grammarly blockiert haben.

Update: Rocker wurde gemäß dem offiziellen Projekt-Repo auf Github eingestellt

Ab Anfang 2018 ist das Container-Ökosystem viel ausgereifter als vor drei Jahren, als dieses Projekt initiiert wurde. Jetzt können einige der kritischen und herausragenden Funktionen von Rocker problemlos durch Docker-Build oder andere gut unterstützte Tools abgedeckt werden, obwohl einige Funktionen nur für Rocker gelten. Weitere Informationen finden Sie unter https://github.com/grammarly/rocker/issues/199 .

xpt
quelle
Ich versuche, mit Rocker das Problem Nummer 1 zu lösen, aber der Befehl mount funktioniert nicht und das erstellte Image enthält nicht den Hostordner. Mein Dockerfile-Mount-Befehl sieht folgendermaßen aus - MOUNT ~/code/docker-app-dev/new-editor/:/src/und mein Rocker-Build-Befehl lautet wie folgt rocker build -f Dockerfile .: Was mache ich falsch?
Yaron Idan
Vielleicht versuchen Sie es mit einem echten Hostpfad? ~ist ein Bourne-Shell-Metazeichen.
Jesse Glick
Rocker builderlaubt keine docker runBefehlszeilenoptionen, erlaubt also derzeit keine Dinge wie --privileged.
Monty Wild
Hallo @xpt, können wir ein weiteres Update bekommen, da Rocker jetzt eingestellt wird
Shardj
Nachdem die Wippe eingestellt wurde, setze ich die Antwort wieder auf "Nicht möglich" zurück. Siehe OP und die ausgewählte Antwort.
xpt
14

Es gibt eine Möglichkeit, ein Volume während eines Builds bereitzustellen, jedoch ohne Docker-Dateien.

Die Technik besteht darin , einen Container aus einer beliebigen Basis zu erstellen, die Sie verwenden möchten (Mounten Ihrer Volumes in den Container mit der -vOption), ein Shell-Skript auszuführen, um Ihre Image- Erstellung durchzuführen , und dann den Container festzuschreiben als Image festzuschreiben, wenn Sie fertig sind .

Dadurch werden nicht nur die überschüssigen Dateien weggelassen, die Sie nicht möchten (dies gilt auch für sichere Dateien wie SSH-Dateien), sondern es wird auch ein einzelnes Image erstellt. Es hat Nachteile: Der Befehl commit unterstützt nicht alle Dockerfile-Anweisungen und lässt Sie nicht abholen, wenn Sie aufgehört haben, wenn Sie Ihr Build-Skript bearbeiten müssen.

AKTUALISIEREN:

Beispielsweise,

CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c '/bin/sh /build.sh'
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID
Keith Mason
quelle
6
+1 Könnten Sie bitte die Anweisungen im 2. Absatz etwas näher erläutern? Wenn zum Beispiel die Basis debian:wheezyund das Shell-Skript ist build.sh, welche spezifischen Anweisungen würde man verwenden?
Drux
6

Während Sie den Container ausführen, wird ein Verzeichnis auf Ihrem Host erstellt und in den Container eingebunden. Sie können herausfinden, in welchem ​​Verzeichnis sich dies befindet

$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]

Wenn Sie ein Verzeichnis von Ihrem Host in Ihrem Container bereitstellen möchten, müssen Sie den -vParameter verwenden und das Verzeichnis angeben. In Ihrem Fall wäre dies:

docker run -v /export:/export data

Sie würden also den Hosts-Ordner in Ihrem Container verwenden.

Behe
quelle
1
Vielen Dank. Frage überarbeitet. Die eigentliche Frage, die ich lösen möchte, lautet: Wie werden Host-Volumes während des Builds in Docker-Containern in Dockerfile bereitgestellt? Vielen Dank.
xpt
Bitte überarbeiten Sie Ihre Fragen nicht so drastisch . Dies macht meine Frage ungültig, obwohl sie vor Ihren Änderungen vollkommen gültig war. Stellen Sie stattdessen eine neue Frage.
Behe
11
Ursprüngliche Frage : Wie verwende ich die VOLUME-Anweisung in Dockerfile? Es steht auch heute noch ganz am Anfang der Frage. Ihre Antwort war für die Laufzeit , und meine Frage war immer über die Erstellungszeit , wofür Dockerfile ist.
xpt
4

Ich denke, Sie können tun, was Sie wollen, indem Sie den Build über einen Docker-Befehl ausführen, der selbst in einem Docker-Container ausgeführt wird. Siehe Docker kann jetzt in Docker | ausgeführt werden Docker Blog . Eine Technik wie diese, die jedoch tatsächlich mit einem Container auf den äußeren Docker zugegriffen hat, wurde verwendet, z. B. während untersucht wurde, wie der kleinstmögliche Docker-Container erstellt werden kann Xebia Blog .

Ein weiterer relevanter Artikel ist Optimieren von Docker-Images | CenturyLink Labs erklärt, dass Sie, wenn Sie während eines Builds Inhalte herunterladen, vermeiden können, dass im endgültigen Image Speicherplatz verschwendet wird, indem Sie den Download in einem RUN-Schritt herunterladen, erstellen und löschen.

nealmcb
quelle
3

Es ist hässlich, aber ich habe einen Anschein davon erreicht:

Dockerfile:

FROM foo
COPY ./m2/ /root/.m2
RUN stuff

imageBuild.sh:

docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"

Ich habe einen Java-Build, der das Universum in /root/.m2 herunterlädt, und das jedes Mal . imageBuild.shKopiert den Inhalt dieses Ordners nach dem Build auf den Host und Dockerfilekopiert ihn für den nächsten Build zurück in das Image.

Dies ist ungefähr so, wie ein Volume funktionieren würde (dh es bleibt zwischen den Builds bestehen).

MatrixManAtYrService
quelle
Dies ist eine praktikable Lösung für die Docker-basierte kontinuierliche Integration, auch bekannt als CI. Richten Sie die Bibliotheken und Compiler ein und führen Sie make über Dockerfile-Befehle aus, starten Sie das Image einfach, um einen Container zu erstellen, und kopieren Sie schließlich das gewünschte Artefakt wie eine .deb. Scheint zu funktionieren, danke, dass du das gepostet hast.
Chrisinmtown
Diese Lösung hinterlässt ein Bild mit ALLEN Dateien in ./m2/ - das, das Sie benötigen und das, das Sie nicht benötigen - und dies kann zu RIESIGEN Produktionsbildern führen, was nicht erwünscht ist! Beim Mounten in ein externes Abhängigkeitsverzeichnis werden nur die erforderlichen Dateien in das Image kopiert.
Marko Krajnc
Wenn Sie beabsichtigen, das Bild zu veröffentlichen, ist es wahrscheinlich am besten, einfach zu warten und maven jedes Mal seine eigenen Abhängigkeiten erneut herunterladen zu lassen. Dieser Hack ist nur dann sinnvoll, wenn Sie ein Bild zum Testen bereitstellen - ein Bild, mit dem Endbenutzer niemals in Kontakt kommen.
MatrixManAtYrService
1

Hier ist eine vereinfachte Version des 2-Schritt-Ansatzes mit Build und Commit ohne Shell-Skripte. Es involviert:

  1. Erstellen Sie das Bild teilweise ohne Volumes
  2. Ausführen eines Containers mit Volumes , Vornehmen von Änderungen, Festschreiben des Ergebnisses und Ersetzen des ursprünglichen Bildnamens.

Bei relativ geringen Änderungen verlängert der zusätzliche Schritt die Erstellungszeit nur um wenige Sekunden.

Grundsätzlich:

docker build -t image-name . # your normal docker build

# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command

# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)   
docker commit --change="CMD bash" temp-container image-name 

# Delete the temporary container:
docker rm temp-container

In meinem Anwendungsfall möchte ich eine maven toolchains.xml-Datei vorgenerieren, aber meine vielen JDK-Installationen befinden sich auf einem Volume, das erst zur Laufzeit verfügbar ist. Einige meiner Bilder sind nicht mit allen JDKS kompatibel, daher muss ich die Kompatibilität beim Erstellen testen und die Datei toolchains.xml unter bestimmten Bedingungen füllen. Beachten Sie, dass das Image nicht portabel sein muss. Ich veröffentliche es nicht in Docker Hub.

Akom
quelle
1

Wie viele bereits geantwortet haben, ist das Mounten von Host-Volumes während des Builds nicht möglich. Ich möchte nur einen docker-composeWeg hinzufügen , ich denke, es wird schön sein, vor allem für die Entwicklung / Testnutzung

Dockerfile

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999

docker-compose.yml

version: '3'
services:
  test-service:
    image: test/image
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./export:/app/export
      - ./build:/app/build

Und lassen Sie Ihren Container vorbeifahren docker-compose up -d --build

Jegor Zaremba
quelle