Atelier

Packer

 

Master Expert Technologie de l'information EPITECH 2020.

Co-fondateur et CTO d'une startup dans l'Edtech 2019 - fin 2022. (+3 ans)

Formation PSPO-1 Agile Scrum 2022.

Co-fondateur et CTO d'une startup dans la Deeptech fin 2022 - aujourd'hui.

Valentin MONTAGNE

1

Découverte de Packer

3

Packer et AWS

2

Packer et Docker

Déroulement du cours

Chaque séance débutera par la présentation d'un concept et de l'intérêt d'utilisation de celui-ci.

1

Théorie

Après la théorie, nous verrons alors la pratique en réalisant des exercices sur un repository gitlab.

2

Pratique

Nous verrons ensemble la correction des travaux pratiques. N'hésitez pas à poser vos questions.

3

Correction

Déroulement des journées

Rendu TP et exercices

Pour faciliter le rendu final des TPs et exercices, vous allez créer un dépôt sur Github avec le nom Packer en publique et le cloner sur votre machine.

 

Si vous préférez le garder en privé, vous devez m'ajouter avec les droits pour voir votre dépôt avec le nom d'utilisateur : ValentinMontagne

Découverte de Packer

1.

Qu'est-ce que Packer ?

Crée des images machines multi-plateformes à partir d'une configuration comme AWS AMI, VMware, VirtualBox, etc.

X

X

Léger, rapide et compatible avec tous les systèmes d’exploitation majeurs, il est en complément avec les outils de configuration comme Chef et Puppet.

Une image machine est un fichier statique contenant un système d’exploitation pré-configuré et des logiciels installés. Elle sert à lancer rapidement des machines identiques.

Pourquoi utiliser Packer ?

Les images machines pré-bâties étaient peu utilisées, car leur création manuelle était longue, complexe et difficile à automatiser.

Packer automatise totalement la création d’images machines, quel que soit le type ou la plateforme, rendant le processus plus simple et reproductible.

Les serveurs sont prêts en quelques secondes, totalement configurés. Cela accélère la production et le développement, où les VM locales n’ont plus besoin d’un provisioning lent à chaque lancement.

Pourquoi utiliser Packer ? - 2

Packer génère des images identiques pour plusieurs plateformes : vous pouvez par exemple exécuter la production sur AWS, la préprod sur OpenStack, et le développement sur VirtualBox.

Comme toute la configuration est faite à la construction de l’image, les erreurs apparaissent immédiatement et non à l’exécution.

Ces images peuvent ensuite être testées rapidement (smoke tests) pour garantir que toutes les futures instances fonctionnent correctement.

Cas d'utilisations

Dans une CD : à chaque modification des scripts d’infrastructure (Chef, Puppet…), il génère des images pour plusieurs plateformes. Elles peuvent être vérifiées avant même leur déploiement.

Dans un env Dev/Prod : les mêmes templates permettent de créer à la fois une image pour AWS (prod) et une VM pour VMware/Vagrant.

Pour les outils clés en main ou les démos : permet de générer des images pour toutes les plateformes avec un outil déjà pré-configuré prêt à l'emploi.

Les éléments importants dans Packer

Découvrons les éléments spécifiques à Packer comme :

  • Artifact : le résultat final d’un Build d’image comme un ID pour AMI AWS ou un dossier pour VMWare.
  • Build : est une tâche unique qui crée un artifact pour une seule plateforme (AWS, VMware, VirtualBox, etc.).
  • Builder : composant qui exécute le Build en lisant la configuration fournie pour une sortie spécifique (amazon-ebs)
  • Data Source : permet de récupérer des données depuis l’extérieur (cloud, API…) pour les réutiliser dans un template.

Les éléments importants dans Packer - 2

  • Post-Processor : modifie, transforme ou déplace l’artifact généré pour créer un nouvel artifact après un Builder.
  • Provisioner : installent et configurent les logiciels à l’intérieur d’une machine en cours de création, avant qu’elle ne devienne un artifact figé.
  • Template : des fichiers en HCL ou JSON qui définissent un ou plusieurs builds en configurant les différents composants de Packer (builders, provisioners, post-processors, etc.).

