Aller au contenu
Conteneurs & Orchestration medium

Multi-arch et manifests : une image pour amd64 et arm64

17 min de lecture

logo podman

Ce guide vous apprend à construire une seule image qui fonctionne sur plusieurs architectures (amd64, arm64, arm/v7). Avec le même podman pull, l’image appropriée est téléchargée automatiquement.

À la fin, vous saurez :

  • Créer un manifest list (index multi-arch)
  • Construire avec émulation (simple mais lent)
  • Construire avec podman farm (natif et rapide)
  • Pousser vers un registry avec un seul tag

Le besoin : une image pour plusieurs architectures

Section intitulée « Le besoin : une image pour plusieurs architectures »

Vous avez une image qui fonctionne sur votre machine de développement (probablement amd64). Mais :

  • Vos serveurs ARM en production ne peuvent pas l’exécuter
  • Vos Raspberry Pi ne peuvent pas l’exécuter
  • Vos Mac M1/M2 (arm64) ne peuvent pas l’exécuter nativement

Erreur typique :

Fenêtre de terminal
podman run myapp:1.0
# exec format error

Un manifest list (ou “fat manifest”) est un index qui pointe vers plusieurs images, une par architecture :

Manifest list multi-architecture : index pointant vers images amd64, arm64, arm/v7

Quand vous exécutez podman pull myapp:1.0, Podman :

  1. Télécharge le manifest list
  2. Identifie l’architecture de votre machine
  3. Télécharge l’image correspondante

Résultat : le même tag fonctionne partout.

Un manifest list est un fichier JSON stocké dans le registry :

{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:abc123...",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:def456...",
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
]
}

Les architectures les plus fréquentes sont amd64 (serveurs, cloud) et arm64 (Raspberry Pi, Mac M1/M2, AWS Graviton). Les autres sont rares mais utiles pour des cas spécifiques (mainframes IBM, vieux matériel).

ArchitectureMachinesExemples
linux/amd64x86_64, Intel/AMDServeurs, cloud, laptops Linux/Windows
linux/arm64ARM 64-bitRaspberry Pi 4+, Mac M1/M2, AWS Graviton
linux/arm/v7ARM 32-bitRaspberry Pi 2/3 (mode 32-bit)
linux/arm/v6ARM vieuxRaspberry Pi Zero
linux/386x86 32-bitVieux serveurs
linux/s390xIBM ZMainframes
linux/ppc64leIBM PowerPower servers
Fenêtre de terminal
# Voir les architectures disponibles
podman manifest inspect docker.io/library/nginx:alpine
# OU avec skopeo (plus lisible)
skopeo inspect --raw docker://docker.io/library/nginx:alpine | jq

L’émulation permet de construire une image arm64 sur une machine amd64 (et vice versa). C’est la solution la plus simple, mais la plus lente.

QEMU : émulation arm64 sur machine amd64

Fenêtre de terminal
# Installer qemu-user-static
sudo dnf install qemu-user-static
# Vérifier
ls /usr/bin/qemu-*-static
Fenêtre de terminal
# Build pour arm64 (émulé sur machine amd64)
podman build --platform linux/arm64 -t myapp:arm64 .
# Build pour amd64 (natif sur machine amd64)
podman build --platform linux/amd64 -t myapp:amd64 .

L’émulation a un coût : QEMU traduit chaque instruction CPU en temps réel. Pour un npm install ou une compilation Go, le ralentissement est significatif. Privilégiez l’émulation pour les tests rapides et podman farm pour les builds de production.

Architecture cibleSur machine amd64Temps relatif
linux/amd64Natif
linux/arm64Émulé5-10×
linux/arm/v7Émulé5-10×

podman farm distribue les builds sur des machines distantes de chaque architecture. Les builds sont natifs, donc rapides.

Podman farm : builds natifs distribués sur machines amd64 et arm64

Pour chaque machine distante :

  1. Podman installé
  2. SSH accessible depuis la machine locale
  3. Podman socket activé :
