Como acelerar compilações do Docker e otimizar o cache com “COPY –link”

0
31


COPY --link é um novo recurso no BuildKit que pode acelerar substancialmente as compilações de imagens do Docker. Ele funciona copiando arquivos em camadas de imagem separadas que não dependem da presença de seus predecessores. Você pode adicionar novo conteúdo a imagens sem a imagem base existente em seu sistema.

Esse recurso foi adicionado como parte do Buildx v0.8 em março de 2022. Ele está incluído no Docker CLI versão 20.10.14, portanto, você já deve ter acesso se estiver executando a versão mais recente.

Neste artigo, mostraremos o que --link faz e explica como funciona. Veremos também algumas das situações em que não deveria ser usado.

O que é “–link”?

--link é um novo argumento opcional para o Dockerfile existente COPY instrução. Ele muda a maneira como as cópias funcionam criando uma nova camada de instantâneo toda vez que você a usa.

Regular COPY As instruções adicionam arquivos à camada que os precede no Dockerfile. O conteúdo dessa camada deve existir em seu disco para que o novo conteúdo possa ser mesclado em:

FROM alpine
COPY my-file /my-file
COPY another-file /another-file

As cópias do Dockerfile acima my-file na camada produzida pelo comando anterior. Depois da FROM instrução, a imagem consiste no conteúdo Alpine:

bin/
dev/
etc/
...

O primeiro COPY instrução produz uma imagem que inclui tudo da Alpine, bem como o my-file processos:

my-file
bin/
dev/
etc/
...

E o segundo COPY a instrução adicionar another-file acima desta imagem:

another-file
my-file
bin/
dev/
etc/
...

A camada produzida por cada declaração inclui tudo o que veio antes, bem como tudo o que foi adicionado recentemente. No final da compilação, o Docker usa um processo de diferenciação para calcular as alterações em cada camada. O blob de imagem final contém apenas os arquivos que foram adicionados em cada estágio do instantâneo, mas isso não se reflete no processo de montagem durante a compilação.

Apresentando “–link”

“–Link” modifica COPY para criar um novo sistema de arquivos independente cada vez que for usado. Em vez de copiar os novos arquivos no topo da camada antiga, eles são enviados para um local completamente diferente para se tornarem uma camada separada. Posteriormente, as camadas são unidas para produzir a imagem final.

Vamos alterar o Dockerfile de exemplo para usar --link:

FROM alpine
COPY --link my-file /my-file
COPY --link another-file /another-file

o resultado da FROM a instrução não mudou: ela produz a camada Alpine, com todo o conteúdo dessa imagem:

bin/
dev/
etc/
...

O primeiro COPY instrução tem um efeito marcadamente diferente. Desta vez, outra camada separada é criada. É um novo sistema de arquivos contendo apenas my-file:

my-file

então o segundo COPY declaração cria outro novo instantâneo com apenas another-file:

another-file

Quando a compilação é concluída, o Docker armazena esses instantâneos separados como novos tarballs. Os tarballs são ligados de volta à cadeia de camadas anteriores, construindo a imagem final. Isso consiste em todos os três instantâneos mesclados, resultando em um sistema de arquivos que corresponde ao original quando os contêineres são criados:

my-file
another-file
bin/
dev/
etc/
...

Esta imagem do projeto BuildKit ilustra as diferenças entre as duas abordagens.

Imagem do blog do Docker mostrando as diferenças entre

Adicionando “COPY –link” às suas compilações

COPY --link ele só está disponível quando você usa o BuildKit para criar suas imagens. Execute sua compilação com docker buildx --create ou usar docker build com ele DOCKER_BUILDKIT=1 conjunto de variáveis ​​de ambiente.

Você também deve aceitar a sintaxe do Dockerfile v1.4 usando um comentário na parte superior do arquivo:

# syntax=docker/dockerfile:1.4
FROM alpine:latest
COPY --link my-file /my-file
COPY --link another-file /another-file

Agora você pode construir sua imagem com suporte para cópias vinculadas:

DOCKER_BUILDKIT=1 docker build -t my-image:latest .

Imagens criadas a partir de Dockerfiles usando COPY --link pode ser usado como qualquer outro. Você pode iniciar um contêiner com docker run e enviá-los diretamente para os registros. a --link sinalizador afeta apenas como o conteúdo é adicionado às camadas da imagem durante a compilação.

Por que as cópias vinculadas são importantes

Usando o --link permitir que os caches de compilação sejam reutilizados mesmo quando o conteúdo COPY em mudanças. Além disso, as compilações podem ser concluídas sem que sua imagem base exista em sua máquina.

Voltando ao exemplo anterior, padrão COPY O comportamento exige alpine Existe uma imagem em seu host do Docker antes que o novo conteúdo possa ser adicionado. A imagem será baixada automaticamente durante a compilação se você não a extraiu anteriormente.

