Moby: Dockerfile COPY with file globs kopiert Dateien aus Unterverzeichnissen in das Zielverzeichnis

Erstellt am 26. Aug. 2015  ·  54Kommentare  ·  Quelle: moby/moby

Problembeschreibung:
Wenn Sie COPY in einem Dockerfile verwenden und Globs zum Kopieren von Dateien und Ordnern verwenden, kopiert Docker (manchmal?) auch Dateien aus Unterordnern in den Zielordner.

$ docker version
Client:
 Version:      1.8.1
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   d12ea79
 Built:        Thu Aug 13 19:47:52 UTC 2015
 OS/Arch:      darwin/amd64

Server:
 Version:      1.8.0
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   0d03096
 Built:        Tue Aug 11 17:17:40 UTC 2015
 OS/Arch:      linux/amd64

$ docker info
Containers: 26
Images: 152
Storage Driver: aufs
 Root Dir: /mnt/sda1/var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 204
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 4.0.9-boot2docker
Operating System: Boot2Docker 1.8.0 (TCL 6.3); master : 7f12e95 - Tue Aug 11 17:55:16 UTC 2015
CPUs: 4
Total Memory: 3.858 GiB
Name: dev
ID: 7EON:IEHP:Z5QW:KG4Z:PG5J:DV4W:77S4:MJPX:2C5P:Z5UY:O22A:SYNK
Debug mode (server): true
File Descriptors: 42
Goroutines: 95
System Time: 2015-08-26T17:17:34.772268259Z
EventsListeners: 1
Init SHA1:
Init Path: /usr/local/bin/docker
Docker Root Dir: /mnt/sda1/var/lib/docker
Username: jfchevrette
Registry: https://index.docker.io/v1/
Labels:
 provider=vmwarefusion

$ uname -a
Darwin cerberus.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64

Umgebungsdetails:
Lokales Setup auf OSX /w boot2docker, erstellt mit docker-machine

Wie zu reproduzieren:

Kontext

$ tree
.
├── Dockerfile
└── files
    ├── dir
    │   ├── dirfile1
    │   ├── dirfile2
    │   └── dirfile3
    ├── file1
    ├── file2
    └── file3

Dockerfile

FROM busybox