Comment créer une template ?

Packer utilise lui aussi le HCL comme Terraform. (Attention JSON est déprécié)

On retrouve alors les mêmes éléments dans le fichier de configuration :

  • Blocs : Ce sont des conteneurs de configuration. Chaque bloc a un type (ex. source, build), peut avoir des étiquettes (labels), et contient un corps avec des arguments et éventuellement d'autres blocs imbriqués.
  • Arguments : Ils attribuent une valeur à un nom (ex. region = "eu-west-1").
  • Expressions : Elles représentent une valeur (littérale ou calculée).

Exemple de la syntaxe

Block - Source

Le block source définit où et comment Packer va créer l'image de base pour le build.

source "amazon-ebs" "web_server" {
  region        = "eu-west-1"
  instance_type = "t3.micro"
  ssh_username  = "ubuntu"
  ami_name      = "mon-image-{{timestamp}}"
  
  source_ami_filter {
    filters = {
      name = "ubuntu/images/hvm-ssd/ubuntu-22.04-*"
    }
    owners = ["099720109477"]
    most_recent = true
  }
}

# Utilisation
build {
  sources = ["source.amazon-ebs.web_server"]
  
  provisioner "shell" {
    inline = ["echo 'Hello World'"]
  }
}

Builders

Les builders sont les composants de Packer qui savent comment créer des images pour une plateforme spécifique. Ce sont les "moteurs" qui transforment une image de base en image personnalisée.

Comme les providers dans Terraform, il y a un grand nombre de builders à utiliser pour intégrer des outils ou des types d'image machine comme AWS, Docker ou même Ansible :

https://developer.hashicorp.com/packer/integrations

Builders - Fonctionnement

Un builder fonctionne de la manière suivante :

  1. Lance une instance temporaire (EC2, conteneur, VM...)
  2. Se connecte via SSH/WinRM
  3. Exécute les provisioners
  4. Capture l'état final en image
  5. Nettoie les ressources temporaires

 

Les builders sont donc les adaptateurs qui permettent à Packer de parler le "langage" de chaque cloud ou plateforme de virtualisation.

Builders - Les plus connus

Builder Plateforme Sortie
amazon-ebs AWS AMI
docker Docker Image Docker
virtualbox-iso VirtualBox VM/OVA
vmware-iso VMware VMDK
azure-arm Azure Image managée
googlecompute GCP Image GCE

Block - Build

Le block build orchestre le processus de création d'image. Il définit quoi faire et dans quel ordre pour transformer votre image de base en image finale.

 

 

 

 

 

 

 

Le block build est donc le chef d'orchestre qui coordonne toutes les étapes pour créer votre image personnalisée.

build {
  name    = "nom_du_build"
  sources = ["source.type.nom"]
  
  # Provisioners (étapes de configuration)
  provisioner "shell" {
    inline = ["commandes..."]
  }
  
  # Post-processors (actions après build)
  post-processor "manifest" {
    output = "manifest.json"
  }
}

Block - Build

Fonctionnement du Build :

  1. Démarre les sources (instances temporaires)
  2. Exécute les provisioners dans l'ordre
  3. Capture l'image finale
  4. Lance les post-processors
  5. Nettoie les ressources temporaires

Points clés :

  • Un build = Un pipeline complet
  • Ordre séquentiel : Provisioners exécutés dans l'ordre déclaré
  • Multi-sources : Peut utiliser plusieurs sources simultanément
  • Nommage : Permet d'identifier et filtrer les builds
  • Gestion d'erreurs : Arrêt en cas d'échec d'un provisioner

Block - Build - Composants

1. Sources - le point de départ

sources = [
  "source.amazon-ebs.web",
  "source.docker.web"
]

2. Provisioners - Configuration

provisioner "shell" {
  script = "./install.sh"
}

provisioner "file" {
  source = "./app/"
  destination = "/opt/app/"
}

