Bedingtes KOPIEREN / HINZUFÜGEN in Dockerfile?

102

Innerhalb meiner Docker-Dateien möchte ich eine Datei in mein Bild kopieren, falls vorhanden. Die Datei "require.txt" für pip scheint ein guter Kandidat zu sein, aber wie würde dies erreicht werden?

COPY (requirements.txt if test -e requirements.txt; fi) /destination
...
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

oder

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi
derrend
quelle
Bitte sehen Sie hier: docs.docker.com/reference/builder
Tuan
4
@Tuan - Was genau an diesem Link hilft dabei?
ToolmakerSteve

Antworten:

24

Dies wird derzeit nicht unterstützt (da ich vermute, dass dies zu einem nicht reproduzierbaren Bild führen würde, da dieselbe Docker-Datei die Datei je nach Existenz kopieren würde oder nicht).

Dies wird in Ausgabe 13045 weiterhin mithilfe von Platzhaltern angefordert : " COPY foo/* bar/" not work if no file in foo" (Mai 2015).
Es wird vorerst (Juli 2015) nicht in Docker implementiert, aber ein anderes Build-Tool wie Bocker könnte dies unterstützen.

VonC
quelle
32
Gute Antwort, aber die Docker-Logik IMO ist fehlerhaft. Wenn Sie dieselbe Docker-Datei mit einem anderen Build-Kontext ausführen, erhalten Sie ein anderes Image. das ist zu erwarten. Wenn Sie denselben Build-Kontext verwenden, erhalten Sie dasselbe Image. und wenn Sie bedingte COPY / ADD-Anweisungen in denselben Build-Kontext einfügen, erhalten Sie dasselbe Image. so dass auscheckt. Das sind nur meine 2 Cent.
Nathan G
Bei Docker geht es um unveränderliche Infrastruktur. Die Entwicklung, das Staging und das Produkt Ihrer Umgebung sollten zu 99,99% so nah wie möglich sein, wenn nicht identisch. Verwenden Sie Umgebungsvariablen.
Andrew McLagan
3
@AndrewMcLagan Was ist, wenn beispielsweise eine Front-End- devUmgebung mit einem Webpack-Entwicklungsserver ausgeführt wird und die entsprechende prodUmgebung mit einem /diststatischen Ordner funktioniert ? Dies ist heute bei den meisten Front-End-Setups der Fall devund prodkann hier offensichtlich nicht dasselbe sein. Wie soll man damit umgehen?
Jivan
Ich verwende Docker nicht, um meine Knoten-Frontends zu entwickeln. Das normale Webpack localhost: 3000 etc ... Obwohl Ihre lokale Docker-Entwicklungsumgebung immer noch gestartet wird, kommuniziert Ihr Node / React / Angular-Frontend mit dem, was in Ihrer normalen Docker-Container-Umgebung ausgeführt wird. ZB APIs, Redis, MySQL, Mongo, elastische Suche und andere Mikrodienste. Sie könnten eine Webpack-Entwicklungsumgebung in einem Container ausführen. Aber ich
denke
@Jivan Wie wäre es, wenn Sie ein Onbuild-Image verwenden, um die allgemeinen Anweisungen zu definieren und dann bestimmte Images für Entwickler und Produkt zu erstellen. Das Docker Hub Node-Repo scheint Onbuild-Images für jede Node-Version zu enthalten: hub.docker.com/_/node . Oder vielleicht könnten Sie Ihre eigenen rollen.
David_i_smith
82

Hier ist eine einfache Problemumgehung:

COPY foo file-which-may-exist* /target

Stellen Sie sicher foo, dass vorhanden ist, da COPYmindestens eine gültige Quelle benötigt wird.

Wenn file-which-may-existvorhanden, wird es auch kopiert.

HINWEIS: Sie sollten darauf achten, dass Ihr Platzhalter keine anderen Dateien aufnimmt, die Sie nicht kopieren möchten. Um vorsichtiger zu sein, können Sie file-which-may-exist?stattdessen verwenden ( ?entspricht nur einem einzelnen Zeichen).

Oder noch besser, verwenden Sie eine Zeichenklasse wie diese, um sicherzustellen, dass nur eine Datei abgeglichen werden kann:

