Scott Rossillo
Engenheiro de software, equipe inicial
A AWS anunciou recentemente uma prévia de sua nova geração de instâncias M6g do Amazon EC2 que são alimentadas por processadores AWS Graviton2 baseados em ARM de 64 bits. As vantagens projetadas de desempenho e preço em relação à última geração de instâncias x86-64 da AWS são impressionantes demais para serem ignoradas.
Embora pudéssemos simplesmente usar o Docker padrão no ARM para criar imagens para esses novos processadores AWS Graviton, há muitos benefícios em oferecer suporte a ambas as arquiteturas em vez de abandonar a versão x86—64:
- Os desenvolvedores precisam ser capazes de executar suas imagens Docker geradas por CI/CD localmente. No futuro próximo, as máquinas de desenvolvedores continuarão usando CPUs x86—64.
- Compartilhe contêineres comuns em clusters x86—64 e Graviton2.
- Execute ambientes de teste em ARM e produção em x86—64 até que Graviton2 saia da versão prévia.
- Quando o Graviton2s estiver disponível ao público em geral, volte rapidamente para x86-64 se uma migração de serviço para o ARM causar algum problema.
A criação de imagens Docker com várias arquiteturas ainda é um recurso experimental. No entanto, a hospedagem de imagens com várias arquiteturas já é bem suportada pelo Docker's Registry, tanto auto-hospedado quanto em hub.docker.com. Sua quilometragem pode variar com implementações de registro Docker de terceiros
Neste post, demonstraremos como criar e publicar imagens Docker de várias arquiteturas em um host ARM Linux para x86—64 (AMD64) e ARM64 para que você possa executar um contêiner Docker a partir da imagem em qualquer arquitetura.
Observação: se você quiser criar suas imagens em seu desktop macOS ou Windows, o Docker Desktop vem pronto para uso com suporte para criar imagens Docker de várias arquiteturas. No entanto, se você executa o Linux ou deseja criar suas imagens do Docker corretamente, como parte do seu pipeline de CI/CD, continue lendo.
Instale o Docker 19.03 ou posterior
Para começar, precisaremos de um host Linux ARM64 capaz de executar o Docker 19.03 ou posterior. Você também pode usar um host x86—64.
No entanto, como queremos nos beneficiar da economia de custos do ARM, usaremos um como nosso servidor de compilação com o Ubuntu 19.10. O Ubuntu é uma distribuição Linux popular suportada por vários serviços em nuvem, no entanto, outras distribuições recentes também devem funcionar bem. No entanto, você precisará se certificar de que está executando um kernel Linux 5.x ou posterior. Na AWS, você pode usar a AMI do Ubuntu 19.10.
No Ubuntu, instale docker.io
para o repositório do Ubuntu. Também instalamos o binfmt-support
e o qemnu-user-static.
O QEMU permite que um único host crie imagens para várias arquiteturas e o binfmt-support adiciona suporte
a vários formatos binários ao kernel Linux. Observe que o binfmt-support
versão 2.1.43 ou mais tarde é necessário.
Adicione seu usuário ao grupo Docker para permitir que os comandos sejam executados a partir da sua conta de usuário. Lembre-se de reinicializar ou sair e entrar novamente depois de executar:
1. #! /bin/bash #Install Dependências do Docker e de vários arcos
2.
3. Sudo apt-get install binfmt-support qemu-user-static
4. Sudo apt-get instale docker.io
5. sudo usermod -aG docker $ userP
6. reinicialização do sudo
Instale o Docker Buildx
Em seguida, precisamos instalar o comando buildx
do Docker. O Buildx está em versão prévia de tecnologia e oferece recursos de construção experimentais, como construções com várias arquiteturas. Se você habilitou o docker para ser executado como seu usuário, você pode instalá-lo como seu usuário regular, em vez de root.
Instale o plug-in de linha de comando buildx
para Docker. O código abaixo instalará a versão mais recente do ARM de 64 bits.
1. #! /bin/bash
2. #Install buildx para arm64 e habilite o plug-in Docker CLI
3.
4. sudo apt-get install jq
5. mkdir -p ~/.docker/cli-plugins
6. BUILDX_URL=$ (curl) https://api.github.com/repos/docker/buildx
/releases/latest | jq -r .assets [] .browser_download_url | grep arm64
7. wget $BUILDX_URL -O ~/.docker/cli-plugins/docker-build
8. chmod +x ~/.docker/cli-plugins/docker-buildx
Crie imagens de várias arquiteturas
A criação de imagens com várias arquiteturas (a documentação do Docker se refere a elas como imagens multiplataforma) requer um construtor apoiado pelo driver
docker-container
e oferece suporte a duas estratégias para criar imagens multiplataforma:
- Usando o suporte à emulação QEMU no kernel
- Construindo em vários nós nativos coordenados por um único construtor
Aqui, estamos usando a abordagem QEMU, pois é a mais barata das duas opções, pois requer apenas um único host de compilação para todas as arquiteturas de destino. Além disso, o Docker não está usando o QEMU aqui para criar uma máquina virtual totalmente funcional,. Estamos usando o modo de usuário do QEMU, portanto, somente as chamadas do sistema precisam ser emuladas.
À medida que suas necessidades de CI/CD evoluem, talvez você queira investir em um conjunto de nós nativos para acelerar o processo de construção.
Vamos criar um bootstrap para o construtor, você pode dar a ele o nome que quiser:
1. $ docker buildx create --name mbuilder
2. mbuilder
3.
4. $ docker buildx usa mbuilder
5.
6. $ docker buildx inspect --bootstrap
7. Nome: mbuilder
8. Driver: docker-container
9.
10. Nódulos:
11. Nome: mbuilder0
12. Ponto final: unix: ///var/run/docker.sock
13. Status: em execução
14. Plataformas: linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le,
linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Perfeito, agora temos um construtor capaz de focar em linux/arm64, linux/amd64 e outras arquiteturas!
Agora vamos criar uma imagem que possa ser executada no Linux amd64 e arm64 a partir de um Dockerfile simples.
Observe que a imagem da qual você está extraindo também deve suportar as arquiteturas que você planeja segmentar. Isso pode ser verificado usando:
$ docker buildx imagetools inspecione alpine
Dockerfile:
DE alpine
EXECUTE apk add util-linux
CMD [" lscpu "]
$ docker buildx build --platform linux/amd64, linux/arm64 -t foo4u/demo-mutliarch:2 --push.
[+] Edifício 4.7s (9/9) CONCLUÍDO
= > [internal] carregar definição de compilação do Dockerfile
= > = > transferindo dockerfile: 31B
= > [interno] carregar .dockerignore
= > = > transferindo contexto: 2B
= > [linux/amd64 internal] carregar metadados para docker.io/library/alpine:latest
= > [linux/arm64 internal] carregar metadados para docker.io/library/alpine:latest
= > [linux/amd64 1/2] DE docker.io/library/alpine @sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
= > = > resolve docker.io/library/alpine @sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
= > EM CACHE [linux/amd64 2/2] EXECUTE apk add util-linux
= > [linux/arm64 1/2] DE docker.io/library/alpine @sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
= > = > resolve docker.io/library/alpine @sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
= > EM CACHE [linux/arm64 2/2] EXECUTE apk add util-linux
= > exportando para imagem
= > = > exportando camadas
= > = > exportando o manifesto sha256:cb54200a7c04dded134ca9e3e6a0e434c2fdf851fb3a7226941d0983ad5bfb88
= > = > exportando a configuração sha 256:307 b885367f8ef4dc443dc35d6ed3298b9a3a48a846cf559a676c028a359731b
= > = > exportando o manifesto sha 256:6 f4fe17def66ef5bc79279448e1cb77a1642d460ed58d5dc60d0e472c023e2eb
= > = > exportando a configuração sha 256:26 e6b092c7c1efffe51ce1d5f68e3359ab44152d33df39e5b85cd4ff6cfed3d4
= > = > exportando a lista de manifestos sha 256:3 b4e4135b92017e5214421543b813e83a77fcea759af8067c685b70a5d978497
= > = > empurrando camadas
= > = > enviando manifesto para docker.io/foo4u/demo-mutliarch:2
Há muita coisa acontecendo aqui, então vamos desvendá-la:
1. O Docker transfere o contexto de construção para nosso contêiner de construção
2. O construtor cria uma imagem para cada arquitetura que solicitamos com o argumento --platform
3. As imagens são enviadas para o Docker Hub
4. O Buildx gera um arquivo JSON de manifesto e o envia para o Docker Hub como a tag de imagem.
Vamos usar imagetools para
inspecionar a imagem do Docker gerada:
1. $ docker buildx imagetools inspeciona foo4u/demo-mutliarch:2
2. Nome: docker.io/foo4u/demo-multiarch:2
3. Tipo de mídia: application/vnd.docker.distribution.manifest.list.v2+json
4. Resumo: sha 256:3 b4e4135b92017e5214421543b813e83a77fcea759af8067c685b70a5d978497
5.
6. Manifestos:
7. Nome: docker.io/foo4u/demo-mutliarch:2 @sha256:cb54200a7c04dded134ca9e3e6a0e434c2fdf851fb3a7226941d0983ad5bfb88
8. Tipo de mídia: application/vnd.docker.distribution.manifest.v2+json
9. Plataforma: linux/amd64
10.
11. Nome: docker.io/foo4u/demo-12. multiarch:2 @sha256:6f4fe17def66ef5bc79279448e1cb77a1642d460ed58d5dc60d0e472c023e2eb
12. Tipo de mídia: application/vnd.docker.distribution.manifest.v2+json
13. Plataforma: linux/arm64
Aqui podemos ver que foo4u/demo-multiarch:2
é um manifesto JSON apontando para os manifestos de cada uma das plataformas que visamos durante a construção. Embora a imagem apareça no registro como uma única imagem, na verdade é um manifesto contendo links para as imagens específicas da plataforma. O Buildx construiu e publicou uma imagem por arquitetura e, em seguida, gerou um manifesto ligando-as.
O Docker usa essas informações ao extrair a imagem para baixar a imagem apropriada para a arquitetura de tempo de execução da máquina.
Vamos executar a imagem em x86—64/amd64:
$ docker run --rm foo4u/demo-multiarch:2
Não foi possível encontrar a imagem 'foo4u/demo-mutliarch:2' localmente
2: Extraindo de foo4u/demo-multiarch
e6b0cf9c0882: Já existe
Status: Baixou a imagem mais recente para foo4u/demo-mutliarch:2
Arquitetura: x86_64
Agora vamos executar a imagem no arm64:
$ docker run --rm foo4u/demo-multiarch:2
Não foi possível encontrar a imagem 'foo4u/demo-mutliarch:2' localmente
2: Extraindo de foo4u/demo-multiarch
Status: Baixou a imagem mais recente para foo4u/demo-mutliarch:2
Arquitetura: aarch64
É isso aí! Agora temos uma imagem Docker totalmente funcional que pode ser executada em nossos servidores x86-64 existentes ou em nossos novíssimos servidores ARM 64!
Concluindo, começar a usar imagens Docker de várias arquiteturas no Linux não é tão difícil. Podemos até mesmo usar um servidor ARM para criar as imagens, potencialmente economizando dinheiro em nossos servidores de CI/CD, bem como em nossa infraestrutura de preparação e produção.
Bônus: você pode otimizar ainda mais suas compilações do Docker se a linguagem usada tiver um bom suporte a várias arquiteturas (como Java ou Go). Por exemplo, você pode criar um aplicativo Spring Boot com uma única compilação de plataforma:
1. DE --platform=$buildPlatform amazoncorretto:11 como construtor
2.
3. COPIAR. /srv/
4. WORKDIR /srv
5. CORRER. /mvnw -dskipTests=pacote verdadeiro spring-boot:repackage
6.
7. DE amazoncorretto:11
8.
9. COPY --from=builder /srv/target/my-service-0.0.1-snapshot.jar /srv/
10.
11. EXPOR 8080
12.
13. PONTO DE ENTRADA [" java ", " -jar ", " /srv/my-service-0.0.1-SNAPSHOT.jar "]