Hot Posts

6/recent/ticker-posts

Ma Publicité

Soutenez la Création

Aidez-moi à partager du contenu exclusif.

Soutenir

Recent Posts

Nouveau Drop

Boutique Officielle

Soutenez le blog monblog-sa-abasse et découvrez nos vêtements & accessoires exclusifs en édition limitée.

Découvrir la collection
Paiement Sécurisé
Livraison Monde

Infrastructure Immuable : Créer ses propres images de serveurs « Golden Images » avec Packer et Cloud-Init

Infrastructure Immuable : Créer ses propres images de serveurs « Golden Images » avec Packer et Cloud-Init

Une salle de serveurs élégante aux baies bleu-vert lumineuses, entourée de motifs hexagonaux abstraits et de nœuds numériques interconnectés symbolisant l’automatisation et la stabilité.

Pourquoi l’infrastructure immuable change vraiment la donne (et pourquoi « ça casse » souvent sans Golden Images)

Pendant longtemps, on a géré des serveurs comme on gère... des machines un peu vivantes. On se connecte, on patch, on installe un paquet, on modifie un fichier, on redémarre un service. Ça marche. Puis on recommence le mois suivant.

Sauf que ce modèle « mutable » a un défaut qui finit toujours par coûter cher : la dérive. La fameuse configuration drift. Deux serveurs censés être identiques ne le sont plus après quelques semaines. Un a un package en version N, l’autre en N-1. Un a un paramètre SSH changé « vite fait ». L’autre a un agent de monitoring qui a été réinstallé parce qu’il plantait. Et personne ne note tout, soyons honnêtes.

Résultat : des différences entre dev, staging et prod, des incidents du type « ça marchait hier », et des déploiements où on croise les doigts. Même quand on a de l’IaC autour, si la base des machines n’est pas cohérente, on traîne des surprises.

L’approche immuable vient casser cette habitude. L’idée est simple : on ne répare pas un serveur, on le remplace. On ne patch pas « à la main » un nœud, on rebuild une image, on redéploie. Donc le serveur devient jetable. Et surtout, reproductible.

C’est là que la notion de « Golden Image » entre en scène : une image de base préconfigurée, versionnée, traçable, et surtout reproductible. Une image qui sert de socle commun à toutes vos instances.

Dans cet article, on reste sur un périmètre clair :

  • construire ses propres images avec Packer,
  • et finaliser la personnalisation au boot avec cloud-init.

Promesse derrière tout ça : des déploiements plus rapides, auditables, et cohérents entre dev, staging et prod. Et moins de « mais pourtant sur mon serveur ça marche ».

Image d’or : ce qu’on met dedans (et ce qu’on évite)

Une Golden Image « saine », c’est un socle. Pas un serveur complet figé avec votre application, vos secrets, vos configs par environnement, et la moitié de /etc bricolée au fil du temps.

En gros, ce qui va bien dans l’image :

  • OS + mises à jour de base (et éventuellement un modèle de patching par rebuild)
  • durcissement minimal (SSH baseline, quelques sysctl, pare-feu de base si pertinent)
  • Dépendances communes : Paquets standards, outils d’investigation (curl, jq, net-tools ou équivalent), time sync
  • agents transverses : monitoring, logging, EDR, métriques, selon votre stack
  • Baseline users si vous en avez une (souvent un compte d’admin break-glass, mais attention aux politiques internes)
  • certificats racine, CA interne, configuration proxy si vous êtes en réseau contraint
  • paramètres système communs : timezone, NTP, locale, limites, configuration kernel basique

Et ce qu’on évite, vraiment :

  • secrets, clés privées, tokens cloud, kubeconfig, mots de passe
  • configuration applicative spécifique (URL d’API prod, credentials, endpoints internes)
  • tout ce qui varie par instance : hostname, IP, join à un cluster, config de rôle

La frontière pratique est simple :

  • image = socle commun, stable
  • cloud-init = personnalisation à l’instance

Cloud-init va très bien gérer : hostname, users et clés SSH, fichiers de config, montage de disques, bootstrap d’un service, join à un cluster, etc. Donc pas besoin de surcharger l’image.

Dernier point qui change la vie : le versioning.

  • un nommage clair (app-os-version-date)
  • un changelog, même minimal
  • des tags immuables (git_sha, build_date, version)
  • et idéalement un identifiant d’image que vous pouvez pinner côté Terraform

