Como usar o comando docker buildx build para construir e gerenciar imagens

DockerBeginner
Pratique Agora

Introdução

Neste laboratório, você obterá experiência prática usando o comando docker buildx build para construir e gerenciar imagens Docker. Você começará construindo uma imagem simples com as configurações padrão, aprendendo como definir instruções de imagem usando um Dockerfile.

Indo além do básico, você explorará recursos mais avançados, como a utilização de argumentos de construção e o direcionamento de estágios específicos dentro de uma construção de múltiplos estágios. Você também aprenderá como gerenciar efetivamente o cache de construção para otimizar os tempos de construção usando --cache-from e --cache-to. Além disso, o laboratório irá guiá-lo através da construção de imagens multi-plataforma e do envio delas para um registro, e demonstrará como expor com segurança segredos e agentes SSH durante o processo de construção.

Construir uma imagem simples com configurações padrão

Nesta etapa, você aprenderá como construir uma imagem Docker simples usando um Dockerfile. Um Dockerfile é um documento de texto que contém todos os comandos que um usuário pode chamar na linha de comando para montar uma imagem. O Docker pode construir imagens automaticamente lendo as instruções de um Dockerfile.

Primeiro, navegue até o diretório ~/project, que é seu diretório de trabalho para este laboratório.

cd ~/project

Agora, vamos criar um Dockerfile simples. Este Dockerfile definirá uma imagem baseada na imagem base ubuntu e simplesmente imprimirá "Hello, Docker!" quando um contêiner for executado a partir desta imagem.

Use o editor nano para criar um arquivo chamado Dockerfile no diretório ~/project.

nano Dockerfile

Adicione o seguinte conteúdo ao Dockerfile:

FROM ubuntu:latest
CMD ["echo", "Hello, Docker!"]

Vamos detalhar este Dockerfile simples:

  • FROM ubuntu:latest: Esta instrução define a imagem base para nossa nova imagem. Estamos usando a versão mais recente da imagem oficial Ubuntu do Docker Hub.
  • CMD ["echo", "Hello, Docker!"]: Esta instrução especifica o comando que será executado quando um contêiner for iniciado a partir desta imagem. Neste caso, ele executará o comando echo com o argumento "Hello, Docker!".

Salve o arquivo pressionando Ctrl + S e saia do editor nano pressionando Ctrl + X.

Agora que temos nosso Dockerfile, podemos construir a imagem usando o comando docker build. O . no final do comando diz ao Docker para procurar o Dockerfile no diretório atual (~/project). Também marcaremos a imagem com um nome, por exemplo, my-hello-image.

docker build -t my-hello-image .

Você verá uma saída indicando que o Docker está construindo a imagem camada por camada. Ele primeiro puxará a imagem ubuntu:latest se ela ainda não estiver presente em seu sistema e, em seguida, executará a instrução CMD.

Após a conclusão da construção, você pode verificar se a imagem foi criada com sucesso listando as imagens disponíveis usando o comando docker images.

docker images

Você deve ver my-hello-image listado na saída.

Finalmente, vamos executar um contêiner a partir de nossa imagem recém-construída para ver a saída da instrução CMD.

docker run my-hello-image

Você deve ver a saída "Hello, Docker!" impressa em seu terminal. Isso confirma que nossa imagem foi construída corretamente e a instrução CMD está funcionando como esperado.

Usar argumentos de construção e estágios de destino

Nesta etapa, você aprenderá como usar argumentos de construção (ARG) e estágios de destino em seu Dockerfile para criar construções mais flexíveis e eficientes. Os argumentos de construção permitem que você passe variáveis para o processo de construção, enquanto os estágios de destino permitem que você defina vários estágios de construção dentro de um único Dockerfile e construa apenas um estágio específico.

Primeiro, certifique-se de estar no diretório ~/project.

cd ~/project

Vamos modificar nosso Dockerfile existente para incluir um argumento de construção e uma construção simples de múltiplos estágios. Definiremos um argumento para uma mensagem de saudação e usaremos um segundo estágio para copiar um arquivo do primeiro estágio.

Abra o Dockerfile usando nano:

nano Dockerfile

Substitua o conteúdo existente pelo seguinte:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Vamos entender as mudanças:

  • ARG GREETING="Hello from build argument!": Esta instrução define um argumento de construção chamado GREETING com um valor padrão. Você pode substituir este valor padrão durante o processo de construção.
  • FROM ubuntu:latest as builder: Isso inicia o primeiro estágio de construção e o nomeia builder. Isso é útil para construções de múltiplos estágios.
  • RUN echo $GREETING > /app/greeting.txt: No estágio builder, este comando cria um arquivo chamado greeting.txt no diretório /app e grava o valor do argumento GREETING nele.
  • FROM ubuntu:latest: Isso inicia o segundo estágio de construção. Por padrão, esta será a imagem final.
  • COPY --from=builder /app/greeting.txt /greeting.txt: Esta instrução copia o arquivo greeting.txt do estágio builder (especificamente de /app/greeting.txt) para o diretório raiz (/) do estágio atual. É assim que você transfere artefatos entre estágios em uma construção de múltiplos estágios.
  • CMD ["cat", "/greeting.txt"]: Isso define o comando a ser executado quando um contêiner da imagem final é executado. Ele imprimirá o conteúdo do arquivo /greeting.txt.

