Aller au contenu
high

CI/CD d'images : tags, digests, cache, multi-arch et promotion

15 min de lecture

Votre pipeline CI/CD build des images, mais vous ne savez jamais exactement quelle version tourne en prod ? Ce guide vous donne les fondamentaux pour industrialiser vos builds d’images : stratégie de tags, digests immuables, cache efficace, multi-architecture et promotion entre environnements. Les bases pour une CI/CD d’images fiable et traçable.

Pipeline CI/CD d'images conteneurs

  • Promotion sans rebuild : garantir que prod = ce qui a été testé
  • Stratégie de tags : latest, semver, git SHA — quand utiliser quoi
  • Tag vs Digest : pourquoi le tag peut changer et comment pinner
  • Cache en CI : accélérer les builds avec BuildKit
  • Multi-arch : produire des images AMD64 et ARM64 efficacement

Un tag est un alias mutable qui pointe vers une image. Le même tag peut pointer vers des images différentes au fil du temps.

Un digest est un hash SHA256 immutable qui identifie une image de manière unique. Il ne change jamais.

Analogie : le tag est comme le nom d’un fichier (rapport.docx) — vous pouvez le modifier sans changer le nom. Le digest est comme une empreinte digitale — si le contenu change, l’empreinte change.

Fenêtre de terminal
# Tag : peut changer !
docker pull nginx:1.25
# Digest : toujours la même image
docker pull nginx@sha256:6926dd802f40e5e7257fded83e0d8030039642e4e10c4a98a6478e9c6fe06153
AspectTagDigest
Formatimage:tagimage@sha256:abc123...
Mutable✅ Oui❌ Non
Lisiblenginx:1.25❌ Hash de 64 caractères
Reproductible❌ Non garanti✅ Toujours identique
AuditDifficileFacile

Règle pratique :

  • Développement : tags semver (1.2.3) ou branch (main)
  • Production : digest ou tag semver strict
  • Audit/compliance : toujours le digest

Une bonne stratégie de tags permet de tracer quelle version du code produit quelle image.

TagQuandExempleUsage
Git SHAToujoursabc1234Traçabilité exacte
SemverRelease1.2.3Production
latestMerge mainlatestDev/test uniquement
PRPull requestspr-123Tests d’intégration
- name: Docker meta
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: ghcr.io/${{ github.repository }}
tags: |
# Sur push de tag v* → tag semver
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
# Sur push sur main → SHA court + latest
type=sha,prefix=
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
# Sur PR → pr-123
type=ref,event=pr

Résultat pour un push de tag v1.2.3 :

  • ghcr.io/mon-org/mon-app:1.2.3
  • ghcr.io/mon-org/mon-app:1.2
  • ghcr.io/mon-org/mon-app:abc1234

Sans cache, chaque build repart de zéro. Avec le cache BuildKit, seules les couches modifiées sont reconstruites.

Le cache est stocké dans l’image elle-même et pushé avec elle.

- name: Build and push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=registry,ref=ghcr.io/mon-org/mon-app:buildcache
cache-to: type=registry,ref=ghcr.io/mon-org/mon-app:buildcache,mode=max

Utilise le cache GitHub Actions, plus rapide car local au runner.

- name: Build and push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
Type de cacheAvantagesInconvénients
RegistryPartagé entre runners, persistantPlus lent (réseau)
GHARapide, local10 Go par défaut (extensible selon plan)
LocalLe plus rapideNon partagé entre runners

Les serveurs modernes utilisent différentes architectures : AMD64 (Intel/AMD) et ARM64 (Graviton AWS, Apple Silicon, Raspberry Pi). Une image multi-arch fonctionne sur les deux.

Fenêtre de terminal
# Créer un builder multi-plateforme
docker buildx create --name multiarch --use
# Build et push pour AMD64 et ARM64
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/mon-org/mon-app:1.0.0 \
--push \
.
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Build and push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}

Comment ça marche ?

Docker crée un manifest list (ou OCI image index) qui contient les références vers les images de chaque architecture. Quand vous faites docker pull, le client télécharge automatiquement la bonne architecture.

Fenêtre de terminal
# Voir les architectures disponibles
docker manifest inspect ghcr.io/mon-org/mon-app:1.0.0

La promotion consiste à faire passer une image déjà buildée d’un environnement à l’autre, sans la reconstruire.

Workflow de promotion d'images entre environnements

ApprocheRisque
Rebuild pour chaque envL’image prod peut différer de celle testée
Promouvoir la même image✅ Garantie que prod = ce qui a été testé