Les pièges classiques ?

  • image trop lourde, qui met 25 minutes à builder et à démarrer
  • image trop spécifique (une image par micro-service, puis vous perdez l’intérêt du socle)
  • image pas testée, et vous répliquez le bug à grande échelle au prochain scale-out

Packer + cloud-init : la combinaison la plus simple pour industrialiser

Packer, c’est votre usine à images. Déclaratif, reproductible, et surtout automatisable en CI. Il sait builder pour plein de cibles : AWS AMI, Azure Managed Image, GCP Image, OpenStack, et aussi des plateformes plus « VM » comme Proxmox ou VMware selon les plugins.

cloud-init, c’est le standard de provisioning au premier boot. Vous donnez un user-data (souvent du YAML « cloud-config »), et cloud-init applique des modules dans un ordre déterminé : users, fichiers, commandes, services, etc.

Pourquoi les deux ensemble ?

  • Packer « bake » le socle
  • cloud-init applique la variabilité, sans transformer vos serveurs en objets modifiés à la main

Un flux type ressemble à ça, et franchement c’est propre :

  1. commit dans Git
  2. pipeline CI
  3. build Packer
  4. tests (smoke + checks)
  5. publication de l’image avec tags
  6. déploiement d’instances (Terraform, par exemple)
  7. cloud-init configure au boot

On peut supporter plusieurs plateformes, mais sans s’éparpiller : le modèle mental reste identique.

Architecture de référence : pipeline « Golden Image » prêt pour la prod

Imaginez un schéma logique, simple :

  • un repo Git avec : packer + scripts + templates cloud-init
  • un runner CI qui exécute packer
  • un compte / projet cloud où les images sont publiées
  • une stratégie de tags + promotion

Les étapes que je vois le plus souvent en prod, dans cet ordre :

  1. packer fmt
  2. build : packer build avec variables
  3. test : boot d’une instance à partir de l’image, smoke tests
  4. publication : tags + manifest + métadonnées
  5. promotion : dev vers prod (tag immuable, ou copie contrôlée selon le cloud)

Gestion des artefacts : ne vous contentez pas de « ça a buildé ».

  • conservez l’ID de l’image
  • conservez le manifest Packer
  • gardez les logs de build
  • si possible, sortez une SBOM (conceptuellement, au moins préparez l’espace pour)

Rollback : en immuable, c’est presque le point le plus important. Vous ne hotfixez pas à la main en prod. Vous redéployez l’image N-1. C’est tout.

Outils autour : Terraform est un bon compagnon pour consommer l’ID d’image. Ansible peut être utilisé, mais plutôt au moment du build Packer (ou pour validation). Évitez de remettre une grosse couche de configuration mutable post-déploiement, sinon vous recréez la dérive.

Pré-requis et conventions (avant d’écrire une ligne de Packer)

Avant le code, prenez 30 minutes pour décider du cadre.

Choisir une base OS

Ubuntu, Debian, RHEL, Alma, Rocky… le critère n’est pas « ce que j’aime », c’est :

  • support éditeur et cycle de vie
  • compatibilité cloud-init et qualité des images de base
  • patching, sécurité, conformité interne
  • disponibilité des paquets dont vous avez besoin

Pour un premier pipeline, une Ubuntu LTS est souvent le chemin le plus simple, car cloud-init est bien intégré et l’écosystème est large. Mais adaptez à votre contexte.

Conventions de nommage et tags

Décidez un format, et tenez-vous-y. Exemple :

  • golden-ubuntu-24-04
  • 1.3.0
  • git_shabuild_dateosos_versionteamenvironment=dev

Le point clé : pouvoir dire exactement « quelle image tourne où ».

Réseau et dépôts

  • accès aux repos packages
  • proxy si nécessaire
  • miroirs internes si vous voulez du déterminisme et de la vitesse

Packer en HCL2

Utilisez HCL2. Lisible, variable-friendly, et c’est le standard actuel.

Structure de repo

Un exemple de structure qui évite le bazar :

repo/ packer/ ubuntu.pkr.hcl variables.pkr.hcl scripts/ base.sh hardening.sh cleanup.sh cloud-init/ base.yaml overlays/ dev.yaml prod.yaml tests/ smoke.sh docs/ CHANGELOG.md

Créer une Golden Image avec Packer (HCL) : exemple guidé de A à Z

On va montrer un exemple volontairement pragmatique : une image Ubuntu LTS sur AWS (AMI). Même si vous êtes sur Azure ou GCP, la logique reste la même. Vous remplacerez juste la source/builder.

Exemple Packer HCL (AWS AMI)