3. Post-processors - Actions finales

post-processor "docker-tag" {
  repository = "mon-app"
  tags = ["latest", "v1.0"]
}

Block - Build - Exemple

build {
  name = "web-server"
  sources = ["source.amazon-ebs.ubuntu"]
  
  # Installation
  provisioner "shell" {
    inline = [
      "sudo apt update",
      "sudo apt install -y nginx"
    ]
  }
  
  # Configuration
  provisioner "file" {
    source = "./nginx.conf"
    destination = "/tmp/nginx.conf"
  }
  
  # Finalisation
  provisioner "shell" {
    inline = ["sudo mv /tmp/nginx.conf /etc/nginx/"]
  }
}

Exemple d'une Template entière pour une image AWS

# Variables
variable "aws_region" {
  type        = string
  default     = "eu-west-1"
  description = "Région AWS pour le build"
}

variable "instance_type" {
  type        = string
  default     = "t3.micro"
  description = "Type d'instance EC2 pour le build"
}

variable "ssh_username" {
  type        = string
  default     = "ubuntu"
  description = "Nom d'utilisateur SSH"
}

variable "ami_name_prefix" {
  type        = string
  default     = "nginx-static-web"
  description = "Préfixe du nom de l'AMI"
}

# Configuration des sources locales
locals {
  timestamp = regex_replace(timestamp(), "[- TZ:]", "")
  ami_name  = "${var.ami_name_prefix}-${local.timestamp}"
}

# Source AMI
source "amazon-ebs" "nginx_server" {
  region          = var.aws_region
  instance_type   = var.instance_type
  ssh_username    = var.ssh_username
  ami_name        = local.ami_name
  ami_description = "AMI Ubuntu 22.04 avec NGINX pour contenu web statique - Built with Packer"
  
  source_ami_filter {
    filters = {
      name                = "ubuntu/images/hvm-ssd/ubuntu-22.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    owners      = ["099720109477"] # Canonical
    most_recent = true
  }

  # Configuration du stockage
  ebs_optimized = true
  
  # Tags pour l'AMI
  tags = {
    Name         = "nginx-static-web"
    Environment  = "production"
    OS           = "Ubuntu 22.04"
    Application  = "NGINX"
    BuildDate    = timestamp()
    BuildTool    = "Packer"
  }

  # Tags pour l'instance de build
  run_tags = {
    Name = "packer-nginx-build"
  }
}

# Build
build {
  name = "nginx-static-web"
  sources = ["source.amazon-ebs.nginx_server"]

  # Mise à jour du système
  provisioner "shell" {
    inline = [
      "echo 'Mise à jour du système...'",
      "sudo apt-get update",
      "sudo apt-get upgrade -y",
      "sudo apt-get install -y curl wget unzip"
    ]
  }

  # Installation et configuration de NGINX
  provisioner "shell" {
    inline = [
      "echo 'Installation de NGINX...'",
      "sudo apt-get install -y nginx",
      "sudo systemctl enable nginx",
      "echo 'NGINX installé et activé'"
    ]
  }

  # Copie du contenu web statique
  provisioner "file" {
    source      = "./web-content/"
    destination = "/tmp/web-content/"
  }

  # Configuration du site web
  provisioner "shell" {
    inline = [
      "echo 'Configuration du contenu web...'",
      "sudo rm -rf /var/www/html/*",
      "sudo cp -r /tmp/web-content/* /var/www/html/",
      "sudo chown -R www-data:www-data /var/www/html",
      "sudo chmod -R 755 /var/www/html"
    ]
  }

  # Configuration NGINX personnalisée
  provisioner "file" {
    content = <<-EOF
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    root /var/www/html;
    index index.html index.htm;
    
    server_name _;
    
    # Gestion des fichiers statiques
    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # Configuration pour SPA (Single Page Application)
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # Sécurité - masquer la version de NGINX
    server_tokens off;
    
    # Headers de sécurité
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
}
EOF
    destination = "/tmp/nginx-site.conf"
  }

  # Application de la configuration NGINX
  provisioner "shell" {
    inline = [
      "echo 'Application de la configuration NGINX...'",
      "sudo cp /tmp/nginx-site.conf /etc/nginx/sites-available/default",
      "sudo nginx -t",
      "sudo systemctl reload nginx",
      "sudo systemctl status nginx"
    ]
  }

  # Nettoyage et optimisation
  provisioner "shell" {
    inline = [
      "echo 'Nettoyage du système...'",
      "sudo apt-get autoremove -y",
      "sudo apt-get autoclean",
      "sudo rm -rf /tmp/*",
      "sudo rm -rf /var/tmp/*",
      "history -c",
      "echo 'Build terminé avec succès!'"
    ]
  }

  # Validation finale
  provisioner "shell" {
    inline = [
      "echo 'Validation de l'installation...'",
      "nginx -v",
      "sudo systemctl is-active nginx",
      "curl -I http://localhost || echo 'Test HTTP échoué'",
      "echo 'Validation terminée'"
    ]
  }
}