Com cópias vinculadas, o Docker não precisa do alpine conteúdo da imagem. Puxe a alpine manifest, cria novas camadas independentes para os arquivos copiados e, em seguida, cria um manifesto revisado que vincula as camadas às fornecidas por alpine. o conteúdo do alpine A imagem, seus blobs de camada, será baixada apenas se você inicializar um contêiner de sua nova imagem ou exportá-lo para um arquivo tar. Quando você envia a imagem para um registro, esse registro armazenará suas novas camadas e adquirirá a imagem remotamente. alpine algum.

Essa funcionalidade também facilita rebases de imagem eficientes. Talvez você esteja distribuindo uma imagem do Docker usando a versão mais recente do Ubuntu 20.04 LTS:

FROM golang AS build
...
RUN go build -o /app .

FROM ubuntu:20.04
COPY --link --from=build /app /bin/app
ENTRYPOINT ["/bin/app"]

Você pode construir a imagem com o cache habilitado usando o BuildKit’s --cache-to bandeira. a inline cache armazena dados de cache de compilação na imagem de saída, onde podem ser reutilizados em compilações subsequentes:

docker buildx build --cache-to type=inline -t example-image:20.04 .

Agora, digamos que você queira fornecer uma imagem baseada no próximo LTS após seu lançamento, o Ubuntu 22.04:

FROM golang AS build
...
RUN go build -o /app .

FROM ubuntu:22.04
COPY --link --from=build /app /bin/app
ENTRYPOINT ["/bin/app"]

Reconstrua a imagem usando os dados de cache incorporados na versão original:

docker buildx build --cache-from example-image:20.04 -t example-image:22.04 .

A compilação será concluída quase instantaneamente. Usando os dados em cache da imagem existente, o Docker pode verificar os arquivos necessários para construir /app Eu não mudei. Isso significa que o cache para a camada separada criada pelo COPY instrução ainda é válida. Como essa camada não depende de nenhuma outra, a ubuntu:22.04 a imagem também não será extraída. O Docker simplesmente vincula a camada de instantâneo que ela contém /bin/app em um novo manifesto no ubuntu:22.04 cadeia de camadas A camada de instantâneo é efetivamente “realocada” para uma nova imagem mestre, sem que ocorra nenhuma operação do sistema de arquivos.

O modelo também otimiza compilações de vários estágios, onde podem ocorrer alterações entre qualquer um dos estágios:

FROM golang AS build
RUN go build -o /app .

FROM config-builder AS config
RUN generate-config --out /config.yaml

FROM ubuntu:latest
COPY --link --from=config /config.yaml build.conf
COPY --link --from=build /app /bin/app

Sem --linkqualquer alteração na geração config.yaml Causas ubuntu:latest a ser extraído e o arquivo a ser copiado. O binário precisa ser recompilado, pois as alterações no sistema de arquivos invalidam seu cache. Com cópias vinculadas, uma alteração na config.yaml permite que a construção continue sem puxar ubuntu:latest ou recompile o binário. A camada de instantâneo com build.conf o interior é simplesmente substituído por uma nova versão independente de todas as outras camadas.

Quando não usar

Existem algumas situações em que o --link sinalizador não funcionará corretamente. Como você está copiando arquivos para uma nova camada, em vez de adicioná-los sobre a camada antiga, não é possível usar referências ambíguas como caminho de destino:

COPY --link my-file /data

com um regular COPY instrução, my-file será copiado para /data/my-file Sim /data já existe como um diretório na imagem. Com --linko sistema de arquivos da camada de destino estará sempre vazio, então my-file está escrito para /data.

A mesma consideração se aplica à resolução de links simbólicos. Padrão COPY resolve automaticamente os caminhos de destino que são links simbólicos na imagem. quando você está usando --linkesse comportamento não é suportado porque o link simbólico não existirá na camada separada da cópia.

Recomenda-se começar a usar --link onde essas limitações não se aplicam. Adotar esse recurso acelerará suas compilações e tornará o cache mais poderoso. Se você não puder remover imediatamente os caminhos de destino ambíguos ou com links simbólicos, ainda poderá usar os existentes COPY instrução. É por causa dessas mudanças incompatíveis com versões anteriores que --link é um sinalizador opcional, em vez do novo valor padrão.

Resumo

BuildKit COPY --link é um novo recurso do Dockerfile que pode tornar as compilações mais rápidas e eficientes. Imagens que usam cópias vinculadas não precisam extrair camadas anteriores apenas para que os arquivos possam ser copiados para elas. O Docker cria uma nova camada separada para cada COPY em vez disso, vincule essas camadas novamente na cadeia.

Você pode começar a usar cópias vinculadas agora se estiver criando imagens com o BuildKit e a versão mais recente do Buildx ou Docker CLI. A adoção de “–link” é uma nova etapa de criação de práticas recomendadas do Docker, desde que não seja afetada pelas alterações de resolução do caminho de destino que ela exige.