packer/ubuntu.pkr.hcl

hcl packer { required_version = ">= 1.10.0" required_plugins { amazon = { version = ">= 1.3.0" source = "github.com/hashicorp/amazon" } } }

variable "aws_region" { type = string default = "eu-west-3" }

variable "instance_type" { type = string default = "t3.micro" }

variable "ssh_username" { type = string default = "ubuntu" }

variable "ami_name_prefix" { type = string default = "golden-ubuntu-24-04" }

variable "version" { type = string default = "1.0.0" }

locals { build_date = formatdate("YYYYMMDD-hhmm", timestamp()) git_sha = coalesce(env("GIT_SHA"), "unknown") ami_name = "${var.ami_name_prefix}-${var.version}-${local.build_date}" }

source "amazon-ebs" "ubuntu" { region = var.aws_region instance_type = var.instance_type ssh_username = var.ssh_username

source_ami_filter { filters = { name = "ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*" virtualization-type = "hvm" root-device-type = "ebs" } owners = ["099720109477"] # Canonical most_recent = true }

ami_name = local.ami_name ami_description = "Golden Image Ubuntu 24.04, version ${var.version}"

tags = { Name = local.ami_name version = var.version git_sha = local.git_sha build_date = local.build_date os = "ubuntu" os_version = "24.04" } }

build { name = "golden-ubuntu" sources = ["source.amazon-ebs.ubuntu"]

provisioner "shell" { scripts = [ "${path.root}/../scripts/base.sh", "${path.root}/../scripts/hardening.sh", "${path.root}/../scripts/cleanup.sh" ] }

post-processor "manifest" { output = "manifest.json" strip_path = true } }

Ça vous donne :

  • une AMI nommée proprement
  • des tags utiles
  • manifest.json

Scripts de provisioning (exemples)

scripts/base.sh

bash #!/usr/bin/env bash set -euxo pipefail

export DEBIAN_FRONTEND=noninteractive

apt-get update apt-get -y upgrade

apt-get -y install

ca-certificates curl jq unzip

chrony

vim-tiny

systemctl enable chrony

scripts/hardening.sh

bash #!/usr/bin/env bash set -euxo pipefail

if [ -f /etc/ssh/sshd_config ]; then sed -i 's/^#?PermitRootLogin./PermitRootLogin no/' /etc/ssh/sshd_config || true sed -i 's/^#?PasswordAuthentication./PasswordAuthentication no/' /etc/ssh/sshd_config || true fi

cat >/etc/sysctl.d/99-golden.conf <<'EOF' net.ipv4.ip_forward=0 net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.all.send_redirects=0 EOF

sysctl --system || true

scripts/cleanup.sh

bash #!/usr/bin/env bash set -euxo pipefail

apt-get -y autoremove apt-get -y clean

rm -rf /var/lib/apt/lists/* rm -rf /tmp/* /var/tmp/*

rm -f /home/*/.bash_history || true rm -f /root/.bash_history || true

find /etc/ssh -name "ssh_host_key" -type f -maxdepth 1 -print || true

Note : selon votre cloud, vous ne devez pas supprimer certaines clés host SSH, ou au contraire les régénérer au premier boot. Décidez une règle et testez-la. Le « ça dépend » ici est réel.

Sortie : récupérer l’ID de l’image

Dans AWS, l’ID AMI est dans le manifest, dans les logs Packer, et peut aussi être exporté par votre CI. L’idée est de le passer ensuite à Terraform, et de le pinner. Pas de « latest » magique.

Provisioners : shell, Ansible, ou les deux ? (choisir sans se compliquer)

Shell :

  • simple, rapide, pas de dépendance
  • mais ça peut devenir illisible si vous empilez 800 lignes

Ansible :

  • rôles réutilisables, plus structuré
  • demande une discipline de versions et un runtime (ansible dans l’environnement CI)

Le mix est courant : shell pour bootstrap minimal, puis Ansible pour appliquer des rôles.

Recommandation pragmatique :

  • commencez en shell tant que l’image fait peu de choses
  • passez à Ansible si vous avez déjà des rôles éprouvés et une équipe à l’aise

Point clé, dans tous les cas : build déterministe.

  • pinner les versions quand c’est nécessaire
  • latest
  • contrôler vos dépôts (miroir interne si besoin)

Sécurité dans l’image : durcissement minimal et hygiène de build

Dans l’image, vous voulez au minimum :

  • OS à jour au moment du build
  • baseline SSH cohérente
  • hygiène de nettoyage

Sur les mises à jour automatiques : débat classique.

  • soit vous activez unattended-upgrades
  • soit vous assumez un modèle immuable strict : pas d’auto patch, on rebuild régulièrement

Les deux peuvent être valables. Mais mélangez-les consciemment. Pas par accident.

Et surtout, ne bakez jamais :

  • secrets
  • tokens
  • clés privées
  • credentials de registry
  • fichiers de config contenant des mots de passe

Traçabilité :

  • tags d’image
  • manifest Packer
  • et si vous voulez pousser plus loin, SBOM et scan vulnérabilités (on en reparle en conclusion)

cloud-init : personnaliser au boot sans casser l'immutabilité

Cloud-init fonctionne au premier boot via un user-data. Il exécute des modules dans un ordre précis, et vous pouvez lui demander de créer des users, injecter des clés SSH, écrire des fichiers avec permissions et owner, exécuter des commandes, activer et démarrer des services systemd, et configurer des montages.

Ce que vous faites avec cloud-init, en général

  • Hostname
  • Users et accès SSH
  • Configuration applicative locale : fichiers, variables, templates simples
  • Bootstrap d'un agent ou d'un service
  • Join à un cluster : Kubernetes worker, Consul, Nomad, etc.

Points d'attention qui font mal quand on débute

  • Indentation YAML incorrecte
  • Taille max du user-data selon le cloud provider
  • Échec silencieux de cloud-init faute de consultation des logs

Séparation recommandée

  • Un cloud-init « base » commun à toutes les instances
  • Des overlays par environnement (dev, stage, prod) si besoin

Exemple de fichier cloud-config : users, SSH, fichiers et services

cloud-init/base.yaml

yaml #cloud-config hostname: web-01 manage_etc_hosts: true

users:

  • name: deploy groups: [ sudo ] shell: /bin/bash sudo: [ "ALL=(ALL) NOPASSWD:ALL" ] ssh_authorized_keys: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... votre_cle_publique"

write_files:

  • path: /etc/myapp/config.env owner: root:root permissions: "0640" content: | APP_ENV=prod LOG_LEVEL=info API_ENDPOINT=https://api.example.internal

runcmd:

  • [ bash, -lc, "systemctl daemon-reload" ]
  • [ bash, -lc, "systemctl enable myapp || true" ]
  • [ bash, -lc, "systemctl restart myapp || true" ]

Ce fichier illustre trois aspects essentiels : la gestion des users avec leurs clés SSH, l'écriture de fichiers avec permissions précises, et l'exécution de commandes de bootstrap.

Deux remarques importantes :

  1. Évitez d'y mettre des secrets en clair. Si vous avez besoin de secrets au boot, passez par un secret manager et récupérez-les via un mécanisme d'identité (IAM role, workload identity, etc.)
  2. /var/log/cloud-init.log/var/log/cloud-init-output.log

Tester une Golden Image comme un produit (sinon vous déplacez juste les incidents)

Une image cassée, ce n’est pas « un serveur cassé ». C’est un incident reproductible à l’infini.

Tests minimaux, déjà très utiles :

  • boot OK
  • SSH OK
  • time sync OK
  • agents attendus démarrés
  • ports de base ouverts ou fermés comme prévu
  • espace disque et FS corrects
  • cloud-init appliqué sans erreurs

Approche plus robuste :

  • tests automatisés type Testinfra / Serverspec (même concept, peu importe l’outil)
  • validation de conformité « CIS-lite » si vous avez des exigences
  • tests cloud-init explicites : fichiers présents, services démarrés, logs propres

Critères de promotion en prod :

  • artefact validé
  • ID stable
  • changelog rempli, même succinct
  • possibilité de rollback immédiat (N-1)

Intégration CI/CD : construire, tagger, publier, promouvoir

Un pipeline type (GitHub Actions, GitLab CI, Jenkins, etc.) :

  1. packer fmt + packer validate
  2. build Packer
  3. lancer une instance de test et exécuter des smoke tests
  4. publier l’image, sauvegarder manifest + logs
  5. promotion dev vers prod

Gestion des credentials :

  • privilégiez OIDC et des rôles temporaires
  • évitez les clés longues durées dans les variables CI

Promotion :

  • soit vous gardez la même image et vous changez des tags logiques
  • soit vous copiez vers un autre compte/projet (souvent le cas en entreprise)

Côté déploiement : Terraform consomme l’ID d’image. Et vous le pinnez. C’est la base de la reproductibilité.

Erreurs classiques (et comment les éviter) quand on démarre avec Packer et cloud-init

  • Confondre provisioning et configuration : trop dans cloud-init, ou trop dans l’image. Gardez l’image comme socle.
  • Ne pas pinner les versions : un build aujourd’hui, un build demain, deux résultats différents. Parfois ça passe. Jusqu’au jour où non.
  • cloud-init qui échoue silencieusement : YAML invalide, module qui plante, et vous ne lisez pas les logs.
  • Images non testées : vous découvrez le problème quand vous scalez en prod.
  • Pas de rollback : vous hotfixez à la main, et vous revenez au monde mutable sans le dire.

Conclusion : un process simple à maintenir (et comment passer à l’étape suivante)

Si on résume sans fioritures :

  • une Golden Image, c’est le socle stable
  • cloud-init, c’est la variabilité contrôlée au boot
  • Packer, c’est la reproductibilité et l’industrialisation

Le conseil le plus rentable : commencez petit. Une image. Un pipeline. Des smoke tests. Un versioning propre. Et déjà, vous allez sentir la différence.

Ensuite vous pourrez enrichir :

  • durcissement plus strict
  • signature / attestation d’artefacts
  • SBOM et scans de vulnérabilités
  • rotation régulière des images
  • images par rôle (base, runtime, data, etc.) si ça fait sens

L’idée clé à garder en tête, celle qui change vraiment la façon d’opérer : en immuable, on corrige en rebuildant, pas en patchant à la main. Et oui, au début ça pique un peu. Puis après, c’est difficile de revenir en arrière.

Questions fréquemment posées

Qu'est-ce que l'infrastructure immuable et pourquoi est-elle préférable à l'infrastructure mutable ?

L'infrastructure immuable consiste à ne pas modifier un serveur en production, mais à le remplacer entièrement par une nouvelle image préconfigurée. Contrairement à l'approche mutable où les serveurs sont patchés ou modifiés manuellement, ce modèle évite la dérive de configuration, garantit la cohérence entre environnements et réduit les incidents liés aux différences non documentées.

Quelle est la définition d'une Golden Image et que doit-elle contenir ?

Une Golden Image est une image de base stable, versionnée et traçable qui sert de socle commun à toutes les instances. Elle contient le système d'exploitation avec mises à jour, un durcissement minimal (SSH baseline, sysctl), dépendances communes, agents transverses (monitoring, logging), utilisateurs basiques, certificats racine et paramètres système communs comme timezone ou NTP.

Quels éléments faut-il éviter d'inclure dans une Golden Image ?

Il faut éviter d'inclure des secrets (clés privées, tokens), des configurations applicatives spécifiques (URL d'API, credentials), ainsi que tout ce qui varie par instance comme hostname, IP ou configurations de rôle. Ces personnalisations doivent être gérées au boot via cloud-init pour garantir la flexibilité et la sécurité.