# Post-processor pour manifest
build {
  sources = ["source.amazon-ebs.nginx_server"]
  
  post-processor "manifest" {
    output = "manifest.json"
    strip_path = true
    custom_data = {
      build_time = timestamp()
      nginx_version = "latest"
    }
  }
}

Le CLI

Packer est utilisable via un CLI, voici les commandes principales : init, plugins, validate, fmt, inspect, build, console.

Avec plusieurs options globales :

Option Description Exemple
-var Variable inline -var="region=us-east-1"
-var-file Fichier de variables -var-file="prod.pkrvars.hcl"
-only Sources spécifiques -only="source.docker.web"
-except Exclure des sources -except="source.vmware.*"
-parallel-builds Limite parallélisme -parallel-builds=3

Le CLI - init

La commande packer init installe automatiquement les plugins requis par votre configuration Packer, similaire à terraform init.

packer init .                          # Initialise le répertoire courant
packer init template.pkr.hcl           # Initialise pour un fichier spécifique
packer init -upgrade .                 # Met à jour les plugins existants
packer {
  required_plugins {
    docker = {
      source  = "github.com/hashicorp/docker"
      version = "~> 1.0"
    }
    amazon = {
      source  = "github.com/hashicorp/amazon" 
      version = ">= 1.2.0"
    }
  }
}

Récupère les informations des plugins à installer dans le block packer.

Le CLI - plugins

La commande packer plugins permet de gérer les plugins Packer : installation, mise à jour, listing et diagnostic.

packer plugins install github.com/hashicorp/docker
packer plugins install github.com/hashicorp/amazon@v1.2.8
packer plugins install --path ./custom-plugin

packer plugins remove github.com/hashicorp/docker
packer plugins remove github.com/hashicorp/docker@1.2.0

$ packer plugins installed
┌──────────────────────────────┬─────────┬──────────────┐
│ Plugin                       │ Version │ API Version  │
├──────────────────────────────┼─────────┼──────────────┤
│ github.com/hashicorp/amazon  │ v1.2.8  │ x5.0, x5.1   │
│ github.com/hashicorp/docker  │ v1.0.9  │ x5.0, x5.1   │
│ github.com/hashicorp/vsphere │ v1.2.1  │ x5.0, x5.1   │
└──────────────────────────────┴─────────┴──────────────┘

Le CLI - validate

Valide la configuration actuelle en vérifiant tous les fichiers Packer.

Cela permet de facilement corriger des erreurs avant de lancer le build, facilement ajoutable dans une pipeline CI ou git hooks.

packer validate template.pkr.hcl                 # Validation syntaxe + logique

packer validate -var-file="vars.pkrvars.hcl" .   # Avec variables

packer validate -syntax-only template.pkr.hcl    # Syntaxe uniquement

Le CLI - fmt

Formate le code de tous les fichiers de configuration Packer automatiquement.

Cela permet de facilement corriger des erreurs de mise en forme avant de lancer le build, facilement ajoutable dans une pipeline CI ou git hooks.