RUN mkdir /test
COPY files/* /test/

Tatsächliche Ergebnisse

$ docker run -it copybug ls -1 /test/
dirfile1
dirfile2
dirfile3
file1
file2
file3

erwartete Ergebnisse
Das resultierende Bild sollte die gleiche Verzeichnisstruktur aus dem Kontext haben

arebuilder

Hilfreichster Kommentar

Machen Sie einen neuen Befehls-CP und machen Sie es diesmal bitte richtig.

Alle 54 Kommentare

Die ursprüngliche Nachricht wurde mit der Ausgabe von docker info und uname -a aktualisiert und neu formatiert, sodass sie der Vorlage für die Problemberichterstattung entspricht.

Ich hatte dies auf 1.6.2 und 1.8
https://gist.github.com/jrabbit/e4f864ca1664ec0dd288 Verzeichnisse der zweiten Ebene werden aus irgendeinem Grund so behandelt wie Verzeichnisse der ersten Ebene?

Für diejenigen, die googeln: Wenn Sie Probleme mit COPY * /src haben, versuchen Sie es mit COPY / /src

@jfchevrette Ich glaube, ich weiß, warum das passiert.
Sie haben COPY files/* /test/ , das zu COPY files/dir files/file1 files/file2 files/file /test/ erweitert wird. Wenn Sie dies in einzelne COPY-Befehle aufteilen (z. B. COPY files/dir /test/ ), werden Sie sehen, dass COPY (im Guten wie im Schlechten) den Inhalt jedes Arg-Verzeichnisses in das Zielverzeichnis kopiert. Nicht das arg-Verzeichnis selbst, sondern der Inhalt. Wenn Sie eine dritte Ebene von Verzeichnissen hinzugefügt haben, wette ich, dass diese erhalten bleiben.

Ich bin nicht begeistert von der Tatsache, dass COPY das Verzeichnis der obersten Ebene nicht beibehält, aber das ist schon eine Weile so.

Sie können versuchen, dies weniger schmerzhaft zu machen, indem Sie, wenn möglich, eine Ebene höher im src-Baum kopieren.

Ich bin mir ziemlich sicher, dass @duglin im Recht ist, und es könnte sehr riskant sein, dieses Verhalten zu ändern. Viele Docker-Dateien können kaputt gehen oder einfach unbeabsichtigte Dinge kopieren.

Ich würde jedoch argumentieren, dass es auf lange Sicht besser wäre, wenn COPY der Art und Weise folgen würde, wie Tools wie cp oder rsync mit Globs und nachgestellten Schrägstrichen in Ordnern umgehen. Es wird definitiv nicht erwartet, dass COPY Dateien aus einem Unterordner, der mit dir/* übereinstimmt, in das Ziel IMO kopiert

@jfchevrette ja - bei der ersten Gelegenheit sollten wir das "reparieren".
Schließe es jetzt...

@duglin also bedeutet schließen, dass es nicht behoben wird?

@tugberkugurlu ja , zumindest

@Duglin danke. Ist es möglich, dieses Problem offen zu halten und den Status hier zu aktualisieren? Oder gibt es dafür ein anderes Thema, das ich abonnieren kann?

@tugberkugurlu Ich dachte, wir hätten ein Problem mit dem "Client-seitigen Builder-Support", aber ich kann es anscheinend nicht finden. Wir haben also möglicherweise nur das, was die ROADMAP ( https://github.com/docker/docker/blob/master/ROADMAP.md#22-dockerfile-syntax ) sagt.

Was das Offenhalten des Problems betrifft, so glaube ich nicht, dass wir das tun können. Die allgemeine Regel, die Docker befolgt hat, besteht darin, alle Probleme zu schließen, die nicht sofort umsetzbar sind. Probleme für zukünftige Arbeiten werden normalerweise geschlossen und dann wieder geöffnet, sobald sich der Stand der Dinge so ändert, dass Maßnahmen (PR) für das Problem ergriffen werden können.

@duglin Dies ist ein sehr ernstes Problem, Sie sollten es nicht einfach schließen, da das Problem in Version 0.1 eingeführt wurde. Es wäre angemessener, dies für die Veröffentlichung von 2.0 anzustreben (Meilensteine ​​​​sind auch auf Github).

Ich denke, die meisten Leute verwenden:

COPY . /app

und alle anderen Ordner in .gitignore die schwarze Liste setzen oder eine einstufige Verzeichnisstruktur haben und COPY verwenden, das tatsächlich eine mv -Semantik hat:

COPY src /myapp

Ich kann mir nur schwer vorstellen, dass jemand tatsächlich COPY zum Abflachen der Verzeichnisstruktur verwenden würde. Die andere Problemumgehung hierfür ist die Verwendung tar -cf .. & ADD tarfile.tar.gz . Zumindest das zu ändern wäre wirklich hilfreich. Die andere Sache respektiert Schrägstriche in Verzeichnisnamen COPY src /src vs. COPY src/ /src (die derzeit vollständig ignoriert werden).

duglin hat dies am 1. September 2015 geschlossen

@duglin Dies ist ein lächerliches und ärgerliches Problem und sollte nicht geschlossen werden. Der Befehl COPY verhält sich speziell im Widerspruch zu der dokumentierten Verwendung und den Beispielen.

@tjwebb es gibt noch ein offenes Problem https://github.com/docker/docker/issues/29211. Dies kann nur untersucht werden, wenn es eine Möglichkeit gibt, dies zu beheben, die vollständig abwärtskompatibel ist. Wir sind offen für Vorschläge, wenn Sie einen Vorschlag haben, _wie_ dies implementiert werden könnte (wenn Sie _tun_, können Sie dies gerne aufschreiben und einen Vorschlag eröffnen, der auf dieses Problem verweist). Beachten Sie, dass es bereits einen Unterschied zwischen (zum Beispiel) OS X und Linux in der Art und Weise gibt, wie cp gehandhabt wird;

mkdir -p repro-15858 \
  && cd repro-15858 \
  && mkdir -p source/dir1 source/dir2 \
  && touch source/file1 source/dir1/dir1-file1 \
  && mkdir -p target1 target2 target3 target4 target5 target6

cp -r source target1 \
&& cp -r source/ target2 \
&& cp -r source/ target3/ \
&& cp -r source/* target4/ \
&& cp -r source/dir* target5/ \
&& cp -r source/dir*/ target6/ \
&& tree

OS X:

.
├── source
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target1
│   └── source
│       ├── dir1
│       │   └── dir1-file1
│       ├── dir2
│       └── file1
├── target2
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target3
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target4
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target5
│   ├── dir1
│   │   └── dir1-file1
│   └── dir2
└── target6
    └── dir1-file1

20 directories, 12 files

Unter Ubuntu (/bin/sh)

