DEV Community

Jonathan
Jonathan

Posted on

Construir imágenes de Docker Multi-arch utilizando Manifest, Buildx y GitHub Actions

Cuando trabajas en entornos de arquitectura uniforme es bastante fácil lidiar con la construcción de imágenes de docker, todos trabajando bajo la misma arquitectura “amd64” por ejemplo.
Pero qué sucede cuando trabajas con una Macbook Air con un chip M1 que es “arm64”, y tiene que generar imágenes para “amd64”, o a la inversa, trabajas con un equipo “amd64” y tienes que levantar contenedores en un cluster de Raspberry PI o servidores ARM, que están ganando cada vez más popularidad entre los proveedores de cloud.

El objetivo de hoy es enseñar desde mi perspectiva y en español a generar imágenes docker multiarquitectura o multi-arch y publicarlas en el Docker Hub.

Lo primero es tener una aplicación en mi caso utilizaré un archivo simple de Python que nos obtenga información acerca del actual sistema. Para lo cual utilizaremos la librería platform

#main.py
import platform
platform_var = platform.uname()
print ("*"*30)
print (f'Machine:\t{platform_var.machine}')
print (f'System:\t\t{platform_var.system}')
print (f'Node:\t\t{platform_var.node}')
print (f'Release:\t{platform_var.release}')
print (f'Version:\t{platform_var.version}')
print (f'Processor:\t{platform_var.processor}')
print ("*"*30)
Enter fullscreen mode Exit fullscreen mode

Lo siguiente es armar nuestro Dockerfile normalmente de la siguiente manera, esta es la versión inicial, más adelante le haremos pequeñas modificaciones para poder visualizar en qué arquitectura estamos construyendo

FROM python:alpine3.10
WORKDIR /app
COPY . .
CMD [ "python", "main.py" ]
Enter fullscreen mode Exit fullscreen mode

Hasta este punto no tenemos nada del otro mundo podemos generar la imagen y hacerla correr.

docker build . -t jevillanueva/multiarch:latest
docker run jevillanueva/multiarch:latest
Enter fullscreen mode Exit fullscreen mode

Donde tenemos una salida similar a esta:


Machine: x86_64
System: Linux
Node: 8ca0b41e5372
Release: 4.19.128-microsoft-standard
Version: #1 SMP Tue Jun 23 12:58:10 UTC 2020
Processor:


Hasta aquí ya sabemos que funciona lo siguiente es modificar el Dockerfile donde añadiremos un par de cosas para poder visualizar lo que pasa.

FROM  --platform=$TARGETPLATFORM python:alpine3.10
ARG TARGETPLATFORM
WORKDIR /app
RUN  echo "construyendo para $TARGETPLATFORM" > /log
COPY . .
CMD [ "python", "main.py" ]

Enter fullscreen mode Exit fullscreen mode

Añadimos la opción platform y el argumento “TARGETPLATFORM”, en este caso

Existen dos caminos para realizar el proceso multi-arch, la primera la más antigua xD, la describiremos a continuación nos servirá para entender cómo funciona.

DOCKER MANIFEST

Todas las imágenes de docker cuentan con un manifest, un manifest contiene información acerca de la imagen, sus layers o capas, el tamaño y su digest que es un id inmutable para cada imagen, para nuestro caso de multi-arch esto es vital ya que en el manifiesto se encuentra detallado para imágenes con funcionalidades idénticas pero para diferentes sistemas operativos y arquitecturas. Puede ver mas de manifest

Vamos a inspeccionar el manifiesto de la imagen base que utilizamos que es “python:alpine3.10”

docker manifest inspect python:alpine3.10
Enter fullscreen mode Exit fullscreen mode

Tendremos una salida similar a esta que nos muestra el tamaño, su digest, las plataformas, el sistema operativo y sus arquitecturas

{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:655edcda221823fcdb79b61095dd77e6c767bf1543505dcf078f6945497c7fcf",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:16779e4847cac747c0496fc56d0daa7c2090ea6cb2e4c9aa455672d6818d7179",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v6"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:3dee5de82f12477d1f0a8ed0836181064863ba84cd8098b9c62c65f8347c656b",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:001faf6c4b5e87a3a4b251e72bb9a9dd1a5ef93083fc3a42d54e8734dc76e1f5",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "v8"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:42829bfc005ccdd282d82da1328c9a2511e20f09439c702311d30627c91c70ca",
         "platform": {
            "architecture": "386",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:52e6ffac79478822ea62492d73c7aac3eca9a1cf41e8531899744e8e4958c236",
         "platform": {
            "architecture": "ppc64le",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1368,
         "digest": "sha256:90f421378ca9b94b071a20e7c4b2df746d9b4bc26dcbd85ea79f0db9322d330f",
         "platform": {
            "architecture": "s390x",
            "os": "linux"
         }
      }
   ]
}
Enter fullscreen mode Exit fullscreen mode

Hasta aquí ya comprendemos a grandes rasgos qué papel importante juega manifest, ahora nos utilizaremos para crear las imágenes multi-arch así que manos a la obra, realizaremos para dos arquitecturas amd64 y arm64.

Imagenes usando manifest

Lo primero es construir las imágenes para cada arquitectura

# AMD64
docker build . -t jevillanueva/multiarch:manifest-amd64 --build-arg TARGETPLATFORM=amd64
docker push jevillanueva/multiarch:manifest-amd64

# ARM64
docker build . -t jevillanueva/multiarch:manifest-arm64 --build-arg TARGETPLATFORM=arm64
docker push jevillanueva/multiarch:manifest-arm64
Enter fullscreen mode Exit fullscreen mode

Ya tenemos construidas nuestras dos imágenes para diferentes arquitecturas y publicadas en el Docker Hub puede verse algo así

Docker Hub Con Manifest

Ahora creemos el manifiesto que listará estas dos imágenes con diferentes tags en una sola unidad.

docker manifest create jevillanueva/multiarch:manifest-latest --amend jevillanueva/multiarch:manifest-amd64 --amend jevillanueva/multiarch:manifest-arm64
Enter fullscreen mode Exit fullscreen mode

Y Posterior a este realizamos el publicado del nuevo manifiesto

docker manifest push jevillanueva/multiarch:manifest-latest
Enter fullscreen mode Exit fullscreen mode

Como se puede visualizar ahora en nuestro registry se encuentra un nuevo tag que hace referencia a las dos imágenes y sus respectivos sistemas operativos y arquitecturas

hub Nuevo Manifiesto

Podemos probar las imágenes basadas en manifest desde diferentes instancias para obtener los resultados de la arquitectura mediante python

docker run jevillanueva/multiarch:manifest-latest
Enter fullscreen mode Exit fullscreen mode

He aquí el resultado desde mi máquina personal de amd64 y desde una máquina arm64

Ejecuciones

Hasta aquí es la forma clásica y detallada para comprender el porqué de las cosas xD ahora algo mucho más rápido actualmente existen una nueva herramienta desarrollada por Docker que podemos utilizar para estos escenarios llamada “Buildx”

DOCKER BUILDX

Docker buildx es una herramienta que nos permite acceder a las características de la herramienta de construcción moby buildkit, nos permite soportar multiples instancias asi que procedemos a crear nuestras imágenes para diferentes plataformas, creamos la instancia para buildx llamada “mybuild” y usamos esa instancia

docker buildx create --name mybuild
docker buildx use mybuild
Enter fullscreen mode Exit fullscreen mode
docker buildx build . --push -t jevillanueva/multiarch:buildx-latest --platform linux/amd64,linux/arm64
Enter fullscreen mode Exit fullscreen mode

Después de un proceso de descarga y compilación utilizando moby/buildkit se publica la imagen en el docker hub y se tiene un resultado similar que usando manifest

Construyendo con buildx

Del mismo modo procedemos a probar desde diferentes dispositivos con diferentes arquitecturas.

prueba con buildx

Como se puede ver existen varias maneras de realizar esto. Y para finalizar como un extra les dejo un modelo de para github actions para generar imágenes multiarch utilizando qemu para virtualizar las arquitecturas.

Github Actions

name: Docker Image CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

env:
  IMAGE_NAME: ${{ github.repository }}
  IMAGE_TAG: ${{ github.sha }}

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - 
      name: Checkout
      uses: actions/checkout@v2
    -
      name: Set up QEMU
      uses: docker/setup-qemu-action@v2
    -
      name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Docker Login
      env:
        DOCKER_USER: ${{secrets.DOCKER_USER}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      run: |
        docker login -u $DOCKER_USER -p $DOCKER_PASSWORD 
    -
      name: Build and push
      uses: docker/build-push-action@v3
      with:
        context: .
        platforms: linux/amd64,linux/arm64
        push: true
        tags: ${{ env.IMAGE_NAME }}:${{env.IMAGE_TAG}},${{ env.IMAGE_NAME }}:latest

Enter fullscreen mode Exit fullscreen mode

Aquí encuentran el repositorio del proyecto

Enlaces utilizados:

https://docs.docker.com/registry/spec/manifest-v2-2/
https://docs.docker.com/engine/reference/commandline/manifest/
https://docs.docker.com/build/buildx/multiplatform-images/
https://github.com/docker/setup-buildx-action#with-qemu

Ahí les dejo mi página personal jevillanueva.dev

Top comments (0)