Salve o Dockerfile e saia do nano.

Agora, vamos construir a imagem usando o comando docker build. Usaremos a flag --build-arg para passar um valor personalizado para o argumento GREETING. Também marcaremos esta imagem como my-arg-image.

docker build --build-arg GREETING="Greetings from the command line!" -t my-arg-image .

Observe a saída. Você deve ver o processo de construção usando o argumento de construção fornecido.

Após a conclusão da construção, liste as imagens para confirmar que my-arg-image está presente.

docker images

Agora, execute um contêiner de my-arg-image para ver a saída.

docker run my-arg-image

Você deve ver "Greetings from the command line!" impresso, confirmando que o argumento de construção foi usado.

Em seguida, vamos construir apenas o estágio builder. Isso é útil para criar imagens intermediárias que contêm artefatos de construção sem a aplicação final. Usamos a flag --target para especificar o nome do estágio.

docker build --target builder -t my-builder-stage .

Liste as imagens novamente. Agora você deve ver my-builder-stage além de my-arg-image.

docker images

Executar um contêiner de my-builder-stage não produzirá a mesma saída que my-arg-image porque a instrução CMD do estágio final não está incluída no estágio builder. Vamos tentar executá-lo e ver o que acontece (provavelmente apenas iniciará e sairá rapidamente, pois não há comando padrão).

docker run my-builder-stage

Isso demonstra como os estágios de destino permitem que você controle qual parte do seu Dockerfile é construída em uma imagem.

Gerenciar o cache de construção com --cache-from e --cache-to

Nesta etapa, você aprenderá como gerenciar o cache de construção do Docker usando as flags --cache-from e --cache-to. O cache de construção pode acelerar significativamente as construções subsequentes, reutilizando camadas de construções anteriores. --cache-from permite que você especifique uma imagem para usar como fonte de cache, e --cache-to permite que você exporte o cache de construção para um local especificado (como um registro ou um diretório local).

Primeiro, certifique-se de estar no diretório ~/project.

cd ~/project

Vamos modificar nosso Dockerfile ligeiramente para simular uma alteração que normalmente quebraria o cache. Adicionaremos uma instrução RUN simples.

Abra o Dockerfile com nano:

nano Dockerfile

Adicione a seguinte linha após a instrução ARG no estágio builder:

RUN echo "Adding a new layer"

O Dockerfile atualizado deve ser assim:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding a new layer"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Salve o Dockerfile e saia do nano.

Agora, vamos construir a imagem novamente sem usar nenhuma opção de cache específica. O Docker usará automaticamente o cache local, se disponível.

docker build -t my-cached-image .

Você deve ver que algumas camadas são construídas do zero porque adicionamos uma nova instrução, invalidando o cache para instruções subsequentes.

Agora, vamos simular um cenário em que você pode querer usar uma imagem construída anteriormente como fonte de cache, talvez de um registro ou outra construção. Para fins de demonstração, usaremos o my-cached-image que acabamos de construir como a fonte de cache para uma nova construção.

Primeiro, vamos fazer uma pequena alteração no Dockerfile novamente para simular outra modificação.

Abra o Dockerfile:

nano Dockerfile

Altere a mensagem na nova instrução RUN:

RUN echo "Adding another new layer"

O Dockerfile atualizado deve ser assim:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Salve e saia do nano.

Agora, construa a imagem novamente, mas desta vez use a flag --cache-from para especificar my-cached-image como a fonte de cache.

docker build --cache-from my-cached-image -t my-cached-image-from .

Você deve observar que o Docker tenta usar camadas de my-cached-image. A camada correspondente à primeira instrução RUN provavelmente será reconstruída porque a instrução foi alterada, mas as camadas subsequentes podem ser puxadas do cache se corresponderem.

A flag --cache-to é usada para exportar o cache de construção. Isso é particularmente útil em pipelines CI/CD para compartilhar o cache entre as construções. Para usar --cache-to, você normalmente precisa usar um driver de construção que suporte a exportação de cache, como o driver docker-container.

Primeiro, vamos instalar o Docker Compose, que é frequentemente usado com buildx.

sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Agora, vamos criar uma instância do construtor buildx.

docker buildx create --use

Agora, vamos construir a imagem e exportar o cache para um diretório local. Usaremos o exportador de cache local.

