Moby: Dockerfile COPY com globs de arquivo copiará arquivos de subdiretórios para o diretório de destino

Criado em 26 ago. 2015  ·  54Comentários  ·  Fonte: moby/moby

Descrição do problema:
Ao usar COPY em um Dockerfile e usar globs para copiar arquivos e pastas, o docker (às vezes?) também copiará arquivos de subpastas para a pasta de destino.

$ 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

Detalhes do ambiente:
Configuração local no OSX /w boot2docker construído com docker-machine

Como reproduzir:

Contexto

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

Dockerfile

FROM busybox

RUN mkdir /test
COPY files/* /test/

Resultados reais

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

resultados esperados
A imagem resultante deve ter a mesma estrutura de diretórios do contexto

arebuilder

Comentários muito úteis

Faça um novo comando CP e acerte dessa vez por favor.

Todos 54 comentários

Mensagem original atualizada com saída de docker info e uname -a e reformatada de acordo com o modelo de relatório de problemas.

Eu tive isso em 1.6.2 e 1.8
https://gist.github.com/jrabbit/e4f864ca1664ec0dd288 diretórios de segundo nível são tratados como os de primeiro nível devem ser por algum motivo?

para aqueles que pesquisam no Google: se você está tendo problemas com COPY * /src tente COPY //src

@jfchevrette Acho que sei por que isso está acontecendo.
Você tem COPY files/* /test/ que se expande para COPY files/dir files/file1 files/file2 files/file /test/ . Se você dividir isso em comandos COPY individuais (por exemplo, COPY files/dir /test/ ) você verá que (para melhor ou para pior) COPY copiará o conteúdo de cada arg dir para o diretório de destino. Não o arg dir em si, mas o conteúdo. Se você adicionou um 3º nível de diretórios, aposto que eles permanecerão.

Não estou entusiasmado com o fato de que COPY não preserva o diretório de nível superior, mas já está assim há algum tempo.

Você pode tentar tornar isso menos doloroso copiando um nível acima na árvore src, se possível.

Estou bastante confiante de que @duglin está certo e pode ser muito arriscado mudar esse comportamento. muitos dockerfiles podem quebrar ou simplesmente copiar coisas indesejadas.

No entanto, eu diria que, a longo prazo, seria melhor se COPY estivesse seguindo a maneira como ferramentas como cp ou rsync lidam com globs e barras à direita nas pastas. Definitivamente, não é esperado que COPY copie arquivos de uma subpasta correspondente a dir/* para o IMO de destino

@jfchevrette sim - na primeira chance que tivermos, devemos "consertar" isso.
Fechando por enquanto...

@duglin então, fechar significa que não será consertado?

@tugberkugurlu sim, pelo menos por enquanto. Há trabalho em andamento para refazer toda a infraestrutura de compilação e quando fazemos isso é quando podemos fazer COPY (ou seu novo equivalente) agir da maneira que deveria.

@duglin obrigado. É possível manter este problema em aberto e atualizar o status aqui? Ou há algum outro problema para isso que eu possa assinar?

@tugberkugurlu Achei que tínhamos um problema para "suporte ao construtor do lado do cliente", mas não consigo encontrá-lo. Portanto, tudo o que podemos ter é o que o ROADMAP ( https://github.com/docker/docker/blob/master/ROADMAP.md#22-dockerfile-syntax ) diz.

Quanto a manter a questão em aberto, acho que não podemos fazer isso. A regra geral que o Docker tem seguido é fechar qualquer problema que não seja acionável imediatamente. Os problemas para trabalhos futuros são normalmente encerrados e reabertos assim que o estado das coisas muda, de modo que alguma ação (PR) possa ser tomada para o problema.

@duglin Este é um problema muito sério, você não deve apenas fechá-lo porque o problema foi introduzido na versão 0.1. Seria mais apropriado direcionar isso para a versão 2.0 (os marcos também estão no github).

Acho que a maioria das pessoas usa:

COPY . /app

e coloque na lista negra todas as outras pastas em .gitignore ou tenha uma estrutura de diretório de nível único e use COPY que na verdade tem a semântica mv :

COPY src /myapp

É muito difícil para mim imaginar que alguém realmente usaria COPY para nivelar a estrutura de diretórios. A outra solução para isso é usar tar -cf .. & ADD tarfile.tar.gz . Mudar pelo menos isso seria realmente útil. A outra coisa é respeitar as barras nos nomes dos diretórios COPY src /src vs COPY src/ /src (que atualmente são completamente ignorados).

duglin fechou isso em 1 de setembro de 2015

@duglin Esta é uma questão ridícula e irritante e não deve ser encerrada. O comando COPY se comporta especificamente em desacordo com o uso e exemplos documentados.

@tjwebb ainda há um problema em aberto https://github.com/docker/docker/issues/29211. Isso só pode ser analisado se houver uma maneira de corrigir isso que seja totalmente compatível com versões anteriores. Estamos abertos a sugestões se você tiver uma proposta _como_ isso poderia ser implementado (se você _fazer_, sinta-se à vontade para escrever isso e abrir uma proposta, linkando para esta questão). Observe que já existe uma diferença entre (por exemplo), OS X e Linux na forma cp é tratado;

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

No 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

Faça um novo comando CP e acerte dessa vez por favor.

Gostaria de repetir o acima, isso deve ter desperdiçado inúmeras horas de desenvolvimento, é extremamente não intuitivo.

+1 de mim. Este é um comportamento realmente estúpido e pode ser facilmente remediado apenas adicionando um comando CP que executa como COPY deveria ter.

"Compatibilidade com versões anteriores" é um policial fora

A versão TL;DR :

Não use COPY * /app , ele não faz o que você espera que faça.
Use COPY . /app para preservar a árvore de diretórios.

COPY só pode copiar sua subpasta.

Passei incontáveis ​​horas nisso... Por que isso funciona dessa maneira?

Estou usando o Paket e quero copiar o seguinte na estrutura correta:

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

E fazendo COPY *paket* ./ resulta nisso dentro do container:

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

Que tal adicionar um sinalizador --glob ou --recursive para COPY e ADD ?

CÓPIA DE . /destination preserva subpastas.

Três anos e isso ainda é um problema :-/

Podemos obter um ETA, quando isso será corrigido

não é um problema...
de cima...
CÓPIA DE .

É verdade que não é mais um problema depois que você fuma por meio dia e acaba aqui. Certo :)
Sejamos construtivos,

image

Nós realmente precisamos de um novo comando _CP_ ou um sinalizador --recursive para _COPY_ para que a compatibilidade com versões anteriores seja preservada.

Pontos principais se também mostrarmos um aviso na criação da imagem, como:
Directory structure not preserved with COPY *, use CP or COPY . More here <link>. se detectarmos um possível uso indevido.

Eu estou procurando por isso para copiar entre os arquivos aninhados lerna package.json em subdiretórios para melhor utilizar o cache npm install para acionar apenas quando as dependências forem alteradas. Atualmente, todos os arquivos alterados fazem com que as dependências sejam instaladas novamente.

Algo assim seria ótimo:

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

Vá verificar o número 29211, pessoal. Este foi fechado e ninguém se importa.

@zentby A conversa está aqui, o problema é rastreado lá (já que este está fechado)... É confuso.

uma solução alternativa é COPY arquivos e RUN cp -R comando

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

Isso não funcionará @instabledesign, pois o comando COPY destrói o cache quando um arquivo é diferente que não deve invalidar o cache (por exemplo, eu só quero copiar arquivos relacionados à instalação de dependência do npm, pois isso geralmente não muda)

Eu também precisava copiar apenas um conjunto de arquivos (no meu caso, arquivos *.sln e *.csproj para dotnet core) para o cache perverso. Uma solução é criar um tar ball apenas com os arquivos que você deseja e, em seguida, ADICIONAR o tarball no arquivo do Docker. Sim, agora você tem que ter um script de shell além do arquivo 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 ..

Arquivo Docker

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

Você pode usar vários comandos COPY para fazer isso, mas isso tem a desvantagem de criar várias camadas de imagem e aumentar o tamanho final da imagem.

Como kayjtea mencionado acima, você também pode envolver o comando docker build em um script de construção auxiliar para criar tarballs que preservam a estrutura do diretório e ADD neles, mas isso adiciona complexidade e quebra coisas como docker-compose build e compilações automatizadas do Docker Hub.

Realmente, COPY deve funcionar exatamente como um comando /bin/cp -r compatível com POSIX, mas parece que isso não acontecerá para 'compatibilidade com versões anteriores', mesmo que o comportamento atual seja completamente não intuitivo para qualquer pessoa com experiência em sistemas *nix.


O melhor compromisso que encontrei é usar uma compilação de vários estágios como um 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"]

Isso é autocontido em um Dockerfile e cria apenas uma camada na imagem final, assim como um ADD project.tar funcionaria.

Ter um comando COPY completo realmente ajudaria ao tentar preservar o cache de compilação do docker. A comunidade ROS desenvolve usando o espaço de trabalho aninhado de pacotes, com cada um declarando dependências em seu próprio arquivo package.xml . Esses arquivos são usados ​​por um gerenciador de dependências para instalar quaisquer bibliotecas upstream. Esses arquivos package.xml são alterados com pouca frequência para codificar nos próprios pacotes, uma vez que o trabalho de base é definido. Se a estrutura da árvore de diretórios foi preservada durante uma cópia, poderíamos simplesmente copiar nosso espaço de trabalho durante a compilação do docker em dois estágios para maximizar o cache, por exemplo:

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

Assim, o cache para a camada de instalação de dependência acima só quebraria se o desenvolvedor mudasse uma dependência declarada, enquanto uma mudança no código do pacote só quebraria a camada de compilação.

Atualmente, todos os arquivos package.xml correspondentes estão sendo copiados uns sobre os outros para a raiz do diretório de destino, com o último arquivo globalizado sendo o único package.xml que persistiu na imagem. O que é realmente pouco intuitivo para os usuários! Por que os arquivos copiados estão sendo substituídos uns sobre os outros, além do comportamento indefinido que eventualmente persiste na imagem.

Isso é uma dor em basicamente todas as pilhas que têm gerenciamento de pacotes, então isso afeta muitos de nós. Pode ser consertado? Sheesh. É um problema desde 2015! A sugestão de usar um novo comando de CP é boa.

Podemos reabrir isso? É um comportamento muito tedioso que o comando COPY use uma função interna golang para correspondência de caminho, em vez de um padrão amplamente adotado, como glob

Para aqueles que gostariam de copiar via globing usando uma solução alternativa com sintaxe de buildkit experimental, mesmo que o cache não seja tão preciso ou robusto, pode dar uma olhada nos comentários aqui: https://github.com/moby/moby/issues /39530#issuecomment -530606189

Eu ainda gostaria de ver este problema reaberto para que possamos armazenar em cache cópias seletivas de estilo glob.

Eu percebi uma solução relativamente simples para o meu exemplo em https://github.com/moby/moby/issues/15858#issuecomment -462017830 por meio de compilações de vários estágios e pensei que muitos de vocês aqui com necessidades semelhantes podem apreciar o cache arbitrário em copiado artefatos do contexto de construção. Usando compilações de vários estágios, é possível filtrar/pré-processar o diretório para armazenar em cache:

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

Para um exemplo de trabalho no mundo real, você também pode dar uma olhada aqui: https://github.com/ros-planning/navigation2/pull/1122

Eu estou procurando por isso para copiar entre os arquivos aninhados lerna package.json em subdiretórios para melhor utilizar o cache npm install para acionar apenas quando as dependências forem alteradas. Atualmente, todos os arquivos alterados fazem com que as dependências sejam instaladas novamente.

Algo assim seria ótimo:

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

estou tendo exatamente o mesmo caso de uso.

Eu estou procurando por isso para copiar entre os arquivos aninhados lerna package.json em subdiretórios para melhor utilizar o cache npm install para acionar apenas quando as dependências forem alteradas. Atualmente, todos os arquivos alterados fazem com que as dependências sejam instaladas novamente.

Algo assim seria ótimo:

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

Este caso, mas para espaços de trabalho do Yarn.

É 2020 e isso ainda não foi corrigido.

Se alguém está lutando com isso em uma configuração dotnet, eu resolvi isso para nós escrevendo uma ferramenta global dotnet core que restaura a estrutura de diretório para os arquivos *.csproj, permitindo uma restauração a seguir. Consulte a documentação sobre como fazê-lo aqui .

Para sua informação, teoricamente uma abordagem semelhante poderia ser usada em outras configurações, mas essencialmente a ferramenta é a engenharia reversa da estrutura de pastas, então não tenho certeza de quão fácil ou mesmo possível isso seria, digamos, uma configuração de espaços de trabalho de lerna ou yarn. Feliz em investigá-lo se houver interesse. Poderia até ser possível na mesma ferramenta se as pessoas estivessem felizes em instalar o runtime do dotnet core para que ele funcionasse, caso contrário, a mesma abordagem que fiz precisaria ser construída em uma linguagem que não requer uma nova dependência, como node Eu acho.

É incrível que implementar um comando de cópia costumava ser uma tarefa para um aluno no primeiro ano, e agora é muito complexo para programadores habilidosos com muitos anos de experiência...

Provavelmente não é o bug mais embaraçoso de todos os tempos, mas levando em conta que é seguido por muitos anos de discussão sem nenhuma saída, certamente chega ao topo.

@benmccallum
Para sua informação, teoricamente uma abordagem semelhante poderia ser usada em outras configurações, mas essencialmente a ferramenta é a engenharia reversa da estrutura de pastas,

Não é mais fácil para a maioria das ocasiões apenas fazer o que https://github.com/moby/moby/issues/15858#issuecomment -532016362 sugeriu e usar uma compilação de vários estágios para pré-filtrar?

Também para o caso dotnet restore é um padrão relativamente fácil:

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

Ainda não é uma desculpa apropriada para o Docker nunca ter implementado uma variante decente no comando COPY que apenas segue a semântica de sincronização normal e sã.
Quero dizer; vamos!

@rjgotten , eu gosto! Certamente muito mais fácil do que eu fiz e não consigo ver por que isso não funcionaria para minhas necessidades. Vou tentar amanhã e se isso funcionar, mudarei meus documentos para recomendar isso como uma abordagem melhor.

Acho que meu problema inicial foi que eu estava no Windows, então provavelmente rejeitei essa sugestão. Não estou mais, mas você tem uma versão equivalente do Windows para completar? Gostaria de saber se o PowerShell está pré-instalado nas imagens dotnet core...

Existe realmente uma necessidade para o adicional/repetido FROM ... embora? Toda vez que você faz um RUN ele cria uma nova camada para cache, certo? Talvez eu esteja perdendo alguma coisa, já faz um tempo desde que eu tive que pensar sobre isso!

Gostaria de saber se o PowerShell está pré-instalado nas imagens dotnet core...

Eu acho que realmente é. O que tornaria um pouco mais fácil fazer isso de maneira multiplataforma.

Existe realmente uma necessidade para o adicional/repetido FROM ... embora?

Estágios de construção isolados obtêm cache de camada independente.
A primeira etapa faz o trabalho de preparação. Como inicialmente tem que copiar tudo, ele sempre invalida o cache de sua primeira camada e, portanto, as camadas posteriores, quando _qualquer_ arquivo é alterado. Mas isso só vale para as camadas _dentro desse estágio de construção_.

A segunda etapa começa _somente_ copiando os arquivos relacionados ao projeto e contanto que esses arquivos sejam os mesmos - ou seja, os mesmos nomes de arquivo; mesmo conteúdo; etc. - em todas as compilações, essa camada _não_ invalidará. O que significa que a camada dotnet restore _also_ não será invalidada, a menos que esses arquivos de projeto sejam realmente alterados.

Tive algum tempo para sentar com isso e eu entendo agora! O Docker é divertido, pois, a menos que você sempre gaste tempo com ele, você esquece como todos os comandos funcionam. Fundamentalmente, eu tinha esquecido que o cmd RUN só pode operar no sistema de arquivos da imagem do docker, não nos arquivos de contexto de compilação. Então você é forçado a COPIAR tudo antes de poder fazer um complexo RUN cmd que preserva dirs. E é por isso que precisamos desesperadamente de um COPY decente!

Essa abordagem
O cmd COPY inicial é, como você mencionou, copiar _tudo_ e, em seguida, está retirando os arquivos .sln e .csproj para uma pasta /proj separada. Qualquer alteração de código invalidará essas etapas. Crucialmente, a limitação que isso está contornando é que o incrível cmd RUN linux só pode operar em arquivos _na imagem do docker já_, trazido pelo COPY ganancioso anterior.

Em seguida, um novo estágio é iniciado e copia o conteúdo da pasta /proj sobre o qual pode ser usado para dotnet restore . Como a "chave" do cache é essencialmente os hashes do arquivo, isso raramente quebrará essa camada de cache, nem a dotnet restore subsequente, para que você evite a restauração cara. Agradável!

Minha abordagem
Usa apenas um estágio de compilação para isso ao custo de mais alguns comandos COPY para trazer os arquivos que afetam uma restauração dotnet. Eu especificamente nivelo todos os arquivos .csproj em um diretório e, em seguida, uso minha ferramenta global para reconstruir a estrutura de diretórios correta a partir do arquivo .sln de entrada. É somente depois disso que eu COPIO todos os arquivos src, para que eu possa efetivamente armazenar em cache as camadas até aqui regularmente, em vez de sempre ter que COPIAR todos os arquivos src na frente.

Aprendizado
Acho que vai depender da base de código das pessoas a eficácia de cada abordagem. Para nós, em um repositório mono, temos MUITO código compartilhado que é copiado na CÓPIA "all src over". .dockerignore ajuda aqui, mas é difícil de manter, então somos bem "gananciosos" nesse COPY; então é bem lento. Como tal, minha abordagem, embora um pouco mais complicada, provavelmente seria mais rápida para nós do que essa alternativa.

Obrigada pelo esclarecimento. Ainda não acredito que temos que ter essa conversa haha. Ainda preciso investigar as coisas mais recentes do BuildKit para ver se isso é mais fácil agora. Alguém mais fez isso?

Investigação do BuildKit
RUN --mount=type=bind - Parece que isso nos permitiria fazer o sofisticado cmd do linux contra o contexto de compilação (em vez de RUN estar limitado apenas ao sistema de arquivos da imagem). Na verdade, aparentemente, o padrão é o contexto de construção.

RUN --mount=type=cache - soa como algum tipo de diretório de cache reutilizável (entre compilações do docker?) que é preservado? Então, em essência, nem precisaríamos nos preocupar muito com uma camada de cache para restaurações de pacotes, porque com um cache reutilizado de pacotes restaurados anteriormente, já seria muito mais rápido!?

Acho que o problema ainda não foi corrigido porque está "fechado" e as pessoas estão acostumadas com as soluções alternativas.

Quando você não entende por que as pessoas tendem a mudar para outro tipo de contêiner.

Existe outro tipo de contêiner que eu possa usar. Não posso acreditar que isso não é suportado depois de tantos anos? O docker é um projeto de código aberto, alguém pode corrigi-lo?

Temos uma opção COPY --dir no Earthly para fazer a cópia se comportar mais como cp -r . Talvez isso possa ser portado para o Dockerfile também?

Para acelerar a construção de imagens para aplicativos .net core, devemos criar algum contêiner intermediário que conterá todos os pacotes nuget restaurados. Para solução de vários projetos, usei esta solução alternativa. Acabei de copiar todos os arquivos do projeto para uma única pasta e executar a restauração dotnet para cada um deles. Existem alguns avisos sobre projetos perdidos porque não podemos preservar a hierarquia de pastas, mas ainda assim é uma solução de trabalho.

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
Existem alguns avisos sobre projetos perdidos porque não podemos preservar a hierarquia de pastas, mas ainda assim é uma solução de trabalho.

Não; isso não funciona. E aqui está o porquê:

O .NET Core armazena as meta-informações do pacote no subdiretório ./obj associado a cada projeto. Sem essa informação presente, o pacote não será considerado instalado e pronto para uso. (Não acredita em mim? Em seguida, jogue fora sua pasta ./obj e, por exemplo, abra o projeto no VSCode e observe-o pedir para você executar novamente a restauração do pacote. Vá em frente; experimente.)

Se os arquivos de projeto nos quais você executa a restauração do pacote estiverem em uma estrutura de diretório diferente da seguinte dotnet build ou dotnet publish , esses comandos não verão o pacote como restaurado.

A razão pela qual sua solução não falha completamente é porque dotnet publish e dotnet build ambos implicam dotnet restore . Eles verificam ativamente os pacotes não restaurados e os restauram rapidamente. Para evitar que eles façam isso, você precisa passar ativamente o sinalizador --no-restore , o que você não está fazendo.

Então, realmente, o que sua solução está fazendo é restaurar os pacotes _TWICE_. A primeira vez é essencialmente uma grande perda de tempo e espaço, porque não entra na estrutura de diretórios correta para ser reutilizada. A segunda vez, implícita como parte do comando publish , funciona; mas como faz parte da mesma camada que a operação de compilação e publicação, seus pacotes não estão realmente sendo armazenados em cache separados de suas alterações de código.

@rjgotten ,
Obrigado pela sua resposta e esclarecimentos.
Na verdade, todos os pacotes nuget são armazenados em cache na pasta global-packages no contêiner docker 'build'. No meu caso é a pasta /root/.nuget/packages/ , e a pasta obj contém apenas pequenos arquivos com referências a este armazenamento global, então não há desperdício de armazenamento (como você mencionou).
Segunda restauração durante a publicação pelo menos x10 vezes mais rápido (no meu caso) porque todos os nugets são armazenados em cache no contêiner.

@zatuliveter @rjgotten obrigado pela informação no final aqui. Eu estava enfrentando problemas semelhantes e criei o seguinte arquivo docker para melhorar os exemplos que você deu. Bash certamente não é meu forte, então pegue leve comigo! Nossa estrutura é Project/Project.csproj para todos os nossos projetos. Isso copia todos os arquivos do projeto, move-os para o local correto e, em seguida, restaura/copia todos/publica.

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
Esta página foi útil?
0 / 5 - 0 avaliações