packer fmt template.pkr.hcl                      # Format un fichier

packer fmt .                                     # Format tous les .pkr.hcl

packer fmt -diff .                               # Affiche les différences

packer fmt -write=false .                        # Mode dry-run

Le CLI - inspect

Récupère toutes les informations des différents fichiers de configuration pour afficher proprement toutes les informations sur les variables disponibles, les builds, etc.

Cela permet de facilement comprendre ce qu'il est possible de faire avec ce projet Packer et de le configurer.

packer inspect .

Packer Inspect: HCL2 mode
> input-variables:

var.docker_image: "ubuntu:lunar"

> local-variables:

> builds:

  > learn-packer:

    sources:
    
      docker.ubuntu
      docker.ubuntu-focal

    provisioners:

      shell
      shell
      file

    post-processors:

      0:
        docker-tag
      1:
        docker-tag
      2:
        manifest

Le CLI - build

Construis les images automatiquement en utilisant les fichiers de configuration.

packer build template.pkr.hcl                    # Build basique

packer build -var="region=eu-west-1" template.pkr.hcl  # Avec variables

packer build -var-file="prod.pkrvars.hcl" .      # Avec fichier de variables

packer build -only="source.docker.web" .         # Build sélectif

packer build -parallel-builds=2 .                # Builds parallèles limités

Le CLI - console

Lance une console interactive avec toute la configuration pour pouvoir vérifier des expressions HCL comme la valeur d'une variable comme var.region.

packer console .                                 # Test d'expressions HCL

# Dans la console :
# > var.region
# > local.timestamp

Des questions ?

Packer et Docker

2.

TP - Débuter avec Docker

Tutoriel officiel

Packer possède déjà plusieurs tutoriels officiels pour apprendre à utiliser l'outil, nous allons utiliser en premier lieu celui de Docker :

https://developer.hashicorp.com/packer/tutorials/docker-get-started

 

Le tutoriel fait office de TP guidé pour avoir la base nécessaire pour réaliser les exercices suivants, il faut réaliser le tutoriel jusqu'aux post-processors inclus.

Exercice 1 : Variables

Ajoutez des variables à la place des valeurs codées en dur suivantes :

  1. Le nom de l’image ubuntu-focal

  2. Le contenu du fichier example.txt

  3. Les tags des images

Vous devez :

  • Déclarer chaque variable dans un fichier variables.pkr.hcl

  • Fournir des valeurs par défaut

  • Déplacer celles déjà présentes.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 2 : Outputs

Ajoutez un Post-Processor pour générer automatiquement un fichier manifest.json qui liste les Artifacts créés durant le build.

N'hésitez pas à regarder la documentation des Post-Processors :

https://developer.hashicorp.com/packer/docs/post-processors/manifest

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 3 : Provisioner File

Ajoutez un Provisioner pour ajouter automatiquement un fichier welcome.txt dans l'image générée. Le fichier doit être créé localement puis copié dans l'image.

Objectifs :

  1. Ajouter un fichier welcome.txt contenant "Bienvenue dans mon image Docker personnalisée !" dans votre dossier.
  2. Placer ce fichier dans /home/ de l'image finale.
  3. Vérifier que le fichier est présent en listant le contenu.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 4 : Créer une autre image

  1. Créer une configuration Packer qui build 2 images en parallèle : Une image Ubuntu 22.04 et une image Alpine Linux 3.19.
  2. Chaque image doit contenir : Un fichier system-info.txt avec les informations du système, les outils de base installés (curl, wget), un script show-info.sh qui affiche les informations système.
  3. Tagger les images avec des noms distincts : multi-os-ubuntu:latest, multi-os-alpine:latest

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 5 : Créer une image d'une application

Utilisez un projet d'exemple pour créer une image prête à l'emploi :

https://gitlab.com/vm-marvelab/ci-cd

Le projet de base est une API très simple en NodeJS, n'hésitez pas à lire le README pour comprendre comment fonctionne la route /auth.

Utilisez la commande pour installer les dépendances du projet :

