
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
Droit au but
Section intitulée « Droit au but »Quatre entrées pour aller directement au bon endroit : la théorie du
manifest list, les deux stratégies de construction, et le workflow des
commandes podman manifest.
Le besoin : une image pour plusieurs architectures
Section intitulée « Le besoin : une image pour plusieurs architectures »Une image de conteneur n'est pas portable d'une architecture processeur à l'autre : les binaires qu'elle embarque sont compilés pour un jeu d'instructions précis. Un même déploiement mélange pourtant souvent des serveurs amd64 et des machines arm64, et personne ne veut maintenir deux tags séparés que les équipes finiront par confondre.
Le problème
Section intitulée « Le problème »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 :
podman run myapp:1.0# exec format errorLa solution : manifest list
Section intitulée « La solution : manifest list »Un manifest list (ou "fat manifest") est un index qui pointe vers plusieurs images, une par architecture :
Quand vous exécutez podman pull myapp:1.0, Podman :
- Télécharge le manifest list
- Identifie l'architecture de votre machine
- Télécharge l'image correspondante
Résultat : le même tag fonctionne partout.
Qu'est-ce qu'un manifest list ?
Section intitulée « Qu'est-ce qu'un manifest list ? »Le manifest list ne contient aucune couche d'image : c'est uniquement une
table d'aiguillage qui associe une plateforme (architecture plus os) au
digest de l'image correspondante. Sa spécification est normalisée par
l'OCI sous le nom d'image index, ce qui garantit que Docker, containerd
et Podman l'interprètent de la même façon.
Structure
Section intitulée « Structure »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" } } ]}Architectures courantes
Section intitulée « Architectures courantes »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).
| Architecture | Machines | Exemples |
|---|---|---|
linux/amd64 | x86_64, Intel/AMD | Serveurs, cloud, laptops Linux/Windows |
linux/arm64 | ARM 64-bit | Raspberry Pi 4+, Mac M1/M2, AWS Graviton |
linux/arm/v7 | ARM 32-bit | Raspberry Pi 2/3 (mode 32-bit) |
linux/arm/v6 | ARM vieux | Raspberry Pi Zero |
linux/386 | x86 32-bit | Vieux serveurs |
linux/s390x | IBM Z | Mainframes |
linux/ppc64le | IBM Power | Power servers |
Vérifier l'architecture d'une image
Section intitulée « Vérifier l'architecture d'une image »Avant de reconstruire quoi que ce soit, regardez si l'image de base que vous
utilisez est déjà multi-arch. Si la sortie ne contient qu'une seule entrée
platform, aucun manifest list n'existe et votre build échouera sur les
autres architectures.
# Voir les architectures disponiblespodman manifest inspect docker.io/library/nginx:alpine
# OU avec skopeo (plus lisible)skopeo inspect --raw docker://docker.io/library/nginx:alpine | jqStratégie 1 : émulation (QEMU)
Section intitulée « Stratégie 1 : émulation (QEMU) »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.
Comment ça fonctionne
Section intitulée « Comment ça fonctionne »Le noyau Linux sait rediriger l'exécution d'un binaire étranger vers un
interpréteur, grâce au mécanisme binfmt_misc. Une fois qemu-user-static
enregistré, lancer un binaire arm64 sur une machine amd64 déclenche
automatiquement la traduction des instructions par QEMU, sans que la
commande podman build ait à s'en préoccuper.
Configuration
Section intitulée « Configuration »L'installation se résume à déposer les binaires QEMU et à enregistrer les handlers auprès du noyau. Sur macOS, la machine virtuelle de Podman embarque déjà tout le nécessaire, il n'y a rien à installer.
# Installer qemu-user-staticsudo dnf install qemu-user-static
# Vérifierls /usr/bin/qemu-*-static# Installer qemu-user-static et binfmt-supportsudo apt install qemu-user-static binfmt-support
# Enregistrer les handlersdocker run --rm --privileged multiarch/qemu-user-static --reset -p yes# L'émulation est déjà incluse dans Podman Machinepodman machine initpodman machine start
# Vérifierpodman info | grep -A 5 hostConstruire avec --platform
Section intitulée « Construire avec --platform »L'option --platform fixe l'architecture cible du build, indépendamment de
celle de votre machine. Chaque build produit une image distincte qu'il faudra
ensuite rassembler dans un manifest list.
# 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 .Performances
Section intitulée « Performances »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 cible | Sur machine amd64 | Temps relatif |
|---|---|---|
| linux/amd64 | Natif | 1× |
| linux/arm64 | Émulé | 5-10× |
| linux/arm/v7 | Émulé | 5-10× |
Stratégie 2 : podman farm (builds natifs)
Section intitulée « Stratégie 2 : podman farm (builds natifs) »podman farm distribue les builds sur des machines distantes de chaque architecture. Les builds sont natifs, donc rapides.
Architecture
Section intitulée « Architecture »Votre poste ne compile rien lui-même : il envoie le contexte de build à chaque machine de la farm, puis rassemble les résultats. Le travail lourd de compilation se fait sur une machine dont le processeur correspond réellement à l'architecture visée, donc sans traduction d'instructions.
Prérequis
Section intitulée « Prérequis »Pour chaque machine distante :
- Podman installé
- SSH accessible depuis la machine locale
- Podman socket activé :
# Sur chaque machine distantesystemctl --user enable --now podman.socketConfigurer la farm
Section intitulée « Configurer la farm »Une farm n'est rien d'autre qu'un regroupement nommé de connexions distantes déjà déclarées dans Podman. Il faut donc d'abord enregistrer chaque machine, puis les rassembler sous un nom commun.
-
Ajouter les connexions Podman
Fenêtre de terminal # Machine amd64podman system connection add amd64 ssh://user@amd64-server.local/run/user/1000/podman/podman.sock# Machine arm64podman system connection add arm64 ssh://user@arm64-server.local/run/user/1000/podman/podman.sock# Vérifierpodman system connection list -
Créer la farm
Fenêtre de terminal podman farm create my-farm amd64 arm64# Vérifierpodman farm list -
Tester
Fenêtre de terminal podman farm listRésultat Name Connectionsmy-farm amd64, arm64
Construire avec la farm
Section intitulée « Construire avec la farm »Le tag doit désigner un registry accessible depuis toutes les machines de la farm : chaque nœud pousse son image, et le manifest list est assemblé ensuite à partir de ces images distantes.
podman farm build \ --farm my-farm \ --platforms linux/amd64,linux/arm64 \ -t registry.example.com/myapp:1.0 \ .Ce qui se passe :
- Le Containerfile est envoyé aux machines distantes
- Chaque machine build pour son architecture (natif)
- Les images sont poussées vers le registry
- Un manifest list est créé avec les deux images
Options de podman farm build
Section intitulée « Options de podman farm build »Les options principales contrôlent la farm utilisée, les architectures cibles, et si le build doit aussi s'exécuter localement.
| Option | Description | Exemple |
|---|---|---|
--farm | Nom de la farm | --farm my-farm |
--platforms | Architectures cibles | linux/amd64,linux/arm64 |
-t, --tag | Tag de l'image | -t registry/app:1.0 |
--local | Build aussi sur la machine locale | --local |
Workflow complet : manifest list
Section intitulée « Workflow complet : manifest list »Si vous avez déjà des images construites (par émulation ou farm), vous pouvez créer manuellement un manifest list.
Commande manifest
Section intitulée « Commande manifest »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-commande | Action |
|---|---|
manifest create | Créer un nouveau manifest list |
manifest add | Ajouter une image au manifest |
manifest inspect | Voir le contenu du manifest |
manifest push | Pousser le manifest vers un registry |
manifest rm | Supprimer un manifest local |
Workflow manuel
Section intitulée « Workflow manuel »Les cinq étapes suivantes reproduisent à la main ce que podman farm build
automatise. Notez le préfixe containers-storage: à l'étape d'ajout : sans
lui, Podman cherche l'image dans un registry distant au lieu du stockage
local.
-
Construire les images pour chaque architecture
Fenêtre de terminal # Sur machine amd64podman build --platform linux/amd64 -t myapp:amd64 .# Sur machine arm64 (ou émulé)podman build --platform linux/arm64 -t myapp:arm64 . -
Créer le manifest list
Fenêtre de terminal podman manifest create myapp:1.0 -
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:amd64podman manifest add myapp:1.0 containers-storage:localhost/myapp:arm64 -
Vérifier
Fenêtre de terminal podman manifest inspect myapp:1.0Résultat (simplifié) {"manifests": [{"platform": {"architecture": "amd64", "os": "linux"},"digest": "sha256:abc123..."},{"platform": {"architecture": "arm64", "os": "linux"},"digest": "sha256:def456..."}]} -
Pousser vers le registry
Fenêtre de terminal podman manifest push myapp:1.0 registry.example.com/myapp:1.0
Vérifier le résultat
Section intitulée « Vérifier le résultat »Après le push, vérifiez que le manifest list est bien multi-arch :
# Inspecter depuis le registrypodman manifest inspect registry.example.com/myapp:1.0
# Ou avec skopeoskopeo inspect --raw docker://registry.example.com/myapp:1.0 | jq '.manifests[].platform'{"architecture": "amd64", "os": "linux"}{"architecture": "arm64", "os": "linux"}Tester sur différentes architectures
Section intitulée « Tester sur différentes architectures »Le test décisif consiste à tirer le même tag depuis deux machines différentes et à comparer l'architecture réellement obtenue. Si les deux renvoient la même valeur, le manifest list n'a pas été poussé correctement.
# Sur machine amd64podman pull registry.example.com/myapp:1.0podman inspect myapp:1.0 --format '{{.Architecture}}'# amd64
# Sur machine arm64podman pull registry.example.com/myapp:1.0podman inspect myapp:1.0 --format '{{.Architecture}}'# arm64Lab : construire une image multi-arch
Section intitulée « Lab : construire une image multi-arch »Objectif : construire une API Go pour amd64 et arm64.
Code de l'application
Section intitulée « Code de l'application »L'application affiche l'architecture sur laquelle elle tourne, ce qui rend le
résultat vérifiable d'un coup d'œil. Le Containerfile compile en
statique (CGO_ENABLED=0) parce que l'image finale part de scratch,
qui ne contient aucune bibliothèque système.
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)}# Build stageFROM docker.io/library/golang:1.22-alpine AS builder
WORKDIR /appCOPY main.go .
# Build statique pour l'architecture cibleRUN CGO_ENABLED=0 go build -o /app/server main.go
# Runtime stageFROM scratchCOPY --from=builder /app/server /serverEXPOSE 8080ENTRYPOINT ["/server"]Option A : avec émulation
Section intitulée « Option A : avec émulation »Cette voie ne demande qu'une seule machine. Comptez plusieurs minutes sur le build arm64, la compilation Go étant intégralement traduite par QEMU.
-
Construire les deux architectures
Fenêtre de terminal podman build --platform linux/amd64 -t myapi:amd64 .podman build --platform linux/arm64 -t myapi:arm64 . -
Créer le manifest
Fenêtre de terminal podman manifest create myapi:1.0podman manifest add myapi:1.0 containers-storage:localhost/myapi:amd64podman manifest add myapi:1.0 containers-storage:localhost/myapi:arm64 -
Vérifier
Fenêtre de terminal podman manifest inspect myapi:1.0 -
Pousser
Fenêtre de terminal podman manifest push myapi:1.0 registry.example.com/myapi:1.0
Option B : avec farm
Section intitulée « Option B : avec farm »Une seule commande remplace les quatre étapes précédentes : la construction, la publication des images et l'assemblage du manifest list sont enchaînés automatiquement.
# 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 \ .Vérification finale
Section intitulée « Vérification finale »Trois contrôles suffisent : le manifest liste bien deux plateformes, le
conteneur démarre sur votre architecture, et l'option --platform permet de
forcer l'autre variante pour la tester sans changer de machine.
# Inspecter le manifest listpodman manifest inspect registry.example.com/myapi:1.0
# Tester sur votre machinepodman 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/arm64Dépannage
Section intitulée « Dépannage »Les incidents multi-arch se manifestent rarement au moment du build : ils
apparaissent au démarrage du conteneur, sur la machine cible, avec un message
laconique. Les sections suivantes vont du symptôme le plus fréquent
(exec format error) aux commandes qui permettent de confirmer l'état réel
de l'émulation et de la farm.
Erreurs courantes
Section intitulée « Erreurs courantes »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.
| Erreur | Cause | Solution |
|---|---|---|
exec format error | Architecture incompatible | Vérifier --platform ou installer qemu |
no suitable image | Manifest sans cette arch | Ajouter l'image pour cette arch |
failed to push manifest | Registry ne supporte pas OCI index | Utiliser Docker Hub, GitHub, etc. |
| Farm build timeout | Machine distante inaccessible | Vérifier SSH et podman socket |
Commandes de diagnostic
Section intitulée « Commandes de diagnostic »Ces quatre commandes répondent aux quatre questions utiles : sur quelle architecture suis-je, l'émulation est-elle enregistrée dans le noyau, que contient réellement mon manifest, et les machines de la farm répondent-elles.
# Architecture de la machine localepodman info --format '{{.Host.Arch}}'
# Vérifier l'émulation disponiblels /proc/sys/fs/binfmt_misc/
# Voir les architectures d'un manifestpodman manifest inspect myapp:1.0 | jq '.manifests[].platform'
# Vérifier les connexions de la farmpodman system connection listpodman farm listL'émulation ne fonctionne pas
Section intitulée « L'émulation ne fonctionne pas »Les enregistrements binfmt_misc ne survivent pas toujours à un
redémarrage selon la distribution : c'est la cause la plus fréquente d'une
émulation qui marchait hier et plus aujourd'hui. Le test final doit renvoyer
aarch64 et non l'architecture de votre machine.
# 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 rapidepodman run --rm --platform linux/arm64 alpine:3.21 uname -m# aarch64Bonnes pratiques
Section intitulée « Bonnes pratiques »Le multi-arch coûte du temps de build et de la complexité d'outillage : la
première décision est donc de choisir la stratégie adaptée à votre volume,
puis d'écrire un Containerfile qui ne présuppose aucune architecture.
Les recommandations ci-dessous couvrent ces deux points, plus la gestion des
tags une fois le manifest publié.
Quand utiliser émulation vs farm
Section intitulée « Quand utiliser émulation vs farm »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.
| Situation | Recommandation |
|---|---|
| Dev local, tests rapides | Émulation |
| CI/CD rapide | Farm |
| Projet open source | Émulation (pas de machines dédiées) |
| Production à grande échelle | Farm |
Optimisations pour le multi-arch
Section intitulée « Optimisations pour le multi-arch »Un Containerfile qui copie un binaire déjà compilé condamne l'image à une
seule architecture. Pour les langages compilés, la cross-compilation
évite l'émulation : Podman renseigne automatiquement l'argument
TARGETARCH avec la plateforme demandée, il suffit de le transmettre au
compilateur.
-
Containerfile compatible : éviter les dépendances architecture-spécifiques
# ✅ Bon : dépendances universellesFROM alpine:3.21RUN apk add --no-cache ca-certificates# ❌ Mauvais : binaire compilé pour une seule archCOPY mybinary /app/mybinary -
Multi-stage avec cross-compilation (Go, Rust) :
FROM golang:1.22 AS builderARG TARGETARCH # Variable automatique : arm64 ou amd64RUN GOARCH=${TARGETARCH} go build -o /app -
Tester sur toutes les architectures avant de pousser
Tags et versioning
Section intitulée « Tags et versioning »Un même manifest list peut être poussé sous plusieurs tags, du plus précis au
plus large. Les consommateurs choisissent alors leur niveau de stabilité :
1.0.0 ne bougera jamais, latest suit la dernière publication.
# Manifest list avec tag sémantiquepodman manifest push myapi:1.0 registry/myapi:1.0.0podman manifest push myapi:1.0 registry/myapi:1.0podman manifest push myapi:1.0 registry/myapi:1podman manifest push myapi:1.0 registry/myapi:latestÀ retenir
Section intitulée « À retenir »- Manifest list = index pointant vers plusieurs images (une par arch)
- Émulation (QEMU) : simple, lente, pas de machine distante
- Farm : builds natifs distribués, rapide, nécessite des machines
- Workflow :
manifest create→add→inspect→push - Vérification :
manifest inspectpour voir les architectures - Test :
--platformpour forcer une architecture (émulée)