Option 1 : Re-tagger avec Skopeo (sans pull/push local)

Fenêtre de terminal
# Copier l'image de staging vers prod sans la télécharger
skopeo copy \
docker://ghcr.io/mon-org/mon-app:staging \
docker://ghcr.io/mon-org/mon-app:prod

Option 2 : Re-tagger avec Crane

Fenêtre de terminal
# Copier avec crane (Google)
crane copy ghcr.io/mon-org/mon-app:staging ghcr.io/mon-org/mon-app:prod

Option 3 : En CI avec docker/build-push-action (sans rebuild)

# Promotion manuelle via workflow_dispatch
on:
workflow_dispatch:
inputs:
source_tag:
description: 'Tag à promouvoir'
required: true
target_env:
description: 'Environnement cible'
required: true
type: choice
options: [staging, prod]
jobs:
promote:
runs-on: ubuntu-latest
steps:
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Promote image
run: |
skopeo copy \
docker://ghcr.io/${{ github.repository }}:${{ inputs.source_tag }} \
docker://ghcr.io/${{ github.repository }}:${{ inputs.target_env }}
1. Merge PR → build image:sha-abc1234, image:main
2. Tests auto → si OK, tag image:staging
3. Tests staging → si OK, promotion manuelle vers image:prod
4. Release → tag image:1.2.3 sur le digest de prod

Pour push des images, vous devez vous authentifier auprès du registry.

- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
ProblèmeCause probableSolution
Build lent malgré le cacheCache invalide (layers changés)Vérifier l’ordre des instructions Dockerfile — mettre les dépendances avant le code
cache-from ne fonctionne pasImage cache n’existe pas encorePremier build sans cache, puis les suivants l’utilisent
Multi-arch échoue sur ARMQEMU non configuréAjouter docker/setup-qemu-action avant buildx
denied: permission deniedToken sans packages:writeVérifier les permissions GitHub Actions
Digest différent après rebuildMétadonnées variables (date, run ID) ou dépendances non pinnéesFixer les LABEL, utiliser lockfiles (package-lock.json, requirements.txt)
Image corrompue après promotionRegistry intermédiaire incompatibleUtiliser skopeo copy --all pour copier toutes les architectures
  • Pinner les actions GitHub par SHA (pas par tag) : uses: docker/build-push-action@263435...
  • Utiliser GITHUB_TOKEN plutôt que des PAT pour GHCR
  • Scanner les images avec Trivy avant le push en prod
  • Attestations SLSA : générer la provenance pour prouver l’origine du build (voir guide supply chain)
  • Dockerfile : copier package.json / requirements.txt avant le code source
  • Multi-stage builds : séparer build et runtime pour réduire la taille
  • Cache registry : type=registry,ref=... pour partager entre runners (voir doc Docker cache)
  • Labels OCI : ajouter org.opencontainers.image.revision avec le SHA
  • Stocker le digest dans les metadata de release
  • SBOM : générer avec Syft et attacher à l’image

Voici un fil rouge bout-en-bout pour une application api-users :

Fenêtre de terminal
# Tags générés
ghcr.io/acme/api-users:abc1234 # Git SHA (traçabilité)
ghcr.io/acme/api-users:main # Branch
ghcr.io/acme/api-users:latest # Alias dev
# Digest obtenu
sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Fenêtre de terminal
# Après validation des tests staging
skopeo copy \
docker://ghcr.io/acme/api-users@sha256:e3b0c44... \
docker://ghcr.io/acme/api-users:prod
# Release tag
crane tag ghcr.io/acme/api-users@sha256:e3b0c44... 1.2.3
# deployment.yaml — le digest garantit l'immutabilité
spec:
containers:
- name: api-users
image: ghcr.io/acme/api-users@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Résultat : traçabilité complète du commit au pod en production.

Vérifions que vous maîtrisez les bases CI/CD d’images : tags, digests, cache et promotion.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80%

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  1. Promouvoir, ne pas rebuilder : l’image prod = celle testée (même digest)
  2. Le digest est le contrat CI→CD : stocker et propager le sha256:...
  3. Toujours tagger avec le Git SHA : traçabilité code ↔ image
  4. Cache BuildKit : cache-from / cache-to pour des builds 10× plus rapides
  5. Multi-arch : runners natifs + merge manifest (éviter l’émulation)
  6. Immutabilité : un tag peut être réécrit, pas un digest — vérifiez les policies registry
  7. Pinner les actions par SHA : sécurité supply chain

Pour comprendre pourquoi un tag peut pointer vers différentes images et comment fonctionne le multi-arch, plongez dans le standard OCI.