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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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
Tag vs Digest : la différence cruciale
Section intitulée « Tag vs Digest : la différence cruciale »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.
# Tag : peut changer !docker pull nginx:1.25
# Digest : toujours la même imagedocker pull nginx@sha256:6926dd802f40e5e7257fded83e0d8030039642e4e10c4a98a6478e9c6fe06153| Aspect | Tag | Digest |
|---|---|---|
| Format | image:tag | image@sha256:abc123... |
| Mutable | ✅ Oui | ❌ Non |
| Lisible | ✅ nginx:1.25 | ❌ Hash de 64 caractères |
| Reproductible | ❌ Non garanti | ✅ Toujours identique |
| Audit | Difficile | Facile |
Règle pratique :
- Développement : tags semver (
1.2.3) ou branch (main) - Production : digest ou tag semver strict
- Audit/compliance : toujours le digest
Stratégie de tags recommandée
Section intitulée « Stratégie de tags recommandée »Une bonne stratégie de tags permet de tracer quelle version du code produit quelle image.
Les tags à générer en CI
Section intitulée « Les tags à générer en CI »| Tag | Quand | Exemple | Usage |
|---|---|---|---|
| Git SHA | Toujours | abc1234 | Traçabilité exacte |
| Semver | Release | 1.2.3 | Production |
| latest | Merge main | latest | Dev/test uniquement |
| PR | Pull requests | pr-123 | Tests d’intégration |
Exemple : metadata-action (GitHub Actions)
Section intitulée « Exemple : metadata-action (GitHub Actions) »- 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=prRésultat pour un push de tag v1.2.3 :
ghcr.io/mon-org/mon-app:1.2.3ghcr.io/mon-org/mon-app:1.2ghcr.io/mon-org/mon-app:abc1234
Cache en CI : accélérer les builds
Section intitulée « Cache en CI : accélérer les builds »Sans cache, chaque build repart de zéro. Avec le cache BuildKit, seules les couches modifiées sont reconstruites.
Option 1 : Cache inline (simple)
Section intitulée « Option 1 : Cache inline (simple) »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=maxOption 2 : Cache GitHub Actions (rapide)
Section intitulée « Option 2 : Cache GitHub Actions (rapide) »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 cache | Avantages | Inconvénients |
|---|---|---|
| Registry | Partagé entre runners, persistant | Plus lent (réseau) |
| GHA | Rapide, local | 10 Go par défaut (extensible selon plan) |
| Local | Le plus rapide | Non partagé entre runners |
Multi-arch : AMD64 et ARM64 en un seul build
Section intitulée « Multi-arch : AMD64 et ARM64 en un seul build »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.
Avec docker buildx (local)
Section intitulée « Avec docker buildx (local) »# Créer un builder multi-plateformedocker buildx create --name multiarch --use
# Build et push pour AMD64 et ARM64docker buildx build \ --platform linux/amd64,linux/arm64 \ -t ghcr.io/mon-org/mon-app:1.0.0 \ --push \ .En CI (GitHub Actions)
Section intitulée « En CI (GitHub Actions) »- 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.
# Voir les architectures disponiblesdocker manifest inspect ghcr.io/mon-org/mon-app:1.0.0Promotion : dev → staging → prod
Section intitulée « Promotion : dev → staging → prod »La promotion consiste à faire passer une image déjà buildée d’un environnement à l’autre, sans la reconstruire.
Pourquoi promouvoir plutôt que rebuilder ?
Section intitulée « Pourquoi promouvoir plutôt que rebuilder ? »| Approche | Risque |
|---|---|
| Rebuild pour chaque env | L’image prod peut différer de celle testée |
| Promouvoir la même image | ✅ Garantie que prod = ce qui a été testé |
Comment promouvoir
Section intitulée « Comment promouvoir »Option 1 : Re-tagger avec Skopeo (sans pull/push local)
# Copier l'image de staging vers prod sans la téléchargerskopeo copy \ docker://ghcr.io/mon-org/mon-app:staging \ docker://ghcr.io/mon-org/mon-app:prodOption 2 : Re-tagger avec Crane
# Copier avec crane (Google)crane copy ghcr.io/mon-org/mon-app:staging ghcr.io/mon-org/mon-app:prodOption 3 : En CI avec docker/build-push-action (sans rebuild)
# Promotion manuelle via workflow_dispatchon: 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 }}Workflow de promotion typique
Section intitulée « Workflow de promotion typique »1. Merge PR → build image:sha-abc1234, image:main2. Tests auto → si OK, tag image:staging3. Tests staging → si OK, promotion manuelle vers image:prod4. Release → tag image:1.2.3 sur le digest de prodAuthentification registry
Section intitulée « Authentification registry »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 }}- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }}- uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 with: registry-type: privateDépannage
Section intitulée « Dépannage »| Problème | Cause probable | Solution |
|---|---|---|
| Build lent malgré le cache | Cache invalide (layers changés) | Vérifier l’ordre des instructions Dockerfile — mettre les dépendances avant le code |
cache-from ne fonctionne pas | Image cache n’existe pas encore | Premier build sans cache, puis les suivants l’utilisent |
| Multi-arch échoue sur ARM | QEMU non configuré | Ajouter docker/setup-qemu-action avant buildx |
denied: permission denied | Token sans packages:write | Vérifier les permissions GitHub Actions |
| Digest différent après rebuild | Métadonnées variables (date, run ID) ou dépendances non pinnées | Fixer les LABEL, utiliser lockfiles (package-lock.json, requirements.txt) |
| Image corrompue après promotion | Registry intermédiaire incompatible | Utiliser skopeo copy --all pour copier toutes les architectures |
Bonnes pratiques
Section intitulée « Bonnes pratiques »Sécurité
Section intitulée « Sécurité »- 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)
Performance
Section intitulée « Performance »- Dockerfile : copier
package.json/requirements.txtavant 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)
Traçabilité
Section intitulée « Traçabilité »- Labels OCI : ajouter
org.opencontainers.image.revisionavec le SHA - Stocker le digest dans les metadata de release
- SBOM : générer avec Syft et attacher à l’image
Exemple complet : du build au déploiement
Section intitulée « Exemple complet : du build au déploiement »Voici un fil rouge bout-en-bout pour une application api-users :
1. Build en CI (merge sur main)
Section intitulée « 1. Build en CI (merge sur main) »# Tags générésghcr.io/acme/api-users:abc1234 # Git SHA (traçabilité)ghcr.io/acme/api-users:main # Branchghcr.io/acme/api-users:latest # Alias dev
# Digest obtenusha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8552. Promotion staging → prod
Section intitulée « 2. Promotion staging → prod »# Après validation des tests stagingskopeo copy \ docker://ghcr.io/acme/api-users@sha256:e3b0c44... \ docker://ghcr.io/acme/api-users:prod
# Release tagcrane tag ghcr.io/acme/api-users@sha256:e3b0c44... 1.2.33. Pin du digest en Kubernetes
Section intitulée « 3. Pin du digest en Kubernetes »# deployment.yaml — le digest garantit l'immutabilitéspec: containers: - name: api-users image: ghcr.io/acme/api-users@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Résultat : traçabilité complète du commit au pod en production.
Testez vos connaissances
Section intitulée « Testez vos connaissances »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
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
📋 Récapitulatif de vos réponses
Vérifiez vos réponses avant de soumettre. Cliquez sur une question pour la modifier.
Détail des réponses
À retenir
Section intitulée « À retenir »- Promouvoir, ne pas rebuilder : l’image prod = celle testée (même digest)
- Le digest est le contrat CI→CD : stocker et propager le
sha256:... - Toujours tagger avec le Git SHA : traçabilité code ↔ image
- Cache BuildKit :
cache-from/cache-topour des builds 10× plus rapides - Multi-arch : runners natifs + merge manifest (éviter l’émulation)
- Immutabilité : un tag peut être réécrit, pas un digest — vérifiez les policies registry
- Pinner les actions par SHA : sécurité supply chain
Pour aller plus loin
Section intitulée « Pour aller plus loin »Prochaine étape
Section intitulée « Prochaine étape »Pour comprendre pourquoi un tag peut pointer vers différentes images et comment fonctionne le multi-arch, plongez dans le standard OCI.