COPY foo file-which-may-exis[t] /target
jdhildeb
quelle
1
Können Sie dasselbe mit einem Ordner tun?
Benjamin Toueg
1
@BenjaminToueg: Ja, gemäß den Dokumenten können Sie sowohl Dateien als auch Ordner kopieren.
JDhildeb
2
Das funktioniert super. Bei Dateien mit mehreren Zielen habe ich in ein temporäres Verzeichnis kopiert und sie dann an den gewünschten Ort verschoben. COPY --from=docker /usr/bin/docker /usr/lib/libltdl.so* /tmp/docker/ RUN mv /tmp/docker/docker /usr/bin/docker RUN mv /tmp/docker/libltdl.so.7 /usr/lib/libltdl.so.7 || true(wo die gemeinsam genutzte Bibliothek die unbekannte Entität ist.)
Adam K Dean
Beim Kopieren mehrerer vorhandener Dateien muss das Ziel ein Verzeichnis sein. Wie funktioniert das, wenn sowohl foo als auch Ihre möglicherweise vorhandene Datei * existieren?
Melchoir55
1
Die Antwort lautet also "Stellen Sie sicher, dass eine Datei vorhanden ist" und anschließend eine Demonstration zur Verwendung des COPY-Operators. Ich verstehe nicht, wie dies mit der ursprünglichen Frage zusammenhängt.
Derrend
27

Wie in diesem Kommentar angegeben , kopiert die Antwort von Santhosh Hirekerur die Datei weiterhin. Um eine echte bedingte Kopie zu archivieren, können Sie diese Methode verwenden.

ARG BUILD_ENV=copy

FROM alpine as build_copy
ONBUILD COPY file /file

FROM alpine as build_no_copy
ONBUILD RUN echo "I don't copy"

FROM build_${BUILD_ENV}
# other stuff

Die ONBUILDAnleitung stellt sicher, dass die Datei nur kopiert wird, wenn der "Zweig" von der ausgewählt wird BUILD_ENV. Stellen Sie diese Variable vor dem Aufruf mit einem kleinen Skript eindocker build

Siyu
quelle
2
Ich mag diese Antwort, weil sie mir nicht nur die Augen für ONBUILD geöffnet hat, was sehr praktisch ist, sondern auch am einfachsten in andere übergebene Variablen zu integrieren scheint, z. B. wenn Sie das Tag basierend auf BUILD_ENV festlegen oder einen Status in speichern möchten ENV.
DeusXMachina
Ich habe gerade so etwas versucht und bekam: Fehlerantwort vom Daemon: Dockerfile Parse Fehlerzeile 52: Ungültiger Name für Build-Phase: "site_builder _ $ {host_env}", Name kann nicht mit einer Zahl beginnen oder Symbole enthalten
paulecoyote
9

Lösung umgehen

Ich musste FOLDER basierend auf ENV-Variablen auf den Server kopieren. Ich habe das leere Server-Image aufgenommen. Die erforderliche Bereitstellungsordnerstruktur wurde im lokalen Ordner erstellt. dann unter der Zeile zu DockerFile hinzugefügt, kopieren Sie den Ordner in den Container. In der letzten Zeile wurde ein Einstiegspunkt hinzugefügt, um init file.sh auszuführen, bevor Docker den Server startet.

#below lines added to integrate testing framework
RUN mkdir /mnt/conf_folder
ADD install /mnt/conf_folder/install
ADD install_test /mnt/conf_folder/install_test
ADD custom-init.sh /usr/local/bin/custom-init.sh
ENTRYPOINT ["/usr/local/bin/custom-init.sh"]

Erstellen Sie dann die Datei custom-init.sh in lokaler Sprache mit einem Skript wie dem folgenden

