Le pattern golden image n'est pas mort, il s'est juste déplacé. Le serveur installé en cinq heures depuis un ISO Debian, configuré à la main puis "documenté dans un wiki", a été remplacé par un pipeline qui produit chaque semaine une image versionnée, signée, scannée, et déployée par dizaines.
Packer (HashiCorp) reste l'outil de référence pour ce job. Il orchestre la construction d'une image, peu importe le format de sortie : AMI AWS, image GCP, image Azure, OVA VMware, qcow2 KVM, ISO custom, image Docker, image Vagrant. Sa logique : démarrer un builder, exécuter des provisioners, capturer le résultat, signer, publier.
Ansible reste le provisioner par défaut côté ops. Idempotent, lisible, indépendant du système cible. Le couple Packer + Ansible s'est imposé comme le standard du DevOps infra moderne.
Plan de l'article
- Le problème : drift, snowflakes, et reproductibilité
- Architecture Packer : builders, provisioners, post-processors
- Pourquoi Ansible plutôt qu'un script bash
- Pipeline type : Packer + Ansible + CI/CD
- Multi-format et multi-cloud
- Tests d'images : Goss, Inspec, Testinfra
- Versioning, signature, distribution
- Pièges et signaux à surveiller
Le problème : drift, snowflakes, et reproductibilité
Trois pathologies récurrentes en infra qu'une stratégie golden image résout.
Drift de configuration. Sur un parc de 50 VMs configurées manuellement ou par scripts pas-à-pas, deux serveurs censés être identiques divergent en six mois. Patches manqués, hotfix en local, paquets oubliés. Le jour d'un incident, on découvre que l'app marche sur 47 et fail sur 3.
Snowflakes. Une VM "qui marche pas comme les autres" et que personne n'ose toucher. La doc est obsolète, le sysadmin qui l'a installée est parti. Le risque accumulé devient un point d'échec.
Reproductibilité. Pour un audit de sécurité, pour un PRA testé, pour un déploiement neuf, on doit pouvoir reproduire à l'identique. Sans pipeline d'image, c'est impossible à 100%.
La parade : construire des images immuables, versionnées, déployées en lot. Quand on doit patcher, on rebuild une image, on la teste, on la déploie. On ne touche pas la VM en place, on la remplace.
Architecture Packer : builders, provisioners, post-processors
Packer fonctionne en trois étapes :
Builders : créent l'environnement temporaire dans lequel l'image va être construite. AMI builder lance une EC2, Azure builder lance une VM Azure, QEMU builder lance une VM locale, Docker builder lance un conteneur. Chaque builder produit l'image native du provider.
Provisioners : exécutent la configuration sur l'environnement temporaire. Ansible, Shell, PowerShell, Chef, Puppet, file uploads. C'est ici qu'on installe les paquets, configure les services, applique les hardening.
Post-processors : transforment et publient l'artefact final. Compress, manifest JSON, push vers un bucket, push vers Vagrant Cloud, etc.
Une config Packer minimale en HCL (le format moderne, depuis 1.7) :
source "amazon-ebs" "ubuntu" {
region = "eu-west-3"
ami_name = "golden-ubuntu-{{timestamp}}"
instance_type = "t3.micro"
source_ami_filter {
filters = {
virtualization-type = "hvm"
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
}
build {
sources = ["source.amazon-ebs.ubuntu"]
provisioner "ansible" {
playbook_file = "./playbooks/baseline.yml"
}
provisioner "shell" {
inline = ["sudo apt-get clean", "sudo rm -rf /var/lib/apt/lists/*"]
}
post-processor "manifest" {
output = "manifest.json"
}
}
packer init, packer validate, packer build orchestrent le tout.
Pourquoi Ansible plutôt qu'un script bash
Le débat est classique. La réponse est triple.
Idempotence. Un playbook Ansible peut être réexécuté 100 fois, le résultat est le même. Un script bash mal écrit casse au deuxième passage.
Lisibilité. apt: name=nginx state=present est compréhensible par tout le monde. Un apt-get install -y nginx || true parsé dans 200 lignes de bash, moins.
Réutilisation. Les rôles Ansible (cf. ansible-roles-galaxy) permettent de mutualiser la baseline entre l'image golden et la configuration runtime. Le même rôle users qui crée les comptes et déploie les clés SSH tourne en build et en run.
L'argument courant contre Ansible : la latence. Un playbook prend plus de temps qu'un script. Pour un build d'image qui s'exécute quelques fois par semaine, la différence est négligeable. Pour Molecule, qui teste les rôles Ansible, c'est même le bon endroit pour l'intégration.
Pipeline type : Packer + Ansible + CI/CD
Le pipeline qu'on déploie chez nos clients ressemble à ça.
- Source de vérité : repo Git avec
packer/,ansible/,tests/. - Pull request : revue de la modif. CI lance
packer validate,ansible-lint,yamllint. - Build sur main : runner CI lance
packer build. L'image produite est taggée avec le SHA du commit + date. - Tests automatisés : Goss ou Testinfra valident que l'image contient bien ce qu'elle doit.
- Scan vulnérabilités : Trivy scanne l'image. Échec si CVE critique non patchée.
- Signature : Cosign signe l'image (cf. sigstore-cosign-images pour le détail).
- Publication : push vers la cible (AMI partagée multi-comptes, registry Docker, repo qcow2 sur S3, etc.).
- Promotion : workflow manuel ou automatique pour passer une image en "stable" / "current".
GitHub Actions ou GitLab CI font le job sans gymnastique particulière.
# .gitlab-ci.yml
stages: [validate, build, test, scan, publish]
packer-validate:
stage: validate
script: packer validate ./packer/
packer-build:
stage: build
script: packer build ./packer/
artifacts:
paths: [manifest.json]
goss-tests:
stage: test
needs: [packer-build]
script: ./scripts/run-goss.sh $(jq -r .builds[0].artifact_id manifest.json)
trivy-scan:
stage: scan
needs: [packer-build]
script: trivy image --exit-code 1 --severity CRITICAL $(jq -r .builds[0].artifact_id manifest.json)
À aligner avec GitLab CI on K8s ou un Jenkins pipeline selon la stack en place.
Multi-format et multi-cloud
L'avantage Packer : un même code source produit plusieurs formats simultanément.
build {
sources = [
"source.amazon-ebs.ubuntu",
"source.googlecompute.ubuntu",
"source.azure-arm.ubuntu",
"source.qemu.ubuntu",
"source.proxmox-iso.ubuntu",
]
provisioner "ansible" {
playbook_file = "./playbooks/baseline.yml"
}
}
Cinq builds en parallèle, même provisioner Ansible. L'AMI AWS, l'image GCE, l'image Azure, la qcow2 KVM, et l'image Proxmox sortent toutes du même run, configurées identiquement.
Pour des organisations qui opèrent du multi-cloud (cf. cloud-hybride-multi-cloud-2026), c'est l'outil qui résout la question "comment garder les images alignées entre nos environnements".
Tests d'images : Goss, Inspec, Testinfra
Tester une image avant de la promouvoir n'est pas optionnel. Trois outils dominent.
Goss / dgoss. Leger, YAML, idéal pour les tests d'image. Vérifie packages, services, ports, fichiers, processus, users. Run en quelques secondes.
package:
nginx:
installed: true
service:
nginx:
enabled: true
running: true
port:
tcp:80:
listening: true
file:
/etc/nginx/nginx.conf:
exists: true
owner: root
Inspec (Chef Inspec). Plus riche, ruby DSL, conformité CIS et autres benchmarks intégrés. À considérer si on a déjà du Chef ou des audits CIS comme ceux de notre article cis-benchmark-audit.
Testinfra. Pytest pour serveurs. Plus naturel pour les équipes Python.
Le test n'est pas un nice-to-have. Une image qui passe packer build mais qui a un service mort au démarrage cause un outage en prod le mardi suivant.
Versioning, signature, distribution
Versioning : SemVer (major.minor.patch) ou date-based (YYYY.MM.DD-shortsha). Les deux marchent. Le critère : pouvoir identifier l'image qui tourne en prod et la rebuilder à l'identique.
Signature : Cosign même pour les images VM (sig dans un manifest associé). Pour les AMIs, signature via SHA256 + manifest publié dans un repo signé. La traçabilité est non négociable pour les audits de sécurité.
Distribution : trois patterns courants.
- AMI partagée multi-comptes AWS : commande
aws ec2 modify-image-attribute --launch-permission. - Registry Docker / OCI : push vers Harbor, GHCR, ECR. Pour les Vagrant boxes, push vers Vagrant Cloud ou bucket S3.
- Repo de qcow2 : bucket S3 ou MinIO, indexé par un manifest JSON consommé par les déploiements Terraform / Ansible.
Pour la sauvegarde des images stables longue durée, s3 lifecycle archivage couvre la rétention.
Pièges et signaux à surveiller
Packer cleanup raté. Une AMI privée orpheline coûte des dollars chaque mois. Configurer des tags et un script de cleanup périodique. Idem pour les images Azure / GCP.
Cache Ansible mismatch. Des rôles Ansible qui dépendent de variables host_vars ne fonctionnent pas en build Packer. Toujours utiliser des group_vars / playbook vars, pas des host_vars.
Provisioning trop lourd. Un playbook qui prend 2h pour build une image bloque le pipeline. Optimiser : packages en bulk, parallélisation Ansible, repo apt local pour les déploiements multi-régions.
Tests Goss incomplets. Une image testée uniquement "le service tourne" ne valide pas que la conf est correcte. Tester le comportement réel : requête HTTP qui doit répondre 200, fichier de log qui doit contenir tel message, port firewall qui doit être bloqué.
Drift entre image et runtime. Une image golden qui pose la baseline, et un Ansible runtime qui modifie la conf, c'est compatible. Mais documenter la frontière. Sans ça, on rebuild l'image et on casse le runtime.
Mise à jour des images mères. Une image golden basée sur Ubuntu 22.04 doit être rebuildée régulièrement (mensuel a minima) pour intégrer les CVE upstream. Le pipeline doit déclencher automatiquement sur publication d'image base.
Sur les pipelines images qu'on opère pour nos clients, le ROI est mesurable en mois. Une équipe qui déploie 200 VMs identiques par trimestre avec un pipeline image économise des semaines de travail comparé à du provisioning ad hoc, et élimine les snowflakes. Si vous opérez encore des images custom artisanales et que vous voulez basculer sur un pipeline propre sans tout réécrire, on peut auditer votre stack et installer le pipeline en 2-3 semaines.
Sources
- Packer documentation officielle : référence builders, provisioners, post-processors.
- Ansible documentation officielle : playbooks, rôles, modules.
- Goss tests d'infrastructure : YAML pour tester images.
- Chef Inspec : framework conformité et tests.
- HashiCorp Packer GitHub : code et plugins.
- SLSA build levels : référentiel pour la maturité supply chain image.


