it-swarm.com.de

Bereitstellen von Host-Volumes in Docker-Containern in Dockerfile während der Erstellung

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

Die eigentliche Frage, die ich lösen möchte, ist, wie Host-Volumes während des Builds in Docker-Container in Dockerfile eingebunden werden, d. H. Mit der Funktion docker run -v /export:/export während 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 einzelnen Docker gesperrt sind, sondern dass sie freigegeben/wiederverwendet werden. Das ist der Hauptgrund, warum ich diese Frage stelle.

Letztes Update:

Vor Docker v18.09 sollte die richtige Antwort die sein, die mit Folgendem beginnt:

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

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

Einen passenden Cacher-ng-Service andocken
https://docs.docker.com/engine/examples/apt-cacher-ng/

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

Eine andere Möglichkeit ist die neu akzeptierte Antwort ​​, z. B. das Buildkit in Version 18.09.

Suchen Sie sich aus, was zu Ihnen passt.


Was: Es gab eine Lösungswippe, die nicht von Docker stammte, aber jetzt, da die Wippe nicht mehr angeboten wird, kehre ich die Antwort auf - zurück. "Nicht möglich" erneut.


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 die Portabilität für Docker-Entwickler von größter Bedeutung ist. Aber als Docker-Benutzer muss ich sagen, dass ich über dieses fehlende Feature sehr enttäuscht bin. Lassen Sie mich mein Argument mit einem Zitat aus der oben genannten Diskussion abschließen: "Ich möchte Gentoo als Basis-Image verwenden, möchte aber definitiv nicht, dass> 1 GB Portage-Tree-Daten in einer der Ebenen sind, sobald das Image vorliegt Sie könnten einige kompakte Nice-Container haben, wenn nicht der gigantische Portage-Tree 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 nur aus Gründen der Portabilität jedes Mal, wenn ich ein Gentoo-Basis-Image erstelle, mehr als 1 GB Portage Tree herunterladen muss, ist weder effizient noch benutzerfreundlich. Außerdem befindet sich das Paket-Repository IMMER unter/usr/portage und ist daher unter Gentoo IMMER TRAGBAR. Auch hier respektiere ich die Entscheidung, erlaube mir jedoch, meine Enttäuschung in der Zwischenzeit zum Ausdruck zu bringen. Vielen Dank.


Ursprüngliche Frage im Detail:

Von

Verzeichnisse über Volumes freigeben
http://docker.readthedocs.org/de/v0.7.3/use/working_with_volumes/

es heißt, dass die Funktion "Datenvolumen" seit Version 1 der Docker Remote-API verfügbar ist. Mein Docker hat die 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"]

Was ist die richtige Methode in Dockerfile, um vom Host gemountete Volumes über den Befehl VOLUME in Docker-Containern zu mounten?

$ 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
205
xpt

Um zu antworten: "Warum funktioniert VOLUME nicht?" Wenn Sie in der Docker-Datei ein VOLUME 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 Befehl RUN bereitgestellt, mit dem Inhalt des Abbilds vorab ausgefüllt und dann am Ende des Befehls RUN verworfen. Es werden nur Änderungen am Container gespeichert, keine Änderungen am Volume.


Seitdem diese Frage gestellt wurde, wurden einige Funktionen veröffentlicht, die möglicherweise Abhilfe schaffen. Der erste Schritt sind mehrstufige Builds, mit denen Sie einen ineffizienten Speicherplatz in der ersten Stufe erstellen und nur die benötigte Ausgabe in die endgültige Stufe kopieren können, die Sie versenden. Das zweite Feature ist Buildkit, das die Art und Weise, wie Images erstellt werden, dramatisch verändert und dem Build neue Funktionen hinzufügt.

Bei einem mehrstufigen Build würden Sie mehrere FROM Zeilen haben, von denen jede die Erstellung eines separaten Images startet. Standardmäßig ist nur das letzte Bild mit Tags versehen, Sie können jedoch Dateien aus früheren Phasen kopieren. Standardmäßig wird eine Compilerumgebung zum Erstellen eines binären oder anderen Anwendungsartefakts und eine Laufzeitumgebung als zweite Stufe verwendet, die 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 Verzeichnis/export enthält.