#!/bin/bash
if [ "${BUILD_EVN}" = "TEST" ]; then
    cp -avr /mnt/conf_folder/install_test/* /mnt/wso2das-3.1.0/
else
    cp -avr /mnt/conf_folder/install/* /mnt/wso2das-3.1.0/
fi;

In der Docker-Compose- Datei unter den Zeilen.

Umgebung: - BUILD_EVN = TEST

Diese Änderungen kopieren den Ordner während des Docker-Builds in den Container. Wenn wir Docker-Compose ausführen , kopieren Sie den tatsächlich erforderlichen Ordner oder stellen Sie ihn auf dem Server bereit, bevor der Server gestartet wird.

Santhosh Hirekerur
quelle
8
Docker-Bilder sind jedoch geschichtet. ADD würde diese in das Bild kopieren, unabhängig von der if-Anweisung, die Sie erwähnt haben ...
MyUserInStackOverflow
@MyUserInStackOverflow - Ich denke, die Idee dieser "Problemumgehung" ist, dass sowohl install als auch install_test in das Image kopiert werden, aber wenn das Image ausgeführt wird, wird nur einer dieser Ordner an den endgültigen Speicherort kopiert. Wenn es in Ordnung ist, dass sich beide irgendwo im Bild befinden, könnte dies eine vernünftige Technik sein.
ToolmakerSteve
4

Kopieren Sie alle Dateien in ein Wegwerfverzeichnis, wählen Sie die gewünschte aus und verwerfen Sie den Rest.

COPY . /throwaway
RUN cp /throwaway/requirements.txt . || echo 'requirements.txt does not exist'
RUN rm -rf /throwaway

Sie können etwas Ähnliches erreichen, indem Sie Build-Phasen verwenden, die auf derselben Lösung basieren und cpzum bedingten Kopieren verwendet werden. Wenn Sie eine Erstellungsphase verwenden, enthält Ihr endgültiges Image nicht den gesamten Inhalt der ursprünglichen Phase COPY.

FROM alpine as copy_stage
COPY . .
RUN mkdir /dir_for_maybe_requirements_file
RUN cp requirements.txt /dir_for_maybe_requirements_file &>- || true

FROM alpine
# Must copy a file which exists, so copy a directory with maybe one file
COPY --from=copy_stage /dir_for_maybe_requirements_file /
RUN cp /dir_for_maybe_requirements_file/* . &>- || true
CMD sh
cdosborn
quelle
Dies löst zwar technisch das Problem, verringert jedoch nicht die Größe des Bildes. Wenn Sie versuchen, etwas Riesiges bedingt zu kopieren (z. B. ein Deep-Network-Modell), erhöhen Sie die Größe des Bilds aufgrund der Funktionsweise von Overlay fs immer noch.
DeusXMachina
@DeusXMachina, welche Docker-Version verwenden Sie? Die Dokumente widersprechen Ihren Aussagen docs.docker.com/develop/develop-images/multistage-build/… . Die Schichten sollten nicht aus einer nicht endgültigen Bauphase erhalten bleiben.
cdosborn
@cdosburn - Ich habe dies am 18.09 beobachtet. Ich habe hauptsächlich über das erste Beispiel gesprochen. Inszenierte Builds sollten dieses Problem vermeiden. Und ich denke, jede FROM-Stufe verdichtet sich jetzt, aber ich muss meine Erinnerung erraten. Ich werde mit einigen Dingen experimentieren müssen.
DeusXMachina
@DeusXMachina, richtig, nur die zweite Lösung reduziert die Bildgröße.
cdosborn
Das ist eine gute Lösung für meinen Fall. Ich kopiere einen cacheund je nachdem, was der Cache ist, wähle ich, was in Skriptdateien zu tun ist!
Paschalis
1

Versuchte die anderen Ideen, aber keine erfüllte unsere Anforderung. Die Idee ist, ein Basis-Nginx-Image für untergeordnete statische Webanwendungen zu erstellen. Aus Sicherheits-, Optimierungs- und Standardisierungsgründen muss das Basisimage RUNBefehle für Verzeichnisse enthalten können, die von untergeordneten Images hinzugefügt wurden. Das Basisbild steuert nicht, welche Verzeichnisse von untergeordneten Bildern hinzugefügt werden. Angenommen, untergeordnete Bilder werden COPYirgendwo unter Ressourcen gespeichert COMMON_DEST_ROOT.

Dieser Ansatz ist ein Hack, aber die Idee ist, dass das Basis-Image COPYAnweisungen für 1 bis N Verzeichnisse unterstützt, die vom untergeordneten Image hinzugefügt werden. ARG PLACEHOLDER_FILEund ENV UNPROVIDED_DESTwerden verwendet , um zu erfüllen <src>und <dest>Anforderungen für jede COPYAnweisung nicht erforderlich.

#
# base-image:01
#
FROM nginx:1.17.3-alpine
ENV UNPROVIDED_DEST=/unprovided
ENV COMMON_DEST_ROOT=/usr/share/nginx/html
ONBUILD ARG PLACEHOLDER_FILE
ONBUILD ARG SRC_1
ONBUILD ARG DEST_1
ONBUILD ARG SRC_2
ONBUILD ARG DEST_2
ONBUILD ENV SRC_1=${SRC_1:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_1=${DEST_1:-${UNPROVIDED_DEST}}
ONBUILD ENV SRC_2=${SRC_2:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_2=${DEST_2:-${UNPROVIDED_DEST}}

ONBUILD COPY ${SRC_1} ${DEST_1}
ONBUILD COPY ${SRC_2} ${DEST_2}

ONBUILD RUN sh -x \
    #
    # perform operations on COMMON_DEST_ROOT
    #
    && chown -R limited:limited ${COMMON_DEST_ROOT} \
    #
    # remove the unprovided dest
    #
    && rm -rf ${UNPROVIDED_DEST}

#
# child image
#
ARG PLACEHOLDER_FILE=dummy_placeholder.txt
ARG SRC_1=app/html
ARG DEST_1=/usr/share/nginx/html/myapp
FROM base-image:01

Diese Lösung weist offensichtliche Mängel auf, wie den Dummy PLACEHOLDER_FILEund die fest codierte Anzahl der unterstützten COPY-Anweisungen. Es gibt auch keine Möglichkeit, die in der COPY-Anweisung verwendeten ENV-Variablen zu entfernen.

brianNotBob
quelle