docker buildx build --cache-to type=local,dest=./build-cache -t my-exported-cache-image . --load
  • docker buildx build: Usa a ferramenta buildx para construir.
  • --cache-to type=local,dest=./build-cache: Exporta o cache para um diretório local chamado build-cache no diretório atual.
  • -t my-exported-cache-image: Marca a imagem resultante.
  • .: Especifica o contexto de construção (diretório atual).
  • --load: Carrega a imagem construída no cache de imagem Docker local.

Você deve ver a saída indicando a construção e a exportação do cache. Um diretório build-cache será criado em seu diretório ~/project.

Agora, vamos simular um ambiente de construção limpo e tentar construir usando o cache exportado. Primeiro, vamos remover as imagens que construímos.

docker rmi my-cached-image my-cached-image-from my-exported-cache-image

Agora, construa a imagem novamente, desta vez usando o cache exportado como fonte.

docker buildx build --cache-from type=local,src=./build-cache -t my-imported-cache-image . --load
  • --cache-from type=local,src=./build-cache: Importa o cache do diretório local build-cache.

Você deve observar que o Docker usa as camadas do cache exportado, acelerando significativamente o processo de construção em comparação com a construção do zero.

Construir imagens multi-plataforma e enviar para um registry

Nesta etapa, você aprenderá como construir imagens Docker para múltiplas arquiteturas (como linux/amd64 e linux/arm64) e enviá-las para um registro de contêineres. A construção de imagens multi-plataforma é essencial para garantir que seus aplicativos possam ser executados em diferentes tipos de hardware. Usaremos o Docker Buildx, que você inicializou na etapa anterior.

Primeiro, certifique-se de estar no diretório ~/project.

cd ~/project

Usaremos nosso Dockerfile existente para esta etapa. Vamos construir uma imagem para as plataformas linux/amd64 e linux/arm64. Marcaremos a imagem com um nome e uma versão, por exemplo, your-dockerhub-username/my-multi-platform-image:latest. Substitua your-dockerhub-username pelo seu nome de usuário real do Docker Hub. Se você não tiver uma conta do Docker Hub, poderá criar uma gratuitamente.

Para construir para múltiplas plataformas, usamos a flag --platform com docker buildx build. Também precisamos usar a flag --push para enviar a lista de manifestos resultante e as imagens para um registro.

docker buildx build --platform linux/amd64,linux/arm64 -t your-dockerhub-username/my-multi-platform-image:latest . --push

Observação: Este comando exigirá que você faça login no Docker Hub, caso ainda não o tenha feito. Você pode fazer login usando o comando docker login em uma sessão de terminal separada, se necessário.

docker login

Insira seu nome de usuário e senha do Docker Hub quando solicitado.

Após fazer login, execute o comando docker buildx build novamente.

O processo de construção levará mais tempo do que uma construção de plataforma única, pois ele constrói a imagem para cada arquitetura especificada. Após a conclusão da construção, o Buildx criará uma lista de manifestos que referencia as imagens para cada plataforma e enviará tudo para o seu repositório Docker Hub especificado.

Você pode verificar se a imagem multi-plataforma foi enviada visitando seu repositório Docker Hub em um navegador da web. Você deve ver my-multi-platform-image com a tag latest, e na guia "Tags", você deve ver que ela suporta múltiplas arquiteturas.

Alternativamente, você pode usar o comando docker buildx imagetools inspect para inspecionar a lista de manifestos que você acabou de enviar. Substitua your-dockerhub-username pelo seu nome de usuário.

docker buildx imagetools inspect your-dockerhub-username/my-multi-platform-image:latest

A saída mostrará a lista de manifestos e as diferentes imagens (com suas respectivas arquiteturas) que ela aponta.

Isso demonstra como construir e enviar imagens que podem ser executadas em diferentes arquiteturas de CPU, tornando suas imagens Docker mais versáteis.

Expor secrets e agente SSH para a construção

Nesta etapa, você aprenderá como expor com segurança segredos e seu agente SSH para o processo de construção do Docker usando o Buildx. Isso é crucial para cenários em que sua construção precisa acessar repositórios privados, instalar pacotes privados ou interagir com serviços externos que exigem autenticação, sem incorporar informações confidenciais diretamente em seu Dockerfile.

Primeiro, certifique-se de estar no diretório ~/project.

cd ~/project

Modificaremos nosso Dockerfile para demonstrar como usar um segredo durante a construção. Para este exemplo, criaremos um arquivo secreto fictício e leremos seu conteúdo durante a construção.

Crie um arquivo chamado mysecret.txt no diretório ~/project com algum conteúdo secreto.

echo "This is a secret message!" > ~/project/mysecret.txt

Agora, abra o Dockerfile com nano:

nano Dockerfile

Adicione uma nova instrução RUN no estágio builder que usa a flag --mount=type=secret para acessar o segredo.

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]