Fenêtre de terminal
# Sur chaque machine distante
systemctl --user enable --now podman.socket
  1. Ajouter les connexions Podman

    Fenêtre de terminal
    # Machine amd64
    podman system connection add amd64 ssh://user@amd64-server.local/run/user/1000/podman/podman.sock
    # Machine arm64
    podman system connection add arm64 ssh://user@arm64-server.local/run/user/1000/podman/podman.sock
    # Vérifier
    podman system connection list
  2. Créer la farm

    Fenêtre de terminal
    podman farm create my-farm amd64 arm64
    # Vérifier
    podman farm list
  3. Tester

    Fenêtre de terminal
    podman farm list
    Résultat
    Name Connections
    my-farm amd64, arm64
Fenêtre de terminal
podman farm build \
--farm my-farm \
--platforms linux/amd64,linux/arm64 \
-t registry.example.com/myapp:1.0 \
.

Ce qui se passe :

  1. Le Containerfile est envoyé aux machines distantes
  2. Chaque machine build pour son architecture (natif)
  3. Les images sont poussées vers le registry
  4. Un manifest list est créé avec les deux images

Les options principales contrôlent la farm utilisée, les architectures cibles, et si le build doit aussi s’exécuter localement.

OptionDescriptionExemple
--farmNom de la farm--farm my-farm
--platformsArchitectures cibleslinux/amd64,linux/arm64
-t, --tagTag de l’image-t registry/app:1.0
--localBuild aussi sur la machine locale--local

Si vous avez déjà des images construites (par émulation ou farm), vous pouvez créer manuellement un manifest list.

Les sous-commandes suivent un workflow séquentiel : créer d’abord le manifest vide, puis ajouter les images une par une, puis pousser le tout vers un registry.

Sous-commandeAction
manifest createCréer un nouveau manifest list
manifest addAjouter une image au manifest
manifest inspectVoir le contenu du manifest
manifest pushPousser le manifest vers un registry
manifest rmSupprimer un manifest local
  1. Construire les images pour chaque architecture

    Fenêtre de terminal
    # Sur machine amd64
    podman build --platform linux/amd64 -t myapp:amd64 .
    # Sur machine arm64 (ou émulé)
    podman build --platform linux/arm64 -t myapp:arm64 .
  2. Créer le manifest list

    Fenêtre de terminal
    podman manifest create myapp:1.0
  3. Ajouter les images

    Fenêtre de terminal
    # Pour des images locales, préfixer avec containers-storage:
    podman manifest add myapp:1.0 containers-storage:localhost/myapp:amd64
    podman manifest add myapp:1.0 containers-storage:localhost/myapp:arm64
  4. Vérifier

    Fenêtre de terminal
    podman manifest inspect myapp:1.0
    Résultat (simplifié)
    {
    "manifests": [
    {
    "platform": {"architecture": "amd64", "os": "linux"},
    "digest": "sha256:abc123..."
    },
    {
    "platform": {"architecture": "arm64", "os": "linux"},
    "digest": "sha256:def456..."
    }
    ]
    }
  5. Pousser vers le registry

    Fenêtre de terminal
    podman manifest push myapp:1.0 registry.example.com/myapp:1.0

Après le push, vérifiez que le manifest list est bien multi-arch :

Fenêtre de terminal
# Inspecter depuis le registry
podman manifest inspect registry.example.com/myapp:1.0
# Ou avec skopeo
skopeo inspect --raw docker://registry.example.com/myapp:1.0 | jq '.manifests[].platform'
Résultat
{"architecture": "amd64", "os": "linux"}
{"architecture": "arm64", "os": "linux"}
Fenêtre de terminal
# Sur machine amd64
podman pull registry.example.com/myapp:1.0
podman inspect myapp:1.0 --format '{{.Architecture}}'
# amd64
# Sur machine arm64
podman pull registry.example.com/myapp:1.0
podman inspect myapp:1.0 --format '{{.Architecture}}'
# arm64

Objectif : construire une API Go pour amd64 et arm64.

main.go
package main
import (
"fmt"
"net/http"
"runtime"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from %s/%s\n", runtime.GOOS, runtime.GOARCH)
})
http.ListenAndServe(":8080", nil)
}
Containerfile
# Build stage
FROM docker.io/library/golang:1.22-alpine AS builder
WORKDIR /app
COPY main.go .
# Build statique pour l'architecture cible
RUN CGO_ENABLED=0 go build -o /app/server main.go
# Runtime stage
FROM scratch
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
  1. Construire les deux architectures

    Fenêtre de terminal
    podman build --platform linux/amd64 -t myapi:amd64 .
    podman build --platform linux/arm64 -t myapi:arm64 .
  2. Créer le manifest

    Fenêtre de terminal
    podman manifest create myapi:1.0
    podman manifest add myapi:1.0 containers-storage:localhost/myapi:amd64
    podman manifest add myapi:1.0 containers-storage:localhost/myapi:arm64
  3. Vérifier

    Fenêtre de terminal
    podman manifest inspect myapi:1.0
  4. Pousser

    Fenêtre de terminal
    podman manifest push myapi:1.0 registry.example.com/myapi:1.0
Fenêtre de terminal
# Prérequis : farm configurée avec connexions amd64 et arm64
podman farm build \
--farm my-farm \
--platforms linux/amd64,linux/arm64 \
-t registry.example.com/myapi:1.0 \
.
Fenêtre de terminal
# Inspecter le manifest list
podman manifest inspect registry.example.com/myapi:1.0
# Tester sur votre machine
podman run --rm registry.example.com/myapi:1.0
# Hello from linux/<votre-arch>
# Forcer une architecture différente (émulé)
podman run --rm --platform linux/arm64 registry.example.com/myapi:1.0
# Hello from linux/arm64

La plupart des erreurs viennent de trois sources : l’absence de QEMU pour l’émulation, une image manquante dans le manifest, ou un registry qui ne supporte pas les manifest lists OCI.

ErreurCauseSolution
exec format errorArchitecture incompatibleVérifier --platform ou installer qemu
no suitable imageManifest sans cette archAjouter l’image pour cette arch
failed to push manifestRegistry ne supporte pas OCI indexUtiliser Docker Hub, GitHub, etc.
Farm build timeoutMachine distante inaccessibleVérifier SSH et podman socket
Fenêtre de terminal
# Architecture de la machine locale
podman info --format '{{.Host.Arch}}'
# Vérifier l'émulation disponible
ls /proc/sys/fs/binfmt_misc/
# Voir les architectures d'un manifest
podman manifest inspect myapp:1.0 | jq '.manifests[].platform'
# Vérifier les connexions de la farm
podman system connection list
podman farm list
Fenêtre de terminal
# Vérifier que qemu-user-static est installé
ls /usr/bin/qemu-*-static
# Réenregistrer les handlers (Linux)
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# Test rapide
podman run --rm --platform linux/arm64 alpine:3.21 uname -m
# aarch64

Le choix dépend de votre contexte : l’émulation est gratuite mais lente, farm est rapide mais nécessite des machines de chaque architecture. Pour la plupart des projets open source, l’émulation suffit.

SituationRecommandation
Dev local, tests rapidesÉmulation
CI/CD rapideFarm
Projet open sourceÉmulation (pas de machines dédiées)
Production à grande échelleFarm
  1. Containerfile compatible : éviter les dépendances architecture-spécifiques

    # ✅ Bon : dépendances universelles
    FROM alpine:3.21
    RUN apk add --no-cache ca-certificates
    # ❌ Mauvais : binaire compilé pour une seule arch
    COPY mybinary /app/mybinary
  2. Multi-stage avec cross-compilation (Go, Rust) :

    FROM golang:1.22 AS builder
    ARG TARGETARCH # Variable automatique : arm64 ou amd64
    RUN GOARCH=${TARGETARCH} go build -o /app
  3. Tester sur toutes les architectures avant de pousser

Fenêtre de terminal
# Manifest list avec tag sémantique
podman manifest push myapi:1.0 registry/myapi:1.0.0
podman manifest push myapi:1.0 registry/myapi:1.0
podman manifest push myapi:1.0 registry/myapi:1
podman manifest push myapi:1.0 registry/myapi:latest
  1. Manifest list = index pointant vers plusieurs images (une par arch)
  2. Émulation (QEMU) : simple, lente, pas de machine distante
  3. Farm : builds natifs distribués, rapide, nécessite des machines
  4. Workflow : manifest createaddinspectpush
  5. Vérification : manifest inspect pour voir les architectures
  6. Test : --platform pour forcer une architecture (émulée)

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.