.
|-- source
|   |-- dir1
|   |   `-- dir1-file1
|   |-- dir2
|   `-- file1
|-- target1
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target2
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target3
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target4
|   |-- dir1
|   |   `-- dir1-file1
|   |-- dir2
|   `-- file1
|-- target5
|   |-- dir1
|   |   `-- dir1-file1
|   `-- dir2
`-- target6
    |-- dir1
    |   `-- dir1-file1
    `-- dir2

24 directories, 12 files
diff --git a/macos.txt b/ubuntu.txt
index 188d2c3..d776f19 100644
--- a/macos.txt
+++ b/ubuntu.txt
@@ -11,15 +11,17 @@
 │       ├── dir2
 │       └── file1
 ├── target2
-│   ├── dir1
-│   │   └── dir1-file1
-│   ├── dir2
-│   └── file1
+│   └── source
+│       ├── dir1
+│       │   └── dir1-file1
+│       ├── dir2
+│       └── file1
 ├── target3
-│   ├── dir1
-│   │   └── dir1-file1
-│   ├── dir2
-│   └── file1
+│   └── source
+│       ├── dir1
+│       │   └── dir1-file1
+│       ├── dir2
+│       └── file1
 ├── target4
 │   ├── dir1
 │   │   └── dir1-file1
@@ -30,6 +32,8 @@
 │   │   └── dir1-file1
 │   └── dir2
 └── target6
-    └── dir1-file1
+    ├── dir1
+    │   └── dir1-file1
+    └── dir2

-20 directories, 12 files
+24 directories, 12 files

Machen Sie einen neuen Befehls-CP und machen Sie es diesmal bitte richtig.

Ich würde das Obige wiederholen, das muss unzählige Entwicklungsstunden verschwendet haben, es ist extrem unintuitiv.

+1 von mir. Dies ist wirklich dummes Verhalten und könnte leicht behoben werden, indem einfach ein CP-Befehl hinzugefügt wird, der so funktioniert, wie COPY es haben sollte.

"Abwärtskompatibilität" ist ein Ausrutscher

Die TL;DR- Version:

Verwenden Sie nicht COPY * /app , es tut nicht das, was Sie erwarten würden.
Verwenden Sie stattdessen COPY . /app , um den Verzeichnisbaum beizubehalten.

COPY kann nur den Unterordner kopieren.

Ich habe gerade unzählige Stunden damit verbracht ... Warum funktioniert das überhaupt so?

Ich nutze Paket und möchte folgendes in der richtigen Struktur kopieren:

.
├── .paket/
│   ├── paket.exe
│   ├── paket.bootstrapper.exe
├── paket.dependencies
├── paket.lock
├── projectN/

Und wenn Sie COPY *paket* ./ ausführen, führt dies im Container zu folgendem:

.
├── paket.dependencies
├── paket.lock

Wie wäre es mit einem --glob oder --recursive Flag für COPY und ADD ?

KOPIEREN . /destination behält Unterordner bei.

Drei Jahre und das ist immer noch ein Thema :-/

Können wir eine ETA bekommen, wenn das behoben wird

kein Problem...
von oben...
KOPIEREN .

Stimmt, kein Thema mehr, wenn man einen halben Tag raucht und hier landet. Sicher :)
Seien wir konstruktiv,

image

Wir brauchen wirklich einen neuen _CP_-Befehl oder ein --recursive -Flag für _COPY_, damit die Abwärtskompatibilität erhalten bleibt.

Top-Punkte, wenn wir auch eine Warnung zum Image-Build anzeigen, wie:
Directory structure not preserved with COPY *, use CP or COPY . More here <link>. wenn wir möglichen Missbrauch entdecken.

Ich suche danach, um verschachtelte lerna package.json-Dateien in Unterverzeichnissen zu kopieren, um den Cache von npm install besser zu nutzen, um nur auszulösen, wenn sich Abhängigkeiten ändern. Derzeit führen alle geänderten Dateien dazu, dass Abhängigkeiten erneut installiert werden.

So etwas wäre toll:

COPY ["package.json", "packages/*/package.json", "/app/"]

Geht nach #29211 Jungs. Dieser wurde geschlossen und niemand kümmert sich darum.

@zentby Konversation ist hier, Problem wird dort verfolgt (da dieses geschlossen ist) ... Es ist verwirrend.

Ein Workaround ist COPY Dateien und RUN cp -R Befehl

COPY files /tmp/
RUN cp -R /tmp/etc/* /etc/ && rm -rf /tmp/etc

Das funktioniert nicht @instabledesign , da der COPY-Befehl den Cache zerstört, wenn eine Datei anders ist, die den Cache nicht ungültig machen sollte (zum Beispiel möchte ich nur Dateien kopieren, die sich auf die npm-Abhängigkeitsinstallation beziehen, da sich das nicht oft ändert).

Ich musste auch nur eine Reihe von Dateien (in meinem Fall *.sln- und *.csproj-Dateien für dotnet core) in den perversen Cache kopieren. Eine Problemumgehung besteht darin, einen Tarball nur aus den gewünschten Dateien zu erstellen und dann den Tarball in der Docker-Datei HINZUFÜGEN. Ja, jetzt müssen Sie zusätzlich zur Docker-Datei ein Shell-Skript haben ...

build.sh

#!/bin/bash

# unfortunately there's no easy way to copy just the *.sln and *.csproj (see https://github.com/moby/moby/issues/15858)
# so we generate a tar file containing the required files for the layer

find .. -name '*.csproj' -o -name 'Finomial.InternalServicesCore.sln' -o -name 'nuget.config' | sort | tar cf dotnet-restore.tar -T - 2> /dev/null

docker build -t finomial/iscore-build -f Dockerfile ..

Docker-Datei

FROM microsoft/aspnetcore-build:2.0
WORKDIR /src

# set up a layer for dotnet restore 

ADD docker/dotnet-restore.tar ./

RUN dotnet restore

# now copy all the source and do the dotnet buld
COPY . ./

RUN dotnet publish --no-restore -c Release -o bin Finomial.InternalServicesCore.sln

Sie können dazu mehrere COPY -Befehle verwenden, aber das hat den Nachteil, dass mehrere Bildebenen erstellt werden und Ihre endgültige Bildgröße aufgebläht wird.

Wie oben erwähnt, können Sie den docker build -Befehl auch in ein Helfer-Build-Skript einbetten, um Tarballs zu erstellen, die die Verzeichnisstruktur beibehalten, und sie in ADD einzufügen, aber das erhöht die Komplexität und bricht Dinge wie docker-compose build und Docker Hub automatisierte Builds.

Eigentlich sollte COPY genau wie ein POSIX-konformer /bin/cp -r -Befehl funktionieren, aber es scheint, als würde das aus Gründen der 'Abwärtskompatibilität' nicht passieren, obwohl das aktuelle Verhalten für jeden mit Erfahrung völlig unintuitiv ist in *nix-Systemen.


Der beste Kompromiss, den ich gefunden habe, ist die Verwendung eines mehrstufigen Builds als Hack:

FROM scratch as project_root
# Use COPY to move individual directories
# and WORKDIR to change directory
WORKDIR /
COPY ./file1 .
COPY ./dir1/ ./dir1/
COPY ./dir2/ .
WORKDIR /newDir
COPY ./file2 .

# The actual final build you end up using/pushing
# Node.js app as example
FROM node
WORKDIR /opt/app

COPY package.json .
RUN npm install

COPY --from=project_root / .
CMD ["npm", "start"]

Dies ist eigenständig in einer Docker-Datei enthalten und erstellt nur eine Ebene im endgültigen Bild, genau wie ein ADD project.tar funktionieren würde.

Ein vollständiger COPY -Befehl wäre wirklich hilfreich, wenn Sie versuchen, den Docker-Build-Cache zu erhalten. Die ROS-Community entwickelt mit verschachtelten Arbeitsbereichen von Paketen, wobei jedes einzelne Abhängigkeiten in seiner eigenen package.xml -Datei deklariert. Diese Dateien werden von einem Abhängigkeitsmanager verwendet, um Upstream-Bibliotheken zu installieren. Diese package.xml -Dateien ändern sich relativ selten in Bezug auf Code in Paketen selbst, sobald die Grundlagen geschaffen sind. Wenn die Verzeichnisbaumstruktur während einer Kopie erhalten blieb, könnten wir unseren Arbeitsbereich einfach während des Docker-Builds in zwei Schritten kopieren, um das Caching zu maximieren, z.

# copy project dependency metadata
COPY ./**/package.xml /opt/ws/

# install step that fetches unsatisfied dependency
RUN dependency_manager install --workspace /opt/ws/

# copy the rest of the project's code
COPY ./ /opt/ws/

# compile code with cached dependencies
RUN build_tool build --workspace /opt/ws/

Daher würde der Cache für die obige Abhängigkeitsinstallationsschicht nur platzen, wenn der Entwickler zufällig eine deklarierte Abhängigkeit ändert, während eine Änderung im Code des Pakets nur die Kompilierungsschicht platzen lassen würde.

Derzeit werden alle übereinstimmenden package.xml -Dateien übereinander in das Stammverzeichnis des Zielverzeichnisses kopiert, wobei die letzte Globe-Datei die einzige package.xml ist, die im Image verblieben ist. Was für Benutzer wirklich ziemlich unintuitiv ist! Warum werden kopierte Dateien übereinander überschrieben und das undefinierte Verhalten bleibt schließlich im Image bestehen.

Das ist so ein Schmerz in praktisch jedem Stack, der eine Paketverwaltung hat, also betrifft es so viele von uns. Kann es repariert werden? Meine Güte. Problem seit 2015! Der Vorschlag, einen neuen Befehl von CP zu verwenden, ist gut.

Können wir das wieder öffnen? Es ist ein sehr mühsames Verhalten, dass der COPY-Befehl eine interne Golang-Funktion für den Pfadabgleich verwendet und nicht einen wirklich weit verbreiteten Standard wie Glob

Für diejenigen, die per Globing unter Verwendung einer Problemumgehung mit experimenteller Buildkit-Syntax kopieren möchten, auch wenn das Caching nicht so präzise oder robust ist, können Sie sich die Kommentare hier ansehen: https://github.com/moby/moby/issues /39530#issuecomment -530606189

Ich würde immer noch gerne sehen, dass dieses Problem wieder geöffnet wird, damit wir ausgewählte Kopien im Glob-Stil zwischenspeichern können.

Ich habe eine relativ einfache Problemumgehung für mein Beispiel in https://github.com/moby/moby/issues/15858#issuecomment -462017830 über mehrstufige Builds realisiert und dachte, dass viele von Ihnen hier mit ähnlichen Anforderungen willkürliches Caching beim Kopieren zu schätzen wissen Artefakte aus dem Build-Kontext. Mit mehrstufigen Builds ist es möglich, das Verzeichnis zum Zwischenspeichern zu filtern/vorzuverarbeiten:

# Add prior stage to cache/copy from
FROM ubuntu AS package_cache

# Copy from build context
WORKDIR /tmp
COPY ./ ./src

# Filter or glob files to cache upon
RUN mkdir ./cache && cd ./src && \
    find ./ -name "package.xml" | \
      xargs cp --parents -t ../cache

# Continue with primary stage
FROM ubuntu

# copy project dependency metadata
COPY --from=package_cache /tmp/cache /opt/ws/

# install step that fetches unsatisfied dependency
RUN dependency_manager install --workspace /opt/ws/

# copy the rest of the project's code
COPY ./ /opt/ws/

# compile code with cached dependencies
RUN build_tool build --workspace /opt/ws/

Ein reales Arbeitsbeispiel finden Sie auch hier: https://github.com/ros-planning/navigation2/pull/1122

Ich suche danach, um verschachtelte lerna package.json-Dateien in Unterverzeichnissen zu kopieren, um den Cache von npm install besser zu nutzen, um nur auszulösen, wenn sich Abhängigkeiten ändern. Derzeit führen alle geänderten Dateien dazu, dass Abhängigkeiten erneut installiert werden.

So etwas wäre toll:

COPY ["package.json", "packages/*/package.json", "/app/"]

ich habe genau den gleichen Anwendungsfall.

Ich suche danach, um verschachtelte lerna package.json-Dateien in Unterverzeichnissen zu kopieren, um den Cache von npm install besser zu nutzen, um nur auszulösen, wenn sich Abhängigkeiten ändern. Derzeit führen alle geänderten Dateien dazu, dass Abhängigkeiten erneut installiert werden.

So etwas wäre toll:

COPY ["package.json", "packages/*/package.json", "/app/"]

Dieser Fall gilt jedoch für Garn-Arbeitsbereiche.

Es ist 2020 und das steht immer noch nicht fest.

Wenn jemand damit in einer Dotnet-Einstellung zu kämpfen hat, habe ich es für uns gelöst, indem ich ein globales Dotnet-Core-Tool geschrieben habe, das die Verzeichnisstruktur für die *.csproj-Dateien wiederherstellt, sodass eine Wiederherstellung folgen kann. Sehen Sie sich die Dokumentation dazu hier an .

Zu Ihrer Information, theoretisch könnte ein ähnlicher Ansatz in anderen Einstellungen verwendet werden, aber im Wesentlichen ist das Tool ein Reverse-Engineering der Ordnerstruktur, daher bin ich mir nicht sicher, wie einfach oder überhaupt möglich dies beispielsweise für die Einrichtung eines Lerna- oder Garn-Arbeitsbereichs wäre. Gerne recherchieren, falls Interesse besteht. Könnte sogar im selben Tool möglich sein, wenn die Leute gerne die Dotnet-Core-Runtime installieren würden, damit sie funktioniert, sonst müsste der gleiche Ansatz, den ich gemacht habe, in einer Sprache erstellt werden, die keine neue Abhängigkeit erfordert, wie node Ich schätze.

Erstaunlich, dass das Implementieren eines Kopierbefehls früher eine Studentenaufgabe im ersten Studienjahr war und nun für erfahrene Programmierer mit langjähriger Erfahrung zu komplex ist...

Es ist wahrscheinlich nicht der peinlichste Fehler aller Zeiten, aber wenn man bedenkt, dass ihm viele Jahre der Diskussion ohne Ergebnis folgten, macht er es sicherlich an die Spitze.

@benmccallum
FYI, theoretisch könnte ein ähnlicher Ansatz in anderen Einstellungen verwendet werden, aber im Wesentlichen ist das Tool Reverse-Engineering der Ordnerstruktur,

Ist es für die meisten Gelegenheiten nicht einfacher, einfach das zu tun, was https://github.com/moby/moby/issues/15858#issuecomment -532016362 vorgeschlagen hat, und einen mehrstufigen Build zum Vorfiltern zu verwenden?

Auch für den Fall dotnet restore ist es ein relativ einfaches Muster:

# Prefiltering stage using find -exec and cp --parents to copy out
# the project files in their proper directory structure.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS dotnet-prep
COPY . ./src/
RUN mkdir ./proj && cd ./src && \
  find . -type f -a \( -iname "*.sln" -o -iname "*.csproj" \) \
    -exec cp --parents "{}" ../proj/ \;

# New build stage, independent cache
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS dotnet-build

# Copy only the project files with correct directory structure
# then restore packages
COPY --from=dotnet-prep ./proj ./src/
RUN dotnet restore

# Copy everything else
COPY --from=dotnet-prep ./src ./src/
# etc.

Dies ist jedoch keine angemessene Entschuldigung für Docker, nie eine anständige Variante des COPY -Befehls implementiert zu haben, die nur der normalen, vernünftigen Synchronisierungssemantik folgt.
Ich meine; aufleuchten!

@rjgotten , gefällt mir! Sicherlich viel einfacher als das, was ich getan habe, und ich kann nicht sehen, warum dies für meine Bedürfnisse nicht funktionieren würde. Ich werde es morgen versuchen und wenn das funktioniert, werde ich meine Dokumente ändern, um dies als besseren Ansatz zu empfehlen.

Ich denke, mein erstes Problem war, dass ich unter Windows war, also habe ich diesen Vorschlag wahrscheinlich verworfen. Ich bin es nicht mehr, aber haben Sie der Vollständigkeit halber eine gleichwertige Windows-Version? Ich frage mich, ob PowerShell in den dotnet-Core-Images vorinstalliert ist ...

Gibt es wirklich eine Notwendigkeit für das zusätzliche/wiederholte FROM ... obwohl? Jedes Mal, wenn Sie einen RUN ausführen, wird eine neue Ebene zum Caching erstellt, richtig? Vielleicht übersehe ich aber etwas, es ist schon eine Weile her, dass ich darüber nachdenken musste!

Ich frage mich, ob PowerShell in den dotnet-Core-Images vorinstalliert ist ...

Ich denke, das ist es tatsächlich. Das würde es etwas einfacher machen, dies plattformübergreifend zu tun.

Gibt es wirklich eine Notwendigkeit für das zusätzliche/wiederholte FROM ... obwohl?

Isolierte Build-Stufen erhalten unabhängiges Layer-Caching.
Die erste Stufe erledigt die Vorbereitungsarbeit. Da es zunächst alles hineinkopieren muss, macht es immer den Cache seiner ersten Schicht ungültig und damit auch die Schichten danach, wenn sich _irgendeine_ Datei ändert. Aber das gilt nur für die Schichten _innerhalb dieser Bauphase_.

Die zweite Stufe beginnt damit, dass _nur_ die projektbezogenen Dateien hineinkopiert werden und solange diese Dateien gleich sind - dh gleiche Dateinamen; gleicher Inhalt; usw. - über Builds hinweg, die diese Ebene _nicht_ ungültig machen. Das bedeutet, dass die Ebene dotnet restore _ebenfalls_ nicht ungültig wird, es sei denn, diese Projektdateien wurden tatsächlich geändert.

Hatte einige Zeit damit zu sitzen und ich verstehe jetzt! Docker macht Spaß, denn wenn Sie nicht immer Zeit damit verbringen, vergessen Sie, wie alle Befehle funktionieren. Entscheidend war, dass ich vergessen hatte, dass die RUN-cmd nur auf dem Dateisystem des Docker-Images ausgeführt werden kann, nicht auf den Build-Kontextdateien. Sie sind also gezwungen, alles zu KOPIEREN, bevor Sie einen komplexen RUN-Befehl ausführen können, der Verzeichnisse beibehält. Und deshalb brauchen wir so dringend anständiges COPY-Globbing!

Dieser Ansatz
Der anfängliche COPY-Befehl kopiert, wie Sie bereits erwähnt haben, _alles_ hinüber und zieht dann die .sln- und .csproj-Dateien in einen separaten /proj-Ordner. Jede Codeänderung macht diese Schritte ungültig. Entscheidend ist, dass die Einschränkung, die hiermit umgangen wird, darin besteht, dass das großartige Linux-Cmd RUN nur mit Dateien arbeiten kann, die _bereits auf dem Docker-Image_ vorhanden sind und von der gierigen COPY zuvor übernommen wurden.

Dann wird eine neue Stufe gestartet und kopiert den Inhalt des /proj-Ordners, der dann für dotnet restore verwendet werden kann. Da der Cache-"Schlüssel" im Wesentlichen die Datei-Hashes sind, wird dies selten diesen Cache-Layer oder den nachfolgenden dotnet restore sprengen, sodass Sie die teure Wiederherstellung vermeiden. Schön!

Mein Ansatz
Verwendet dafür nur eine Build-Phase auf Kosten einiger weiterer COPY-Befehle, um die Dateien zu übertragen, die sich auf eine Dotnet-Wiederherstellung auswirken. Ich reduziere speziell alle .csproj-Dateien in einem Verzeichnis und verwende dann mein globales Tool, um die richtige Verzeichnisstruktur aus der .sln-Eintragsdatei wiederherzustellen. Erst danach KOPIERE ich alle src-Dateien, sodass ich Ebenen effektiv regelmäßig bis hierhin zwischenspeichern kann, anstatt immer alle src-Dateien im Voraus KOPIEREN zu müssen.

Imbiss
Ich denke, es wird von der Codebasis der Leute abhängen, wie effektiv jeder Ansatz ist. Für uns haben wir in einem Mono-Repo eine Menge gemeinsam genutzten Codes, der in der „all src over“ COPY kopiert wird. .dockerignore hilft hier, ist aber schwer zu pflegen, also sind wir in dieser COPY ziemlich "gierig". also ziemlich langsam. Daher wäre mein Ansatz, obwohl etwas komplizierter, für uns wahrscheinlich schneller als diese Alternative.

Danke für die Erklärung. Ich kann immer noch nicht glauben, dass wir dieses Gespräch überhaupt führen müssen, haha. Ich muss noch die neueren BuildKit-Sachen untersuchen, um zu sehen, ob dies jetzt einfacher ist. Hat das noch jemand gemacht?

BuildKit-Untersuchung
RUN --mount=type=bind - Klingt so, als ob dies uns erlauben würde, das ausgefallene Linux-cmd gegen den Build-Kontext auszuführen (anstatt RUN nur auf das Dateisystem des Bildes zu beschränken). Tatsächlich wird anscheinend standardmäßig der Build-Kontext verwendet.

RUN --mount=type=cache - klingt nach einer Art wiederverwendbarem Cache-Verzeichnis (zwischen Docker-Builds?), das erhalten bleibt? Im Wesentlichen müssten wir uns also nicht einmal allzu viele Gedanken über einen Cache-Layer für Paketwiederherstellungen machen, denn mit einem wiederverwendeten Cache von zuvor wiederhergestellten Paketen wäre es schon verdammt viel schneller!?

Ich denke, das Problem ist noch nicht behoben, weil es "geschlossen" ist und die Leute an die Problemumgehungen gewöhnt sind.

Wenn Sie nicht verstehen, warum Menschen dazu neigen, zu anderen Containertypen zu wechseln.

Gibt es einen anderen Containertyp, den ich verwenden kann? Kann nicht glauben, dass dies nach so vielen Jahren nicht mehr unterstützt wird? Ist Docker ein Open-Source-Projekt, kann es bitte jemand reparieren lassen?

Wir haben eine COPY --dir Option in Earthly, damit sich die Kopie eher wie cp -r verhält. Vielleicht könnte dies auch auf Dockerfile portiert werden?

Um das Erstellen von Images für .net-Core-Anwendungen zu beschleunigen, sollten wir einen Zwischencontainer erstellen, der alle wiederhergestellten Nuget-Pakete enthält. Für eine Lösung mit mehreren Projekten habe ich diese Problemumgehung verwendet. Ich habe gerade alle Projektdateien in einen einzelnen Ordner kopiert und für jede von ihnen eine dotnet-Wiederherstellung ausgeführt. Es gibt einige Warnungen zu verpassten Projekten, weil wir die Ordnerhierarchie nicht beibehalten können, aber es funktioniert trotzdem.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build

# Nugets restore
WORKDIR /src/allprojects          # just temporary storage for .csproj files
COPY */*.csproj ./
RUN for file in $(ls *.csproj); do dotnet restore ${file}; done

# Build/Publish
WORKDIR /src/solution             # actual folder with source code and full hierarchy 
COPY . .
RUN dotnet publish "MyProject/MyProject.csproj" -c Release -o /bublish/myproject

# Run Application
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS base
WORKDIR /app
COPY --from=build /bublish/myproject .
ENTRYPOINT ["dotnet", "MyProject.dll"]

@zatuliveter
Es gibt einige Warnungen zu verpassten Projekten, weil wir die Ordnerhierarchie nicht beibehalten können, aber es funktioniert trotzdem.

Nein; das geht nicht. Und hier ist der Grund:

.NET Core speichert Paketmetainformationen im Unterverzeichnis ./obj , das jedem Projekt zugeordnet ist. Ohne diese Informationen gilt das Paket nicht als installiert und einsatzbereit. (Glauben Sie mir nicht? Dann werfen Sie Ihren ./obj -Ordner weg und öffnen Sie dann z. B. das Projekt in VSCode und beobachten Sie, wie es Sie auffordert, die Paketwiederherstellung erneut auszuführen. Probieren Sie es aus.)

Wenn sich die Projektdateien, für die Sie die Paketwiederherstellung ausführen, in einer anderen Verzeichnisstruktur befinden als die folgenden dotnet build oder dotnet publish , dann sehen diese Befehle das Paket nicht als wiederhergestellt.

Der Grund, warum Ihre Lösung nicht völlig fehlschlägt, ist, dass dotnet publish und dotnet build beide dotnet restore implizieren. Sie suchen aktiv nach nicht wiederhergestellten Paketen und stellen diese on-the-fly wieder her. Um dies zu vermeiden, müssen Sie aktiv das --no-restore -Flag übergeben, was Sie nicht tun.

Ihre Lösung stellt also die Pakete _ZWEIMAL_ wieder her. Das erste Mal ist im Wesentlichen eine große Zeit- und Platzverschwendung, da es nicht in die richtige Verzeichnisstruktur für die Wiederverwendung gelangt. Das zweite Mal, implizit als Teil des Befehls publish , funktioniert; Da dies jedoch Teil derselben Ebene wie der Build- und Veröffentlichungsvorgang ist, werden Ihre Pakete nicht wirklich getrennt von Ihren Codeänderungen zwischengespeichert.

@rjgotten ,
Vielen Dank für Ihre Antwort und Erläuterungen.
Tatsächlich werden alle Nuget-Pakete im Ordner global-packages im Docker-Container „Build“ zwischengespeichert. In meinem Fall ist es der Ordner /root/.nuget/packages/ , und der Ordner obj enthält nur kleine Dateien mit Verweisen auf diesen globalen Speicher, sodass kein Speicherplatz verschwendet wird (wie Sie bereits erwähnt haben).
Zweite Wiederherstellung während der Veröffentlichung mindestens 10-mal schneller (in meinem Fall), da alle Nugets im Container zwischengespeichert werden.

@zatuliveter @rjgotten danke für die Info am Ende hier. Ich bin auf ähnliche Probleme gestoßen und habe mir das folgende Stück Dockerfile ausgedacht, um die von Ihnen angegebenen Beispiele zu verbessern. Bash ist sicherlich nicht meine Stärke, also schonen Sie mich! Unsere Struktur ist Project/Project.csproj für alle unsere Projekte. Dies kopiert alle proj-Dateien hinein, verschiebt sie an die richtige Stelle und führt dann die Wiederherstellung / Alle kopieren / Veröffentlichen durch.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ./*/*.csproj ./proj/
RUN for file in $(ls ./proj); do mkdir /src/${file%.*} && mv ./proj/${file} /src/${file%.*}/${file}; done
RUN dotnet restore "MyProject/MyProject.csproj"
COPY . .
WORKDIR "/src/MyProject"
RUN dotnet publish "MyProject.csproj" -c Release -o /app --no-restore
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen