Moby: Dockerfile COPY с файловыми глобусами скопирует файлы из подкаталогов в каталог назначения.

Созданный на 26 авг. 2015  ·  54Комментарии  ·  Источник: moby/moby

Описание проблемы:
При использовании COPY в Dockerfile и использовании глобусов для копирования файлов и папок докер (иногда?) также копирует файлы из подпапок в папку назначения.

$ 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

Детали среды:
Локальная установка на OSX /w boot2docker, созданная с помощью docker-machine

Как воспроизвести:

Контекст

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

Докерфайл

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 info и uname -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 в целевой каталог. Не сам каталог аргументов, а его содержимое. Если вы добавите 3-й уровень каталогов, держу пари, они останутся.

Я не в восторге от того факта, что COPY не сохраняет каталог верхнего уровня, но так было уже некоторое время.

Вы можете попытаться сделать это менее болезненным, скопировав один уровень выше в дереве src, если это возможно.

Я вполне уверен, что @duglin прав, и менять такое поведение может быть очень рискованно. многие файлы докеров могут сломаться или просто скопировать непредвиденные вещи.

Однако я бы сказал, что в долгосрочной перспективе было бы лучше, если бы COPY следовал тому, как такие инструменты, как cp или rsync, обрабатывают глобусы и завершающие косые черты в папках. Определенно не ожидается, что COPY скопирует файлы из подпапки, соответствующей dir/*, в IMO назначения.

@jfchevrette да - при первом шансе мы должны «исправить» это.
Закрываем пока...

@duglin , значит, закрытие не будет исправлено?

@tugberkuguurlu да, по крайней мере, сейчас. Идет работа по переделке всей инфраструктуры сборки, и когда мы это сделаем, мы сможем заставить 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 /src против COPY src/ /src (которые в настоящее время полностью игнорируются).

duglin закрыл это 1 сент. 2015 г.

@duglin Это нелепая и раздражающая проблема, и ее не следует закрывать. Поведение команды COPY явно противоречит документированному использованию и примерам.

@tjwebb есть еще открытая проблема https://github.com/docker/docker/issues/29211. Это можно рассмотреть только в том случае, если есть способ исправить это, который полностью обратно совместим. Мы открыты для предложений, если у вас есть предложение, _как_ это можно реализовать (если вы _делаете_, не стесняйтесь написать это и открыть предложение со ссылкой на этот выпуск). Обратите внимание, что уже есть разница между (например) 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 от меня. Это действительно глупое поведение, и его можно легко исправить, просто добавив команду CP, которая выполняет то, что должен делать COPY.

«Обратная совместимость» — отговорка

Версия TL;DR :

Не используйте COPY * /app , это не то, что вы ожидаете.
Вместо этого используйте COPY . /app , чтобы сохранить дерево каталогов.

COPY может копировать только свою подпапку.

Просто потратил бесчисленное количество часов на это... Почему это вообще так работает?

Я использую пакет и хочу скопировать следующее в правильной структуре:

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

И выполнение COPY *paket* ./ приводит к следующему результату внутри контейнера:

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

Как насчет добавления флага --glob или --recursive для COPY и ADD ?

КОПИРОВАТЬ . /destination сохраняет вложенные папки.

Три года, а это все еще проблема :-/

Можем ли мы получить ETA, когда это будет исправлено?

Не ошибка...
сверху...
КОПИРОВАТЬ .

Правда, уже не проблема после того, как полдня покуришь и окажешься здесь. Конечно :)
Давайте будем конструктивными,

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-болл только из нужных вам файлов, а затем ДОБАВИТЬ tar-архив в файл Docker. Да, теперь у вас должен быть сценарий оболочки в дополнение к файлу Docker...

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

Докер-файл

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 , чтобы сделать это, но это имеет недостаток, заключающийся в создании нескольких слоев изображения и раздувании конечного размера изображения.

Как упоминалось выше, вы также можете обернуть команду docker build во вспомогательный скрипт сборки, чтобы создать tar-архивы, которые сохраняют структуру каталогов, и ADD в них, но это усложняет и ломает такие вещи, как docker-compose build и автоматизированные сборки Docker Hub.

Действительно, COPY должна работать точно так же, как совместимая с POSIX команда /bin/cp -r , но похоже, что это не произойдет для «обратной совместимости», даже несмотря на то, что текущее поведение совершенно не интуитивно понятно для любого с опытом. в *nix-системах.


Лучший компромисс, который я нашел, — использовать многоэтапную сборку в качестве хака:

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 .

Наличие полной команды COPY действительно поможет при попытке сохранить кеш сборки докера. Сообщество ROS разрабатывается с использованием вложенного рабочего пространства пакетов, каждый из которых объявляет зависимости в своем собственном файле package.xml . Эти файлы используются менеджером зависимостей для установки любых вышестоящих библиотек. Эти файлы package.xml изменяются относительно редко по отношению к коду в самих пакетах после того, как заложена основа. Если структура дерева каталогов была сохранена во время копирования, мы могли бы просто скопировать нашу рабочую область во время сборки докера в два этапа, чтобы максимизировать кэширование, например:

# 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

Для тех, кто хотел бы копировать с помощью глобуса, используя обходной путь с экспериментальным синтаксисом сборки, даже если кэширование не такое точное или надежное, могут взглянуть на комментарии здесь: https://github.com/moby/moby/issues .

Я все еще хотел бы, чтобы эта проблема была повторно открыта, чтобы мы могли кэшировать выборочные копии в стиле глобуса.

Я нашел относительно простой обходной путь для моего примера в 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, который восстанавливает структуру каталогов для файлов *.csproj, позволяя следовать восстановлению. См. документацию о том, как это сделать здесь .

К вашему сведению, теоретически аналогичный подход можно использовать и в других настройках, но, по сути, этот инструмент реконструирует структуру папок, поэтому я не уверен, насколько просто или даже возможно это будет, скажем, для настройки рабочих пространств lerna или пряжи. С удовольствием расследую, если есть интерес. Это было бы даже возможно в том же инструменте, если бы люди были счастливы установить среду выполнения ядра dotnet, чтобы она работала, иначе тот же подход, который я сделал, должен быть построен на языке, который не требует новой зависимости, например node Наверное.

Удивительно, что реализация команды копирования раньше была задачей студента-первокурсника, а теперь слишком сложна для опытных программистов с многолетним стажем...

Это, вероятно, не самая досадная ошибка, но, принимая во внимание, что за ней последовали многолетние обсуждения без каких-либо результатов, она, безусловно, занимает первое место.

@бенмаккаллум
К вашему сведению, теоретически аналогичный подход можно использовать и в других настройках, но, по сути, инструмент реконструирует структуру папок,

Не проще ли в большинстве случаев просто делать то, что предлагает 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...?

Изолированные этапы сборки получают независимое кэширование слоев.
Первый этап выполняет подготовительную работу. Поскольку он изначально должен копировать все, он всегда делает недействительным свой первый слой кэша и, следовательно, слои после него, когда изменяется _любой_ файл. Но это справедливо только для слоев _внутри этой стадии сборки_.

Второй этап начинается с _только_ копирования файлов, связанных с проектом, и до тех пор, пока эти файлы одинаковы, т.е. с одинаковыми именами файлов; тот же контент; и т. д. - в сборках этот слой _не_ станет недействительным. Это означает, что слой dotnet restore _также_ не будет признан недействительным, если только эти файлы проекта не изменятся.

Пришлось некоторое время сидеть с этим, и теперь я понимаю! Docker — это весело, потому что, если вы не будете постоянно проводить с ним время, вы забудете, как работают все команды. Важно отметить, что я забыл, что команда RUN может работать только с файловой системой образа докера, а не с файлами контекста сборки. Таким образом, вы вынуждены КОПИРОВАТЬ все сначала, прежде чем сможете выполнить сложную команду RUN, которая сохраняет каталоги. И именно поэтому мы так отчаянно нуждаемся в достойном COPY globbing!

Этот подход
Первоначальная команда COPY, как вы упомянули, копирует _все_, а затем извлекает файлы .sln и .csproj в отдельную папку /proj. Любое изменение кода сделает эти шаги недействительными. Важно отметить, что ограничение, которое это обходит, заключается в том, что потрясающий RUN linux cmd может работать только с файлами _уже в образе докера_, перенесенными жадным COPY ранее.

Затем запускается новый этап и копируется содержимое папки /proj, поверх которой затем можно использовать dotnet restore . Поскольку «ключ» кеша — это, по сути, хэши файлов, это редко приводит к разрушению этого уровня кеша или последующего уровня dotnet restore , поэтому вы избегаете дорогостоящего восстановления. Хороший!

Мой подход
Для этого используется только один этап сборки за счет еще нескольких команд COPY для переноса файлов, влияющих на восстановление dotnet. Я специально объединяю все файлы .csproj в один каталог, а затем использую свой глобальный инструмент для воссоздания правильной структуры каталогов из файла .sln записи. Только после этого я КОПИРУЮ все файлы src, поэтому я могу эффективно кэшировать слои вплоть до этого регулярно, вместо того, чтобы всегда КОПИРОВАТЬ все файлы src заранее.

Выводы
Я думаю, насколько эффективен каждый подход, будет зависеть от кодовой базы людей. Для нас, в монорепозитории, у нас есть МНОГО общего кода, который копируется в КОПИЮ «все src поверх». .dockerignore помогает здесь, но его сложно поддерживать, поэтому мы довольно «жадны» в этом COPY; так что это довольно медленно. Таким образом, мой подход, хотя и немного сложнее, вероятно, будет для нас быстрее, чем этот вариант.

Спасибо за объяснение. До сих пор не могу поверить, что нам вообще нужно вести этот разговор, ха-ха. Мне все еще нужно исследовать новые материалы BuildKit, чтобы увидеть, стало ли это проще. Кто-нибудь еще сделал это?

Исследование BuildKit
RUN --mount=type=bind — похоже, это позволило бы нам выполнять причудливую командную строку linux в контексте сборки (вместо того, чтобы RUN ограничивалось только файловой системой образа). Действительно, по-видимому, по умолчанию используется контекст сборки.

RUN --mount=type=cache - звучит как какой-то повторно используемый каталог кеша (между сборками докеров?), Который сохраняется? Таким образом, по сути, нам даже не нужно слишком беспокоиться о уровне кеша для восстановления пакетов, потому что с повторно используемым кешем ранее восстановленных пакетов это было бы чертовски быстрее!?

Я думаю, что проблема еще не решена, потому что она «закрыта», и люди привыкли к обходным путям.

Когда вы не понимаете, почему люди склонны переходить на другой тип контейнера.

Есть ли другой тип контейнера, который я могу использовать. Не могу поверить, что это не поддерживается после стольких лет? Является ли докер проектом с открытым исходным кодом, может ли кто-нибудь исправить его?

У нас есть опция COPY --dir в Earthly, чтобы копия вел себя как cp -r . Возможно, это тоже можно портировать в Dockerfile?

Чтобы ускорить сборку образов для приложений .net core, мы должны создать некий промежуточный контейнер, который будет содержать все восстановленные пакеты nuget. Для многопроектного решения я использовал этот обходной путь. Я просто скопировал все файлы проекта в одну папку и запустил восстановление dotnet для каждого из них. Есть некоторые предупреждения о пропущенных проектах, потому что мы не можем сохранить иерархию папок, но все же это рабочее решение.

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"]

@затуливетер
Есть некоторые предупреждения о пропущенных проектах, потому что мы не можем сохранить иерархию папок, но все же это рабочее решение.

Нет; это не работает. И вот почему:

.NET Core хранит метаданные пакета в подкаталоге ./obj , связанном с каждым проектом. Без этой информации пакет не будет считаться установленным и готовым к использованию. (Не верите мне? Тогда выбросьте свою папку ./obj , а затем, например, откройте проект в VSCode и посмотрите, как он попросит вас повторно запустить восстановление пакета. Давай, попробуй.)

Если файлы проекта, для которых вы выполняете восстановление пакета, находятся в другой структуре каталогов, чем следующая dotnet build или dotnet publish , то эти команды не увидят пакет как восстановленный.

Причина, по которой ваше решение не терпит неудачу, заключается в том, что dotnet publish и dotnet build оба подразумевают dotnet restore . Они активно проверяют невосстановленные пакеты и восстанавливают их на лету. Чтобы они этого не делали, вы должны активно передавать флаг --no-restore , чего вы не делаете.

Так что на самом деле ваше решение восстанавливает пакеты _TWICE_. Первый раз, по сути, является большой тратой времени и места, потому что он не входит в правильную структуру каталогов для повторного использования. Второй раз, неявный как часть команды publish , работает; но поскольку это часть того же уровня, что и операция сборки и публикации, ваши пакеты на самом деле вообще не кэшируются отдельно от изменений вашего кода.

@rjgotten ,
Спасибо за ваш ответ и разъяснения.
На самом деле все пакеты nuget кэшируются в папке global-packages в док-контейнере build. В моем случае это папка /root/.nuget/packages/ , а папка obj просто содержит небольшие файлы со ссылками на это глобальное хранилище, поэтому хранилище не тратится (как вы упомянули).
Второе восстановление во время публикации как минимум в 10 раз быстрее (в моем случае), потому что все nugets кэшируются в контейнере.

@zatuliveter @rjgotten спасибо за информацию в конце. Я столкнулся с похожими проблемами и придумал следующий фрагмент файла докеров, чтобы улучшить приведенные вами примеры. Баш определенно не моя сильная сторона, так что полегче со мной! Наша структура Project/Project.csproj для всех наших проектов. Это копирует все файлы проекта, перемещает их в нужное место, а затем выполняет восстановление / копирование всех / публикацию.

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 рейтинги