npm install

Ensuite utilisez la commande pour lancer l'API :

npm start

Appelez-moi pour que l'on puisse valider ensemble.

Packer et AWS

3.

TP - Débuter avec AWS

Tutoriel officiel

Packer possède déjà plusieurs tutoriels officiels pour apprendre à utiliser l'outil, nous allons utiliser celui d'AWS :

https://developer.hashicorp.com/packer/tutorials/aws-get-started

 

Comme pour Terraform, nous avons accès à un Lab AWS pour réaliser le TP et les exercices. Attention, utilisez la même technique qu'avec Terraform pour vous identifier, avec un fichier credentials dans le dossier .aws à la racine de votre Utilisateur. Utilisez la région us-east-1. (Documentation)

 

Le tutoriel fait office de TP guidé pour avoir la base nécessaire pour réaliser les exercices suivants, il faut réaliser le tutoriel jusqu'aux post-processors inclus.

Exercice 1 : Installer des outils

Ajoutez des outils à notre image AMI généré pour AWS :

  1. Htop, pour améliorer le monitoring

  2. Git, pour ajouter des dépôts de code

  3. Jq, pour formater du JSON depuis le terminal

 

Vérifiez ensuite en vous connectant sur votre instance EC2 en SSH que vous puissiez lancer ces différents outils.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 2 : Variables et environnements

Ajoutez une variable "node_version" :

  1. La variable doit être typé.

  2. La variable doit être validé pour vérifier qu'elle respecte le format x.y.z.

Ensuite, créez une variable "env" qui peut avoir les valeurs : dev, staging et production et n'utilisez node_version pour Node seulement si le build est dev ou staging sinon il faut utiliser la version LTS en production.

Utilisez l'outil nvm pour changer facilement de version. (Documentation)

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 3 : Déployer sur AWS

Créez une image machine AMI sur AWS via Packer qui doit être un serveur ubuntu utilisant Nginx pour délivrer du contenu HTML statique sur le port 80.

Ensuite, avec ce que vous avez appris sur Terraform, déployez automatiquement une instance EC2 sur le Lab AWS avec votre AMI construit avec Packer.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 4 : Healthcheck

Ajoutez dans le build de votre image AMI un test permettant de vérifier la configuration du nginx, son status et de vérifier via un healthcheck une requête sur le port 80 pour vérifier son fonctionnement directement dans le build.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 5 : Déployer sur AWS et Docker

Pour simuler plusieurs environnement entre développement et production, créez la même image machine en format AMI sur AWS et format Docker via Packer qui doit être un serveur ubuntu utilisant Nginx pour délivrer du contenu HTML statique sur le port 80.

Vérifiez que les différentes plateformes fonctionnent en lançant en local un container Docker qui utilise votre image et déployez via Terraform l'instance EC2.

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 6 : Déployer sur AWS et Docker une application

Récupérez un projet que vous avez déjà réalisé et réalisez une image Docker et AMI de ce projet avec Packer.

Déployez l'image Docker et les images nécessaires pour votre projet en utilisant Terraform sur le compte AWS fournit pour cet atelier.

(Si vous n'avez pas de projet d'application, vous êtes libre de choisir une technologie ou outil déjà existant comme un Wordpress par exemple)

 

Appelez-moi pour que l'on puisse valider ensemble.

Exercice 7 Bonus : Mise en place d'une CI / CD

Mettez en place une CI / CD sur Gitlab CI ou Github Actions pour réaliser l'exercice précédent mais de manière automatisé en lançant une pipeline qui doit :

  • Vérifier, valider, tester le code du projet
  • Vérifier et valider le format et la configuration Packer.
  • Vérifier et valider le format et la configuration Terraform.
  • Lancer les builds ou plans.
  • Lancer le déploiement.

 

Appelez-moi pour que l'on puisse valider ensemble.

Soutenance

QCM

30 minutes

4.

N'hésitez pas à me donner votre avis.

Merci!

Packer

By Valentin MONTAGNE

Packer

  • 56