Wie kann ich Dateien außerhalb des Build-Kontexts von Docker einschließen?

462

Wie kann ich Dateien von außerhalb des Docker-Build-Kontexts mit dem Befehl "ADD" in die Docker-Datei aufnehmen?

Aus der Docker-Dokumentation:

Der Pfad muss sich im Kontext des Builds befinden. Sie können nicht hinzufügen ../something/something, da der erste Schritt eines Docker-Builds darin besteht, das Kontextverzeichnis (und die Unterverzeichnisse) an den Docker-Daemon zu senden.

Ich möchte nicht mein gesamtes Projekt umstrukturieren, nur um Docker in dieser Angelegenheit unterzubringen. Ich möchte alle meine Docker-Dateien im selben Unterverzeichnis behalten.

Außerdem scheint Docker Symlinks noch nicht (und möglicherweise auch nie) zu unterstützen: Der Befehl Dockerfile ADD folgt nicht den Symlinks auf Host # 1676.

Das einzige andere, was mir einfällt, ist, einen Vorab-Schritt zum Kopieren der Dateien in den Docker-Build-Kontext einzuschließen (und meine Versionskontrolle so zu konfigurieren, dass diese Dateien ignoriert werden). Gibt es dafür eine bessere Problemumgehung?

ben_frankly
quelle
94
Das muss das Schlimmste an Docker sein. Aus meiner Sicht gibt es kein "Docker-Projekt". Docker ist für Versandprojekte. Es ist nur ein Werkzeug. Ich möchte nicht mein gesamtes Projekt neu erstellen müssen, um Docker aufzunehmen, .dockerignore usw. hinzuzufügen. Wer weiß am Ende des Tages, wie lange Docker dauern wird? Es wäre großartig, eine Trennung zwischen Code (dh Winkelprojekt) und den Mitteln zur Bereitstellung (dh Docker) zu haben. Schließlich hat es wirklich keinen Vorteil, eine Docker-Datei neben allem anderen zu haben. Es ist nur eine Verkabelung, um ein Bild zu erstellen :(
TigerBear
3
Ja, das ist ein großer Wermutstropfen. Ich habe das gleiche Problem und habe eine größere Binärdatei (bereits komprimiert), die ich nicht in jeden Docker-Build-Kontext kopieren möchte. Ich würde es lieber von seinem aktuellen Speicherort beziehen (außerhalb des Docker-Build-Kontexts). Und ich möchte kein Volume zur Laufzeit zuordnen, da ich versuche, die Datei beim Erstellen zu kopieren / hinzuzufügen und zu entpacken und das zu tun, was ich brauche, damit bestimmte Binärdateien in das Image eingebrannt werden. Auf diese Weise können die Behälter schnell hochgefahren werden.
Jersey Bean
Ich habe eine gute Struktur gefunden und erkläre sie mit Details unter stackoverflow.com/a/53298446/433814
Marcello de Sales
1
Das Problem bei Docker-Builds ist das erfundene Konzept des "Kontexts". Docker-Dateien reichen nicht aus, um einen Build zu definieren, es sei denn, sie befinden sich in einem strategischen Verzeichnis (auch bekannt als Kontext), dh "/" als Extrem, sodass Sie auf jeden Pfad zugreifen können (beachten Sie, dass dies in einem vernünftigen Projekt nicht das Richtige ist entweder ... und außerdem werden Docker-Builds sehr langsam, da Docker beim Start den gesamten Kontext scannt. Sie können ein Docker-Image mit allen erforderlichen Dateien erstellen FROMund von dort aus fortfahren. Ich würde die Projektstruktur nicht ändern, um Docker (oder Build-Tools) aufzunehmen.
Devis L.

Antworten:

411

Der beste Weg, dies zu umgehen, besteht darin, die Docker-Datei unabhängig vom Build-Kontext mit -f anzugeben.

Mit diesem Befehl erhält der Befehl ADD beispielsweise Zugriff auf alle Elemente in Ihrem aktuellen Verzeichnis.

docker build -f docker-files/Dockerfile .

Update : Docker erlaubt jetzt, die Docker-Datei außerhalb des Build-Kontexts zu haben (behoben in 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Sie können also auch so etwas tun

docker build -f ../Dockerfile .
Cyberwiz
quelle
8
@ Ro. Sie verwenden die dockerfile:Eigenschaft in dem build:Abschnitt in der Compose-Datei docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Ich erhalte die Meldung "Die Docker-Datei muss sich im Build-Kontext befinden". Ich hätte wirklich gerne eine Docker-Datei, die sich unter dem aktuellen Build-Kontext befinden kann. In Ihrem Beispiel befindet sich die Docker-Datei innerhalb / unterhalb des aktuellen Build-Kontexts, was natürlich funktioniert.
Alexander Mills
3
Ja, ich möchte nur eine gemeinsam genutzte Docker-Datei, die mehreren Unterverzeichnissen entspricht, die alle "Build-Kontexte" sind
Alexander Mills
51
Behebt dies das Problem des OP, ADDeine Datei außerhalb des Kontextverzeichnisses zu wollen? Das ist es, was ich versuche, aber ich glaube nicht, dass die Verwendung -fexterne Dateien addierbar macht.
Sridhar Sarnobat
18
Ich kann das nicht genug upvoten .. in meiner docker-compose.yml habe ich : build: context: .., dockerfile: dir/Dockerfile. Jetzt ist mein Build-Kontext das übergeordnete Verzeichnis!
Mike Gleason jr. Couturier
51

Ich nutze die --build-argOption oft für diesen Zweck. Zum Beispiel, nachdem Sie Folgendes in die Docker-Datei eingefügt haben:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Sie können einfach tun:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Beachten Sie jedoch die folgende Warnung aus der Docker-Dokumentation :

Warnung: Es wird nicht empfohlen, Build-Time-Variablen zum Übergeben von Geheimnissen wie Github-Schlüsseln, Benutzeranmeldeinformationen usw. zu verwenden. Build-Time-Variablenwerte sind für jeden Benutzer des Images mit dem Docker-Verlaufsbefehl sichtbar.

user2658308
quelle
6
Dies ist ein schlechter Rat ohne große Warnung. Aus der Docker-Dokumentation: "Warnung: Es wird nicht empfohlen, Build-Time-Variablen zum Übergeben von Geheimnissen wie Github-Schlüsseln, Benutzeranmeldeinformationen usw. zu verwenden. Build-Time-Variablenwerte sind für jeden Benutzer des Images mit dem Docker-Verlaufsbefehl sichtbar." [1] Mit anderen Worten, das in diesem Beispiel gegebene Beispiel offenbart den privaten SSH-Schlüssel im Docker-Image. In einigen Kontexten könnte das in Ordnung sein. docs.docker.com/engine/reference/builder/#arg
sheldonh
3
Um dieses Sicherheitsproblem zu lösen, können Sie Techniken wie Squashing oder mehrstufige Builds verwenden: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

Unter Linux können Sie andere Verzeichnisse bereitstellen, anstatt sie zu verknüpfen

mount --bind olddir newdir

Weitere Informationen finden Sie unter /superuser/842642 .

Ich weiß nicht, ob für andere Betriebssysteme etwas Ähnliches verfügbar ist. Ich habe auch versucht, mit Samba einen Ordner freizugeben und ihn erneut im Docker-Kontext bereitzustellen, was ebenfalls funktioniert hat.

Günter Zöchbauer
quelle
2
Nur root kann Verzeichnisse binden
jjcf89
28

Ich habe viel Zeit damit verbracht, ein gutes Muster herauszufinden und besser zu erklären, was mit dieser Funktionsunterstützung los ist. Mir wurde klar, dass der beste Weg, dies zu erklären, folgender war ...

  • Dockerfile: Zeigt nur Dateien unter einem eigenen relativen Pfad an
  • Kontext: Ein Ort im "Space", an den die Dateien, die Sie freigeben möchten, und Ihre Docker-Datei kopiert werden

Nach alledem ist hier ein Beispiel für die Docker-Datei, die eine aufgerufene Datei wiederverwenden muss start.sh

Dockerfile

Es ALWAYSwird von seinem relativen Pfad geladen, wobei das aktuelle Verzeichnis als localReferenz auf die von Ihnen angegebenen Pfade verwendet wird.

COPY start.sh /runtime/start.sh

Dateien

In Anbetracht dieser Idee können wir uns vorstellen, mehrere Kopien für die Docker-Dateien zu haben, die bestimmte Dinge erstellen, aber alle benötigen Zugriff auf die start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

In Anbetracht dieser Struktur und der obigen Dateien ist hier eine docker-compose.yml

docker-compose.yaml

  • In diesem Beispiel ist Ihr sharedKontextverzeichnis das runtimeVerzeichnis.
    • Gleiches mentales Modell hier, denken Sie, dass alle Dateien unter diesem Verzeichnis in das sogenannte verschoben werden context.
    • Geben Sie in ähnlicher Weise einfach die Docker-Datei an, die Sie in dasselbe Verzeichnis kopieren möchten. Sie können dies mit angeben dockerfile.
  • Das Verzeichnis, in dem sich Ihr Hauptinhalt befindet, ist der tatsächlich einzustellende Kontext.

Das docker-compose.ymlist wie folgt

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-servicewird als Kontext festgelegt, wird die gemeinsam genutzte Datei start.shdort sowie die jeweils angegebene Docker- Datei kopiert dockerfile.
  • Jeder kann auf seine eigene Weise erstellt werden und teilt die Startdatei!

Prost!

Marcello de Sales
quelle
1
Ihr Punkt auf Dockerfile ist nicht vollständig wahr, wie spitz durch die akzeptierte Antwort, wenn Sie in einer Ordnerhierarchie sind a/b/c, dann ja laufen docker build .in cwerden Sie nicht den Zugriff erlauben ../file-in-b. Aber ich denke, das allgemeine Missverständnis in diesem (oder zumindest meinem) war, dass der Kontext durch den Ort definiert wird, der durch das erste Argument des Befehls build angegeben wird, nicht durch den Ort der Docker-Datei. Also wie in der akzeptierten Antwort angegeben: von a: docker build -f a/b/c/Dockerfile . bedeutet, dass in der Docker-Datei .jetzt der Ordner ista
β.εηοιτ.βε
1
Zitieren aus den Dockerfile-Dokumenten: Pfade von Dateien und Verzeichnissen werden relativ zur Quelle des Kontextes des Builds interpretiert.
Nishant George Agrwal
18

Wenn Sie die Diskussion in der Ausgabe 2745 lesen, unterstützt Docker möglicherweise nicht nur niemals Symlinks, sondern auch niemals das Hinzufügen von Dateien außerhalb Ihres Kontexts. Es scheint eine Designphilosophie zu sein, dass Dateien, die in den Docker-Build aufgenommen werden, explizit Teil des Kontexts sein sollten oder von einer URL stammen, bei der sie vermutlich auch mit einer festen Version bereitgestellt werden, damit der Build mit bekannten URLs oder Dateien wiederholt werden kann, die mit dem geliefert werden Docker-Container.

Ich ziehe es vor, aus einer versionierten Quelle zu erstellen - dh Docker-Build-t-Zeug http://my.git.org/repo - ansonsten baue ich von einem zufälligen Ort mit zufälligen Dateien.

Grundsätzlich nein ... - SvenDowideit, Docker Inc.

Nur meine Meinung, aber ich denke, Sie sollten sich umstrukturieren, um die Code- und Docker-Repositorys zu trennen. Auf diese Weise können die Container generisch sein und zur Laufzeit eine beliebige Version des Codes abrufen, anstatt sie zu erstellen.

Alternativ können Sie Docker als grundlegendes Code-Bereitstellungsartefakt verwenden und dann die Docker-Datei im Stammverzeichnis des Code-Repositorys ablegen. Wenn Sie diesen Weg gehen, ist es wahrscheinlich sinnvoll, einen übergeordneten Docker-Container für allgemeinere Details auf Systemebene und einen untergeordneten Container für die für Ihren Code spezifische Einrichtung zu haben.

Usman Ismail
quelle
Warum dann überhaupt Docker verwenden?
lscoughlin
11

Ich glaube, die einfachere Problemumgehung wäre, den "Kontext" selbst zu ändern.

Also zum Beispiel anstatt zu geben:

docker build -t hello-demo-app .

Angenommen, Sie möchten das übergeordnete Verzeichnis als Kontext festlegen. Verwenden Sie dazu einfach Folgendes:

docker build -t hello-demo-app ..
Anshuman Manral
quelle
6
Ich denke, das bricht .dockerignore: - \
NullVoxPopuli
Ich habe .dockerignore aufgegeben und stattdessen einen von Makefile verwalteten Docker-Ordner erstellt, der nur Dateien enthält, die für den Build-Kontext benötigt werden. Ich muss nur aufrufen make buildund alle benötigten Dateien abrufen , wenn sie aktualisiert wurden. Dann wird der entsprechende Docker-Build aufgerufen. Ich muss zusätzliche Arbeit leisten, aber es funktioniert einwandfrei, weil ich die volle Kontrolle habe.
Sahsahae
5

Sie können auch einen Tarball erstellen, der zeigt, was das Bild zuerst benötigt, und diesen als Kontext verwenden.

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts

Laurent Picquet
quelle
Toller Tipp! Ich habe festgestellt, dass Sie sogar Docker füttern können, um den Tarball als Kontext auf stdin zu erstellen : tar zc /dir1 /dir2 |docker build -. Dies war in meinem Fall sehr hilfreich.
Tore Olsen
3

Mit Docker-Compose habe ich dies erreicht, indem ich einen Dienst erstellt habe, der die benötigten Volumes bereitstellt und das Image des Containers festschreibt. Im nachfolgenden Dienst verlasse ich mich dann auf das zuvor festgeschriebene Image, in dem alle Daten an bereitgestellten Speicherorten gespeichert sind. Sie müssen diese Dateien dann an ihr endgültiges Ziel kopieren, da vom Host bereitgestellte Verzeichnisse beim Ausführen eines docker commitBefehls nicht festgeschrieben werden

Sie müssen Docker-Compose nicht verwenden, um dies zu erreichen, aber es macht das Leben ein bisschen einfacher

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
Kris Rivera
quelle
1

Ich hatte das gleiche Problem mit einem Projekt und einigen Datendateien, die ich aus HIPPA-Gründen nicht in den Repo-Kontext verschieben konnte. Am Ende habe ich 2 Dockerfiles verwendet. Man erstellt die Hauptanwendung ohne das Zeug, das ich außerhalb des Containers brauchte, und veröffentlicht das im internen Repo. Dann zieht eine zweite Docker-Datei dieses Image und fügt die Daten hinzu und erstellt ein neues Image, das dann bereitgestellt und nirgendwo gespeichert wird. Nicht ideal, aber es hat funktioniert, um vertrauliche Informationen aus dem Repo herauszuhalten.

Annulet Consulting
quelle
1

Eine einfache Problemumgehung könnte darin bestehen, das Volume einfach mit dem Flag -v oder --mount in den Container einzubinden, wenn Sie es ausführen und auf diese Weise auf die Dateien zugreifen.

Beispiel:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

Weitere Informationen finden Sie unter: https://docs.docker.com/storage/volumes/

Ben Wex
quelle
Beachten Sie, dass dies nur funktioniert, wenn das Volume eine Laufzeitabhängigkeit ist. Für Build-Time-Abhängigkeiten docker runist es zu spät.
user3735633
1

Wie in diesem GitHub-Problem beschrieben, findet der Build tatsächlich statt /tmp/docker-12345, sodass ein relativer Pfad wie ../relative-add/some-filerelativ zu ist /tmp/docker-12345. Es würde also suchen /tmp/relative-add/some-file, was auch in der Fehlermeldung angezeigt wird. *

Es ist nicht gestattet, Dateien von außerhalb des Build-Verzeichnisses einzuschließen, daher wird die Meldung "Verbotener Pfad" angezeigt.

Rocksn17
quelle
0

Eine schnelle und schmutzige Möglichkeit besteht darin, den Build-Kontext so viele Ebenen einzurichten, wie Sie benötigen - dies kann jedoch Konsequenzen haben. Wenn Sie in einer Microservices-Architektur arbeiten, die folgendermaßen aussieht:

./Code/Repo1
./Code/Repo2
...

Sie können den Build-Kontext auf das übergeordnete CodeVerzeichnis setzen und dann auf alles zugreifen. Es stellt sich jedoch heraus, dass bei einer großen Anzahl von Repositorys der Build sehr lange dauern kann.

Eine Beispielsituation könnte sein, dass ein anderes Team ein Datenbankschema verwaltet Repo1und der Code Ihres Teams Repo2davon abhängt. Sie möchten diese Abhängigkeit mit einigen Ihrer eigenen Seed-Daten andocken, ohne sich Gedanken über Schemaänderungen machen oder das Repository des anderen Teams verschmutzen zu müssen (je nachdem, welche Änderungen Sie vornehmen müssen, müssen Sie möglicherweise noch Ihre Seed-Daten-Skripte ändern). Der zweite Ansatz ist zwar hackig umgeht das Problem der langen Builds:

Erstellen Sie ein sh- (oder ps1-) Skript, ./Code/Repo2um die benötigten Dateien zu kopieren, und rufen Sie die gewünschten Docker-Befehle auf, zum Beispiel:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

Legen Sie in der Docker-Compose-Datei einfach den Kontext als Repo2root fest und verwenden Sie den Inhalt des ./db/schemaVerzeichnisses in Ihrer Docker-Datei, ohne sich um den Pfad zu kümmern. Bedenken Sie, dass Sie das Risiko eingehen, dieses Verzeichnis versehentlich der Quellcodeverwaltung zuzuweisen. Die Bereinigungsaktionen für Skripte sollten jedoch einfach genug sein.

user1007074
quelle
0

In meinem Fall ist meine Docker-Datei wie eine Vorlage geschrieben, die Platzhalter enthält, die ich mithilfe meiner Konfigurationsdatei durch einen echten Wert ersetze.

Daher konnte ich diese Datei nicht direkt angeben, sondern sie wie folgt in den Docker-Build einfügen:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Aber wegen der Pipe COPYfunktionierte der Befehl nicht. Aber der obige Weg löst es durch -f -(explizit sagen Datei nicht bereitgestellt). Wenn Sie nur -auf das -fFlag verzichten, werden der Kontext UND die Docker-Datei nicht bereitgestellt, was eine Einschränkung darstellt.

Daniel Katz
quelle
0

Der Trick besteht darin, zu erkennen, dass Sie den Kontext im Build-Befehl angeben können, um Dateien aus dem übergeordneten Verzeichnis einzuschließen, wenn Sie den Docker-Pfad angeben. Ich würde meine Docker-Datei so ändern, dass sie folgendermaßen aussieht:

...
COPY ./ /dest/
...

Dann kann mein Build-Befehl folgendermaßen aussehen:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Aus dem Projektverzeichnis

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Vom Projekt / Docker

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
quelle