Comment fonctionne la personnalisation des instances avec cloud-init ?

Cloud-init est un outil standard de provisioning au premier démarrage. Il utilise un fichier user-data en YAML appelé cloud-config pour appliquer des modules dans un ordre défini : création d'utilisateurs, configuration réseau, montage de disques, bootstrap de services ou intégration dans un cluster. Cela permet de finaliser la personnalisation sans modifier l'image elle-même.

Quels sont les avantages de combiner Packer avec cloud-init pour construire des images ?

Packer permet de construire des images déclaratives, reproductibles et automatisables en CI pour différentes plateformes (AWS AMI, Azure Managed Image, GCP Image...). Associé à cloud-init qui gère la personnalisation au boot, cette combinaison industrialise la création d'images fiables et cohérentes facilitant les déploiements rapides et auditables.

Quels pièges classiques faut-il éviter lors de la création d'une Golden Image ?

Il faut éviter que l'image soit trop lourde (longue à builder/démarrer), trop spécifique (une image par micro-service perdant le socle commun) ou non testée (réplication massive de bugs). Un bon versioning clair avec changelog et tags immuables est essentiel pour gérer efficacement les images.

Enregistrer un commentaire

0 Commentaires

Comments

Nouveau Drop

Boutique Officielle

Soutenez le blog monblog-sa-abasse et découvrez nos vêtements & accessoires exclusifs en édition limitée.

Découvrir la collection
Paiement Sécurisé
Livraison Monde

Ad Code

Soutenez la Création

Aidez-moi à partager du contenu exclusif.

Soutenir