Moby: 带有文件 glob 的 Dockerfile COPY 会将文件从子目录复制到目标目录

创建于 2015-08-26  ·  54评论  ·  资料来源: moby/moby

问题描述:
当在Dockerfile中使用COPY $ 并使用 glob 复制文件和文件夹时,docker 将(有时?)还将文件从子文件夹复制到目标文件夹。

$ 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

环境细节:
使用 docker-machine 构建的 OSX /w boot2docker 上的本地设置

如何复制:

语境

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

Dockerfile

FROM busybox

RUN mkdir /test
COPY files/* /test/

实际结果

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

预期成绩
生成的图像应具有与上下文相同的目录结构

arebuilder

最有用的评论

请制作一个新的命令 CP 并把它做好。

所有54条评论

使用docker infouname -a的输出更新了原始消息,并将其重新格式化为根据问题报告模板。

我在 1.6.2 和 1.8 上有过这个
https://gist.github.com/jrabbit/e4f864ca1664ec0dd288二级目录被视为一级目录应该是出于某种原因吗?

对于那些谷歌搜索:如果您遇到 COPY * /src 问题,请尝试 COPY / /src

@jfchevrette我想我知道为什么会这样。
您有COPY files/* /test/扩展为COPY files/dir files/file1 files/file2 files/file /test/ 。 如果将其拆分为单独的 COPY 命令(例如COPY files/dir /test/ ),您会看到(无论好坏) COPY 会将每个 arg 目录的内容复制到目标目录中。 不是 arg 目录本身,而是内容。 如果您添加了第 3 级目录,我敢打赌,这些目录会一直存在。

我对 COPY 不保留顶级目录这一事实并不感到兴奋,但它已经有一段时间了。

如果可能的话,您可以尝试通过在 src 树中复制更高级别来减少这种痛苦。

我非常有信心@duglin是正确的,改变这种行为可能会非常冒险。 许多 dockerfiles 可能会破坏或简单地复制意外的东西。

但是,我认为从长远来看,如果 COPY 遵循诸如 cp 或 rsync 之类的工具处理文件夹上的全局和尾随斜杠的方式会更好。 绝对不希望 COPY 将文件从匹配 dir/* 的子文件夹复制到目标 IMO

@jfchevrette是的-我们第一次有机会应该“解决”这个问题。
暂时关闭...

@duglin所以,关闭意味着它不会得到修复?

@tugberkugurlu是的,至少现在是这样。 重做整个构建基础设施的工作正在进行中,当我们这样做时,我们就可以让 COPY(或其新的等价物)按照应有的方式运行。

@duglin谢谢。 是否可以保持此问题开放并在此处更新状态? 或者还有其他我可以订阅的问题吗?

@tugberkugurlu我以为我们遇到了“客户端构建器支持”的问题,但我似乎找不到。 所以我们可能拥有的只是 ROADMAP(https://github.com/docker/docker/blob/master/ROADMAP.md#22-dockerfile-syntax)所说的。

至于让问题保持开放,我认为我们不能这样做。 Docker 一直遵循的一般规则是关闭任何无法立即采取行动的问题。 未来工作的问题通常会关闭,然后在事情状态发生变化时重新打开,以便可以针对该问题采取一些行动 (PR)。

@duglin这是一个非常严重的问题,您不应该关闭它,因为该问题是在 0.1 版本中引入的。 将其作为 2.0 版本的目标会更合适(里程碑也在 github 上)。

我想大多数人使用:

COPY . /app

并将.gitignore中的所有其他文件夹列入黑名单或具有单级目录结构并使用COPY实际上具有mv语义:

COPY src /myapp

我很难想象有人会真正使用COPY来扁平化目录结构。 另一个解决方法是使用tar -cf .. & ADD tarfile.tar.gz 。 至少改变这一点会非常有帮助。 另一件事是尊重目录名称中的斜杠COPY src /srcCOPY src/ /src (目前完全被忽略)。

duglin 于 2015 年 9 月 1 日关闭此

@duglin这是一个荒谬而令人愤怒的问题,不应关闭。 COPY命令的行为特别与记录的用法和示例不一致。

@tjwebb还有一个未解决的问题https://github.com/docker/docker/issues/29211。 只有在有一种完全向后兼容的方法来解决这个问题时,才能研究这个问题。 如果您有_how_ 可以实施的提案,我们愿意接受建议(如果您_do_,请随时写下来,并打开提案,链接到此问题)。 请注意,(例如)、OS X 和 Linux 在处理cp的方式上已经有所不同;

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

操作系统:

.
├── 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

在 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

请制作一个新的命令 CP 并把它做好。

我会附和上面的,这肯定浪费了无数的开发时间,它非常不直观。

+1 来自我。 这是非常愚蠢的行为,只需添加一个执行 COPY 应该具有的 CP 命令即可轻松纠正。

“向后兼容性”是一种逃避

TL;DR版本:

不要使用COPY * /app ,它不会做你期望它做的事情。
改用COPY . /app来保留目录树。

COPY 只能复制它的子文件夹。

只是在这上面花了无数个小时......为什么这甚至会这样工作?

我正在使用Paket并希望以正确的结构复制以下内容:

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

通过执行COPY *paket* ./会在容器内产生以下结果:

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

COPYADD添加--glob--recursive标志怎么样?

复制 。 /destination 保留子文件夹。

三年,这仍然是一个问题:-/

我们能得到一个预计到达时间,什么时候能解决这个问题

没什么大不了...
从上面...
复制 。

确实,在你发了半天的烟,到这里结束后,这不再是问题了。 当然 :)
让我们有建设性,

image

我们确实需要一个新的 _CP_ 命令或一个--recursive标志来 _COPY_ 以便保留向后兼容性。

如果我们还在镜像构建中显示警告,那么要点,例如:
Directory structure not preserved with COPY *, use CP or COPY . More here <link>.如果我们检测到可能的误用。

我正在寻找这个来复制子目录中嵌套的 lerna package.json 文件,以更好地利用npm install缓存仅在依赖项更改时触发。 当前,所有文件更改都会导致依赖项重新安装。

这样的事情会很棒:

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

去检查#29211伙计们。 这个已经关闭了,没有人关心。

@zentby对话在这里,问题在那里被跟踪(因为这个已经关闭)......这令人困惑。

一种解决方法是COPY文件和RUN cp -R 命令

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

这将不起作用@instabledesign ,因为当文件不同时不应使缓存无效时, COPY 命令会破坏缓存(例如,我只想复制与 npm 依赖项安装相关的文件,因为这不会经常改变)

我还需要只复制一组文件(在我的例子中,*.sln 和 *.csproj 文件用于 dotnet 核心)来破坏缓存。 一种解决方法是创建一个包含所需文件的 tar 球,然后在 Docker 文件中添加 tar 球。 是的,现在除了 Docker 文件之外,您还必须有一个 shell 脚本......

构建.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 ..

码头工人文件

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

您可以使用多个COPY命令来执行此操作,但这具有创建多个图像层和膨胀最终图像大小的缺点。

正如上面提到的 kayjtea,您还可以将docker build命令包装在帮助程序构建脚本中以创建保留目录结构的 tarball,并将ADD放入其中,但这会增加复杂性并破坏诸如docker-compose build之类的东西

确实, COPY应该像符合 POSIX 的/bin/cp -r命令一样运行,但对于“向后兼容性”似乎不会发生这种情况,即使当前的行为对于任何有经验的人来说都是完全不直观的在 *nix 系统中。


我发现的最佳折衷方案是使用多阶段构建作为 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"]

这是自包含在一个 Dockerfile 中的,并且只在最终映像中创建一层,就像ADD project.tar的工作方式一样。

在尝试保留 docker 构建缓存时,拥有一个完整的COPY命令真的很有帮助。 ROS 社区使用包的嵌套工作区进行开发,每个包都在自己的package.xml文件中声明依赖项。 依赖管理器使用这些文件来安装任何上游库。 一旦奠定了基础,这些package.xml文件相对很少更改为包本身的代码。 如果在复制过程中保留了目录树结构,我们可以在 docker 构建期间分两个阶段简单地复制我们的工作空间以最大化缓存,例如:

# 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/

因此,仅当开发人员碰巧更改了声明的依赖项时,上述依赖项安装层的缓存才会破坏,而包代码的更改只会破坏编译层。

目前,所有匹配的package.xml文件都被一个接一个地复制到目标目录的根目录,最后一个全局文件是唯一保留在映像中的package.xml 。 这对用户来说真的很不直观! 为什么复制的文件被覆盖在彼此之上,以及最终在图像中持续存在的未定义行为。

这对基本上每个具有包管理的堆栈来说都是一种痛苦,因此它影响了我们很多人。 可以修复吗? 嘘。 自2015年以来一直是一个问题! 使用CP的新命令的建议是一个很好的建议。

我们可以重新打开这个吗? COPY 命令使用 golang 内部函数进行路径匹配,而不是真正广泛采用的标准,如 glob,这是非常乏味的行为

对于那些想通过使用具有实验性 buildkit 语法的解决方法通过 globing 进行复制的人,即使缓存不那么精确或健壮也可以查看此处的评论: https ://github.com/moby/moby/issues

我仍然希望重新打开此问题,以便我们可以缓存选择性 glob 样式副本。

我通过多阶段构建https://github.com/moby/moby/issues/15858#issuecomment -462017830 中为我的示例实现了一个相对简单的解决方法,并认为这里有类似需求的许多人可能会欣赏复制时的任意缓存来自构建上下文的工件。 使用多阶段构建,可以过滤/预处理要缓存的目录:

# 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/

对于现实世界的工作示例,您还可以在这里查看: https ://github.com/ros-planning/navigation2/pull/1122

我正在寻找这个来复制子目录中嵌套的 lerna package.json 文件,以更好地利用npm install缓存仅在依赖项更改时触发。 当前,所有文件更改都会导致依赖项重新安装。

这样的事情会很棒:

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

我有完全相同的用例。

我正在寻找这个来复制子目录中嵌套的 lerna package.json 文件,以更好地利用npm install缓存仅在依赖项更改时触发。 当前,所有文件更改都会导致依赖项重新安装。

这样的事情会很棒:

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

这种情况下,但对于 Yarn 工作区。

现在是 2020 年,这仍然没有解决。

如果有人在 dotnet 设置中遇到这个问题,我已经通过编写一个 dotnet core 全局工具为我们解决了这个问题,该工具可以恢复 *.csproj 文件的目录结构,从而可以进行恢复。 请参阅有关如何在此处执行此操作的文档。

仅供参考,理论上可以在其他设置中使用类似的方法,但本质上该工具是对文件夹结构进行逆向工程,所以我不确定对于 lerna 或 yarn 工作区设置来说有多容易甚至可能。 如果有兴趣,很高兴调查它。 如果人们乐于安装 dotnet 核心运行时以使其工作,甚至可以在同一个工具中使用,否则我所做的相同方法需要使用不需要新依赖项的语言构建,例如 node我猜。

令人惊奇的是,以前实现一个复制命令是一个学生第一年的任务,现在对于有多年经验的熟练程序员来说太复杂了……

这可能不是有史以来最令人尴尬的错误,但考虑到它经过多年讨论而没有任何输出,它肯定是最重要的。

@benmccallum
仅供参考,理论上可以在其他设置中使用类似的方法,但本质上该工具是对文件夹结构进行逆向工程,

对于大多数场合来说,做https://github.com/moby/moby/issues/15858#issuecomment -532016362 建议并使用多阶段构建进行预过滤不是更容易吗?

同样对于dotnet restore情况,这是一个相对简单的模式:

# 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.

仍然不能为 Docker 从未在COPY命令上实现一个体面的变体提供适当的借口,该变体只是遵循正常的、理智的同步语义。
我是说; 来吧!

@rjgotten ,我喜欢! 当然比我做的要容易得多,我不明白为什么这不能满足我的需要。 明天我会试一试,如果可行,我将更改我的文档以推荐此方法作为更好的方法。

我认为我最初的问题是我在 Windows 上,所以可能驳回了这个建议。 我不再是了,但是您是否有完整的等效 Windows 版本? 我想知道 PowerShell 是否预装在 dotnet 核心映像中...

真的需要额外的/重复的 FROM ...吗? 每次执行 RUN 时,它都会创建一个新的缓存层,对吗? 也许我错过了一些东西,自从我不得不考虑这个已经有一段时间了!

我想知道 PowerShell 是否预装在 dotnet 核心映像中...

我认为它实际上是。 这将使以跨平台方式执行此操作变得更容易一些。

真的需要额外的/重复的 FROM ...吗?

独立的构建阶段获得独立的层缓存。
第一阶段进行准备工作。 由于它最初必须复制所有内容,因此当_any_ 文件更改时,它总是使其第一层的缓存无效,因此它之后的层也无效。 但这仅适用于构建阶段内的层。

第二阶段从_only_复制项目相关文件开始,只要这些文件是相同的——即相同的文件名; 相同的内容; 等等 - 跨构建该层_不会_无效。 这意味着dotnet restore层_also_ 不会失效,除非这些项目文件实际更改。

有一些时间坐下来,我现在明白了! Docker 很有趣,除非你总是花时间使用它,否则你会忘记所有命令是如何工作的。 至关重要的是,我忘记了 RUN cmd 只能在 docker 映像的文件系统上运行,而不能在构建上下文文件上运行。 因此,您必须先复制所有内容,然后才能执行保留目录的复杂 RUN cmd。 这就是为什么我们如此迫切地需要体面的 COPY globbing!

这种方法
就像您提到的那样,最初的 COPY cmd 是复制 _everything_ ,然后将 .sln 和 .csproj 文件拉出到单独的 /proj 文件夹中。 任何代码更改都会使这些步骤无效。 至关重要的是,这个解决方法的限制是,令人敬畏的RUN linux cmd 只能在 docker 映像上的文件上操作,这是由之前的贪婪 COPY 带来的。

然后开始一个新阶段,并复制 /proj 文件夹内容,然后可以将其用于dotnet restore 。 由于缓存“键”本质上是文件哈希,因此这很少会破坏这个缓存层,也不会破坏后续的dotnet restore ,因此您可以避免昂贵的恢复。 好的!

我的方法
为此仅使用一个构建阶段,代价是更多的 COPY 命令来带来影响 dotnet 还原的文件。 我特意将所有的.csproj 文件扁平化到一个目录中,然后使用我的全局工具从入口.sln 文件中重新构建正确的目录结构。 只有在此之后,我才复制所有 src 文件,因此我可以定期有效地缓存层一直到这里,而不是总是必须预先复制所有 src 文件。

外卖
我认为每种方法的有效性取决于人们的代码库。 对我们来说,在一个单一的 repo 中,我们有很多共享代码在“all src over” COPY 中被复制。 .dockerignore 在这里有帮助,但很难维护,所以我们在那个 COPY 中非常“贪婪”; 所以它很慢。 因此,我的方法虽然稍微复杂一些,但对我们来说可能比这种替代方法更快。

感谢您的解释。 仍然不敢相信我们甚至不得不进行这样的对话哈哈。 我仍然需要调查较新的 BuildKit 东西,看看现在这是否更容易。 有没有其他人这样做过?

BuildKit 调查
RUN --mount=type=bind - 听起来这样可以让我们针对构建上下文执行花哨的 linux cmd(而不是RUN仅限于图像的文件系统)。 事实上,它显然默认为构建上下文。

RUN --mount=type=cache - 听起来像是某种可重复使用的缓存目录(在 docker 构建之间?)被保留了? 所以本质上,我们甚至不需要太担心用于包恢复的缓存层,因为使用以前恢复的包的重复使用缓存,它已经快得多了!?

我认为这个问题还没有解决,因为它已经“关闭”并且人们已经习惯了变通方法。

当您不明白为什么人们倾向于转向其他容器类型时。

是否有其他我可以使用的容器类型。 不敢相信这么多年还不支持? docker是一个开源项目吗,有人可以修复它吗?

我们在 Earthly 中有一个COPY --dir选项,以使复制行为更像cp -r 。 也许这也可以移植到 Dockerfile?

为了加快为 .net 核心应用程序构建映像,我们应该创建一些中间容器,其中包含所有恢复的 nuget 包。 对于多项目解决方案,我使用了这种解决方法。 我只是将所有项目文件复制到单个文件夹并为每个文件运行 dotnet restore。 有一些关于错过项目的警告,因为我们无法保留文件夹层次结构,但它仍然是有效的解决方案。

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
有一些关于错过项目的警告,因为我们无法保留文件夹层次结构,但它仍然是有效的解决方案。

不; 那是行不通的。 这就是为什么:

.NET Core 将包元信息存储在与每个项目关联的./obj子目录中。 如果不存在该信息,则该软件包将不会被视为已安装并可以使用。 (不相信我?然后扔掉你的./obj文件夹,然后在 VSCode 中打开项目并观看它要求你重新运行包还原。继续;试一试。)

如果您执行包还原的项目文件位于与以下dotnet builddotnet publish不同的目录结构中,则这些命令将看不到包已还原。

您的解决方案没有完全失败的原因是因为dotnet publishdotnet build都暗示dotnet restore 。 他们主动检查未恢复的包并即时恢复它们。 为了避免他们这样做,您必须主动传递--no-restore标志,而您没有这样做。

所以真的,你的解决方案正在做的是恢复包_TWICE_。 第一次本质上是对时间和空间的极大浪费,因为它没有进入正确的目录结构以供重复使用。 第二次,作为publish命令的一部分隐式起作用; 但是因为它与构建和发布操作属于同一层,所以您的包实际上并没有与您的代码更改分开缓存。

@rjgotten
感谢您的回复和澄清。
事实上,所有 nuget 包都缓存在“build”docker 容器中的global-packages文件夹中。 在我的情况下,它是/root/.nuget/packages/文件夹,而obj文件夹仅包含引用此全局存储的小文件,因此不会浪费存储(正如您提到的)。
发布期间的第二次恢复至少快 10 倍(在我的情况下),因为所有 nuget 都缓存在容器中。

@zatuliveter @rjgotten感谢这里最后提供的信息。 我遇到了类似的问题,并提出了以下 dockerfile 来改进您提供的示例。 Bash 当然不是我的强项,所以对我放轻松! 我们所有项目的结构都是Project/Project.csproj 。 这将复制所有 proj 文件,将它们移动到正确的位置,然后执行恢复/复制所有/发布。

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
此页面是否有帮助?
0 / 5 - 0 等级