Das Buildkit wird am 18.09. Aus dem Experiment genommen. Es ist eine komplette Neugestaltung des Build-Prozesses, einschließlich der Möglichkeit, den Frontend-Parser zu ändern. Eine dieser Parser-Änderungen hat die Option RUN --mount implementiert, mit der Sie ein Cache-Verzeichnis für Ihre Ausführungsbefehle bereitstellen können. Z.B. Hier ist eine, die einige der Debian-Verzeichnisse einbindet (bei 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, den Sie haben, z. $ HOME/.m2 für Maven oder /root/.cache für Golang.


TL; DR: Die Antwort ist hier: Mit dieser RUN --mount -Syntax können Sie auch schreibgeschützte Mount-Verzeichnisse aus dem Build-Kontext binden. Der Ordner muss im Build-Kontext vorhanden sein und wird weder dem Host noch dem Build-Client zugeordnet:

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

Beachten Sie, dass das Verzeichnis aus dem Kontext bereitgestellt wird und schreibgeschützt ist. Sie können keine Änderungen auf den Host oder Client zurückschreiben. Wenn Sie erstellen, möchten Sie ein Build-Kit mit 18.09 oder neuer installieren und mit export DOCKER_BUILDKIT=1 aktivieren.

21
BMitch

Es ist nicht möglich, die Anweisung VOLUME zu verwenden, um Docker mitzuteilen, was eingehängt werden soll. Das würde die Portabilität ernsthaft beeinträchtigen. Diese Anweisung teilt Docker mit, dass Inhalte in diesen Verzeichnissen nicht in Bildern gespeichert werden und von anderen Containern aus mit dem Befehlszeilenparameter --volumes-from aufgerufen werden können. Sie müssen den Container mit -v /path/on/Host:/path/in/container ausführen, um vom Host aus auf Verzeichnisse zuzugreifen.

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

109
Andreas Steffan

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

GUTE NACHRICHTEN, es gibt jetzt einen Weg -

Die Lösung lautet 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 zielt Rocker darauf ab, die folgenden Anwendungsfälle zu lösen, die bei einfachem Docker schmerzhaft sind:

  1. Stellen Sie wiederverwendbare Volumes auf der Build-Stufe bereit, sodass Tools für die Abhängigkeitsverwaltung 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 ist diese Logik in einer einzigen Docker-Datei enthalten.
  4. Markieren/Verschieben von Bildern direkt aus Dockerdateien.
  5. Übergeben Sie Variablen aus dem Shell-Build-Befehl, damit sie durch eine Docker-Datei ersetzt werden können.

Und mehr. Dies sind die wichtigsten Probleme, die unsere Übernahme von Docker bei Grammarly blockiert haben.

Update: Rocker wurde eingestellt, laut offiziellem Projekt-Repo auf Github

Ab Anfang 2018 ist das Container-Ökosystem viel ausgereifter als vor drei Jahren, als dieses Projekt gestartet wurde. Jetzt können einige der kritischen und herausragenden Funktionen von Rocker leicht durch Docker-Builds 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 .

63
xpt

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

Die Technik wäre, einen Container zu erstellen von jeder beliebigen Basis aus, die Sie verwenden möchten (Mounten Ihrer Volumes im Container mit der Option -v), ein Shell-Skript auszuführen, um Ihr Image zu erstellen Bauarbeiten, dann Festschreiben des Containers als Bild, wenn fertig.

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

AKTUALISIEREN:

Zum Beispiel,

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
14
Keith Mason

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 das befindet

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

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

docker run -v /export:/export data

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

5
Behe

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 | Docker Blog ausgeführt werden . Eine Technik wie diese, die jedoch tatsächlich über einen Container auf den äußeren Docker zugegriffen hat, wurde beispielsweise verwendet, während untersucht wurde, wie Erstellen des kleinstmöglichen Docker-Containers | Xebia Blog .

Ein weiterer relevanter Artikel ist Optimieren von Docker-Images | CenturyLink Labs , der erklärt, dass Sie durch Herunterladen, Erstellen und Löschen vermeiden können, dass Speicherplatz im endgültigen Image verschwendet wird, wenn Sie Dinge während eines Builds herunterladen der Download alles in einem RUN-Schritt.

4
nealmcb

Es ist hässlich, aber ich habe so etwas 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 dies tat jedes Mal. imageBuild.sh kopiert den Inhalt dieses Ordners nach dem Build auf den Host, und Dockerfile kopiert ihn für den nächsten Build zurück in das Image.

Dies ist ungefähr so, wie ein Volume funktionieren würde (d. H. Es bleibt zwischen Builds bestehen).

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

  1. Bild teilweise erstellen, ohne Volumes
  2. Ausführen eines Containers mit Volumes, Vornehmen von Änderungen, Festschreiben des Ergebnisses und Ersetzen des ursprünglichen Image-Namens.

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

Grundsätzlich gilt:

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 Images sind nicht mit allen JDKS kompatibel. Daher muss ich die Kompatibilität zum Zeitpunkt der Erstellung testen und toolchains.xml unter bestimmten Bedingungen ausfüllen. Beachten Sie, dass das Image nicht portabel sein muss. Ich veröffentliche es nicht in Docker Hub.

0
Akom