Vamos entender a nova instrução:

  • RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt: Esta instrução monta um segredo no contêiner de construção.
    • --mount=type=secret: Especifica que estamos montando um segredo.
    • id=mysecret: Este é o ID do segredo que forneceremos durante a construção.
    • cat /run/secrets/mysecret: Dentro do contêiner de construção, o segredo está disponível em /run/secrets/mysecret. Estamos usando cat para ler seu conteúdo.
    • > /app/secret_content.txt: Redirecionamos o conteúdo secreto para um arquivo chamado secret_content.txt no diretório /app dentro do estágio builder.

Também adicionamos uma instrução COPY no estágio final para copiar o arquivo secret_content.txt do estágio builder.

Salve o Dockerfile e saia do nano.

Agora, construa a imagem usando docker buildx build e forneça o segredo usando a flag --secret.

docker buildx build --secret id=mysecret,src=~/project/mysecret.txt -t my-secret-image . --load
  • --secret id=mysecret,src=~/project/mysecret.txt: Esta flag fornece o segredo para a construção.
    • id=mysecret: Corresponde ao ID especificado no Dockerfile.
    • src=~/project/mysecret.txt: Especifica o caminho para o arquivo secreto em sua máquina local.

O processo de construção agora terá acesso ao conteúdo de mysecret.txt durante a execução da instrução RUN --mount=type=secret.... O conteúdo secreto não é armazenado nas camadas finais da imagem.

Após a conclusão da construção, execute um contêiner a partir da imagem.

docker run my-secret-image

Você deve ver a mensagem de saudação e o conteúdo do seu arquivo secreto impressos no console.

Agora, vamos demonstrar como expor seu agente SSH para a construção. Isso é útil para clonar repositórios Git privados durante o processo de construção.

Primeiro, certifique-se de que seu agente SSH esteja em execução e tenha sua chave carregada. Você pode normalmente verificar isso com ssh-add -l. Se seu agente não estiver em execução ou sua chave não estiver adicionada, talvez seja necessário iniciar o agente e adicionar sua chave.

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa ## Replace with your SSH key path if different

Agora, modifique o Dockerfile para usar o agente SSH. Adicionaremos uma instrução RUN que tenta clonar um repositório privado (inexistente) usando SSH.

Abra o Dockerfile:

nano Dockerfile

Adicione uma nova instrução RUN no estágio builder que usa a flag --mount=type=ssh.

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt
RUN --mount=type=ssh git clone git@github.com:your-username/your-private-repo.git || echo "Skipping git clone as this is a demo"

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]

Observação: Substitua your-username/your-private-repo.git por um espaço reservado para uma URL de repositório privado. A parte || echo "Skipping git clone as this is a demo" é adicionada para que a construção não falhe se o repositório não existir ou você não tiver acesso.

Salve o Dockerfile e saia do nano.

Agora, construa a imagem usando docker buildx build e forneça acesso ao seu agente SSH usando a flag --ssh.

docker buildx build --ssh default -t my-ssh-image . --load
  • --ssh default: Esta flag expõe seu agente SSH padrão para a construção.

Durante a construção, a instrução RUN --mount=type=ssh... poderá usar seu agente SSH para autenticar com o servidor Git.

Após a conclusão da construção, você pode executar a imagem, embora a saída seja a mesma de antes, pois a operação git clone provavelmente foi ignorada.

docker run my-ssh-image

Isso demonstra como usar com segurança segredos e seu agente SSH durante o processo de construção do Docker com Buildx, impedindo que informações confidenciais sejam incorporadas em suas imagens.

Resumo

Neste laboratório, você aprendeu os fundamentos da construção de imagens Docker usando um Dockerfile. Você começou criando um Dockerfile simples que define uma imagem baseada no Ubuntu e executa um comando básico. Em seguida, você usou o comando docker build para construir essa imagem, entendendo como o Docker processa as instruções no Dockerfile camada por camada e como marcar a imagem resultante.

Com base nos fundamentos, você explorou recursos mais avançados do comando docker buildx build. Isso incluiu a utilização de argumentos de construção para passar valores dinâmicos para o processo de construção e o aproveitamento de estágios de destino dentro de uma construção de múltiplos estágios para otimizar o tamanho da imagem e o tempo de construção. Você também aprendeu a gerenciar o cache de construção de forma eficaz usando --cache-from e --cache-to para acelerar as construções subsequentes. Além disso, você ganhou experiência na construção de imagens multi-plataforma, permitindo que suas imagens sejam executadas em diferentes arquiteturas, e no envio dessas imagens multi-plataforma para um registro de contêineres. Por fim, você descobriu como expor com segurança segredos e utilizar um agente SSH durante o processo de construção, aprimorando a segurança e a flexibilidade de suas construções de imagem.