L'automatisation du déploiement des machines virtuelles (VM) est une pratique essentielle dans le domaine de l'infrastructure informatique moderne. Packer et Proxmox sont deux outils puissants qui, lorsqu'ils sont combinés, offrent une solution efficace pour la création automatisée d'images de VM. Cet article explore les raisons pour lesquelles utiliser Packer avec Proxmox est bénéfique, ainsi que la mise en œuvre pratique de cette combinaison.

Pourquoi utiliser Packer avec Proxmox ?

  1. Reproductibilité et consistance : Packer permet de définir l'ensemble des configurations d'une image VM dans un fichier au format HCL ou JSON. En utilisant Packer, vous garantissez une reproductibilité dans le processus de création d'images, assurant une uniformité dans vos déploiements.
  2. Support multiplateforme : Packer prend en charge plusieurs fournisseurs, et Proxmox en est un parmi tant d'autres. Cette flexibilité vous permet de créer des images qui peuvent être déployées sur différents types de plateformes, que ce soit sur site ou dans le cloud.
  3. Optimisation des ressources : Vous pouvez automatiser la création d'images légères et optimisées, en éliminant les composants inutiles et en appliquant des configurations spécifiques à votre environnement.
  4. Économie de temps : L'automatisation du processus de création d'images réduit considérablement le temps nécessaire pour préparer des VM. Vous pouvez automatiser des tâches telles que l'installation de logiciels, la configuration réseau, et bien d'autres, accélérant ainsi le cycle de déploiement.

Comment utiliser Packer avec Proxmox ?

Installez Packer sur votre machine de développement et configurez-le pour utiliser le fournisseur Proxmox en spécifiant les détails de connexion (adresse IP du serveur où sera créée l'image, nom d'utilisateur, mot de passe). Pour l'installation, je ne peux que vous suggérer de visiter le site officiel à cette adresse.

Il y a ensuite 3 étapes dans la démarche de création d'image avec Packer :

  1. Création des fichiers de définition du modèle Packer et de configuration automatisée de l'OS
    • Créez un fichier de configuration Packer au format HCL ou JSON en décrivant les étapes nécessaires à la création de votre image Proxmox.
    • Spécifiez les paramètres tels que le type de système d'exploitation, les scripts de provisionnement, et les paramètres spécifiques à Proxmox.
    • Les systèmes d'exploitation permettent aujourd'hui d'être configurés de manière autonome et dynamique, sans avoir à cliquer ou entrer des commandes. Pour ce faire, l'utilisation de fichier de configuration est à prendre en compte et à maintenir en fonction de l'évolution du besoin.
  2. Exécution du build

Lancez le processus de construction (build) avec Packer. Packer se connectera à Proxmox, créera une VM temporaire, appliquera les configurations définies, et transformera cette VM en une image clonable une fois le processus terminé avec succès.

  1. Validation et distribution :
    • Vérifiez l'image générée en la déployant sur une VM Proxmox.
    • Distribuez l'image sur d'autres hôtes Proxmox ou sauvegardez-la pour une utilisation future.

Mon cas d'usage et ma configuration

J'utilise Packer dans mon homelab pour générer des modèles (Debian 12 et Ubuntu 22.04) que je réutiliserai ensuite via Open Tofu pour construire les VM nécessaires. Pour faciliter la personnalisation, j'utilise au maximum des variables. Ainsi, le fichier de base est statique et seules les variables (dans un fichier à part) seront à modifier selon le besoin.

J'ai déjà téléversé l'image ISO de Debian 12 dans un stockage de mon hôte Proxmox. Il est possible d'utiliser Packer pour télécharger l'image ISO depuis l'internet et l'envoyer sur votre hôte Proxmox. Je n'ai pas retenu cette solution pour limiter les accès web et me mettre en condition "auto-hébergement" complet.

Pour effectuer la connexion entre Proxmox et Packer, j'ai créé un utilisateur dans le realm "pve", puis j'ai créé un jeton à cet utilisateur et enfin mis des droits sur l'API token :

  • Dans l'onglet "Datacenter", dirigez-vous dans la rubrique "Permissions" et cliquez sur "Users" et le bouton "Add". Entrez les informations nécessaires (pas besoin de mot de passe) puis enregistrer le formulaire. Retenez bien le nom de cet utilisateur.
  • Toujours dans la rubrique "Permissions", cliquez sur "API Token" et le bouton "Add". Sélectionnez l'utilisateur précédemment créé, donnez un nom au token (à retenir) et poursuivez le formulaire ; un secret sera alors affiché et devra être stocké en lieu sûr. Ce secret est nécessaire pour opérer Proxmox avec Packer.
  • Enfin, retournez dans la rubrique "Permissions", puis cliquez sur le bouton "Add - API Token Permission" : sélectionnez l'utilisateur avec l'identifiant API créé à l'instant, et ajoutez-lui les droits suivants :
    • Path : "/"
    • Role : "PVEAdmin" (c'est beaucoup, mais pas le choix. Sinon, vous pouvez créer votre propre rôle avec les droits nécessaires…)
    • Propagate : cochez la case (utile pour avoir les permissions sur l'ensemble de l'infrastructure — machines, stockage, réseau)

Dans le dossier où sont stockés mes fichiers, j'ai créé un sous-dossier "autoinstall", pour y placer le fichier preseed permettant l'installation automagique de Debian.

~/git/homelab/packer$ tree
.
├── autoinstall
│   ├── preseed.cfg
├── customdeb12.pkrvars.hcl
└── debian12.pkr.hcl

Désormais, voici donc les fichiers nécessaires pour créer une image Debian 12 avec Packer, pour Proxmox :

Fichier "debian12.pkr.hcl" : c'est ce fichier qui définit le besoin pour le bon fonctionnement de Packer dans notre cas d'usage, la base de notre modèle, la connexion vers le serveur Proxmox et les variables.

// debian12.pkr.hcl

packer {
  required_plugins {
    name = {
      version = "1.1.6"
      source  = "github.com/hashicorp/proxmox"
    }
  }
}

variable "bios_type" {
  type = string
}

variable "boot_command" {
  type = string
}

variable "boot_wait" {
  type = string
}

variable "bridge_firewall" {
  type    = bool
  default = false
}

variable "bridge_name" {
  type = string
}

variable "cloud_init" {
  type = bool
}

variable "iso_file" {
  type = string
}

variable "iso_storage_pool" {
  type    = string
  default = "local"
}

variable "machine_default_type" {
  type    = string
  default = "pc"
}

variable "network_model" {
  type    = string
  default = "virtio"
}

variable "os_type" {
  type    = string
  default = "l26"
}

variable "proxmox_api_token_id" {
  type = string
}

variable "proxmox_api_token_secret" {
  type      = string
  sensitive = true
}

variable "proxmox_api_url" {
  type = string
}

variable "proxmox_node" {
  type = string
}

variable "qemu_agent_activation" {
  type    = bool
  default = true
}

variable "scsi_controller_type" {
  type = string
}

variable "ssh_timeout" {
  type = string
}

variable "tags" {
  type = string
}

variable "io_thread" {
  type = bool
}

variable "cpu_type" {
  type    = string
  default = "kvm64"
}

variable "vm_info" {
  type = string
}

variable "disk_discard" {
  type    = bool
  default = true
}

variable "disk_format" {
  type    = string
  default = "qcow2"
}

variable "disk_size" {
  type    = string
  default = "16G"
}

variable "disk_type" {
  type    = string
  default = "scsi"
}

variable "nb_core" {
  type    = number
  default = 1
}

variable "nb_cpu" {
  type    = number
  default = 1
}

variable "nb_ram" {
  type    = number
  default = 1024
}

variable "ssh_username" {
  type = string
}

variable "ssh_password" {
  type = string
}

variable "ssh_handshake_attempts" {
  type = number
}

variable "storage_pool" {
  type    = string
  default = "local-lvm"
}

variable "vm_id" {
  type    = number
  default = 99999
}

variable "vm_name" {
  type = string
}

locals {
  packer_timestamp = formatdate("YYYYMMDD-hhmm", timestamp())
}

source "proxmox-iso" "debian12" {
  bios                     = "${var.bios_type}"
  boot_command             = ["${var.boot_command}"]
  boot_wait                = "${var.boot_wait}"
  cloud_init               = "${var.cloud_init}"
  cloud_init_storage_pool  = "${var.storage_pool}"
  communicator             = "ssh"
  cores                    = "${var.nb_core}"
  cpu_type                 = "${var.cpu_type}"
  http_directory           = "autoinstall"
  insecure_skip_tls_verify = true
  iso_file                 = "${var.iso_file}"
  machine                  = "${var.machine_default_type}"
  memory                   = "${var.nb_ram}"
  node                     = "${var.proxmox_node}"
  os                       = "${var.os_type}"
  proxmox_url              = "${var.proxmox_api_url}"
  qemu_agent               = "${var.qemu_agent_activation}"
  scsi_controller          = "${var.scsi_controller_type}"
  sockets                  = "${var.nb_cpu}"
  ssh_handshake_attempts   = "${var.ssh_handshake_attempts}"
  ssh_pty                  = true
  ssh_timeout              = "${var.ssh_timeout}"
  ssh_username             = "${var.ssh_username}"
  ssh_password             = "${var.ssh_password}"
  tags                     = "${var.tags}"
  template_description     = "${var.vm_info} - ${local.packer_timestamp}"
  token                    = "${var.proxmox_api_token_secret}"
  unmount_iso              = true
  username                 = "${var.proxmox_api_token_id}"
  vm_id                    = "${var.vm_id}"
  vm_name                  = "${var.vm_name}"

  disks {
    discard      = "${var.disk_discard}"
    disk_size    = "${var.disk_size}"
    format       = "${var.disk_format}"
    io_thread    = "${var.io_thread}"
    storage_pool = "${var.storage_pool}"
    type         = "${var.disk_type}"
  }

  network_adapters {
    bridge   = "${var.bridge_name}"
    firewall = "${var.bridge_firewall}"
    model    = "${var.network_model}"
  }
}

build {
  sources = ["source.proxmox-iso.debian12"]
}

Fichier "custom.pkvars.hcl" : Les variables permettent de personnaliser le modèle.

// custom.pkvars.hcl

bios_type                = "seabios"
boot_command             = "<esc><wait>auto console-keymaps-at/keymap=fr console-setup/ask_detect=false debconf/frontend=noninteractive fb=false url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg<enter>"
boot_wait                = "10s"
bridge_name              = "vmbr0"
bridge_firewall          = false
cloud_init               = true
cpu_type                 = "x86-64-v2-AES"
disk_discard             = true
disk_format              = "qcow2"
disk_size                = "12G"
disk_type                = "scsi"
iso_file                 = "local:iso/debian-12.4.0-amd64-netinst.iso"
machine_default_type     = "q35"
nb_core                  = 1
nb_cpu                   = 1
nb_ram                   = 2048
network_model            = "virtio"
io_thread                = false
os_type                  = "l26"
proxmox_api_token_id     = "packbot@pve!nom_token"
proxmox_api_token_secret = "aaaaaa-aaaa-bbbb-cccc-123456789012"
proxmox_api_url          = "https://ip_proxmox:8006/api2/json"
proxmox_node             = "nom_hôte_proxmox"
qemu_agent_activation    = true
scsi_controller_type     = "virtio-scsi-pci"
ssh_handshake_attempts   = 6
ssh_timeout              = "35m"
ssh_username             = "jho"
ssh_password             = "pouetpouet"
storage_pool             = "stoCeph"
tags                     = "template"
vm_id                    = 99998
vm_info                  = "Debian 12 Packer Template"
vm_name                  = "pckr-deb12"

Quelques notes sur ces variables :

  • N'oubliez pas de changer les valeurs du "bridge_name", de "iso_file" (le nom du stockage où est l'image ISO et son nom), les "proxmox_", les "ssh_*"...
  • La variable "boot_command" permet de forcer l'ISO à utiliser le fichier preseed
  • "cloud_init" est installé et activé pour faciliter la configuration des VM par Open Tofu

Je vous laisse prendre en compte les autres variables et les modifier selon votre besoin. Pour plus d'informations, rendez-vous sur le site officiel d'HashiCorp à cette adresse.

Fichier "autoinstall/preseed.cfg" : fichier de configuration de l'installation de Debian, pour être en zéro-touch.

#_preseed_V1
d-i debian-installer/language string en
d-i debian-installer/country string FR
d-i debian-installer/locale string en_US.UTF-8
d-i localechooser/supported-locales multiselect en_US.UTF-8, fr_FR.UTF-8
d-i keyboard-configuration/xkb-keymap select fr
d-i console-keymaps-at/keymap select fr-latin9
d-i debian-installer/keymap string fr-latin9
d-i netcfg/choose_interface select auto
d-i netcfg/link_wait_timeout string 5
d-i netcfg/dhcp_timeout string 60
d-i netcfg/get_hostname string pckr-deb12
d-i netcfg/get_domain string local.hommet.net
d-i netcfg/wireless_wep string
d-i hw-detect/load_firmware boolean false
d-i mirror/country string FR
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i passwd/root-login boolean true
d-i passwd/make-user boolean true
d-i passwd/root-password password pouetpouet
d-i passwd/root-password-again password pouetpouet
d-i passwd/user-fullname string jho
d-i passwd/username string jho
d-i passwd/user-password password pouetpouet
d-i passwd/user-password-again password pouetpouet
d-i clock-setup/utc boolean true
d-i time/zone string Europe/Paris
d-i clock-setup/ntp boolean true
d-i clock-setup/ntp-server string 0.fr.pool.ntp.org
d-i partman-auto/disk string /dev/sda
d-i partman-auto/method string lvm
d-i partman-auto-lvm/guided_size string max
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select multi
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman-md/confirm boolean true
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/mount_style select uuid
d-i base-installer/install-recommends boolean false
d-i apt-setup/cdrom/set-first boolean false
d-i apt-setup/use_mirror boolean true
d-i apt-setup/security_host string security.debian.org
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/include string qemu-guest-agent sudo ca-certificates cloud-init
d-i pkgsel/upgrade select safe-upgrade
popularity-contest popularity-contest/participate boolean false
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean false
d-i grub-installer/bootdev string default
d-i finish-install/reboot_in_progress note
d-i cdrom-detect/eject boolean true

Avec tous les fichiers ci-dessus et la configuration de l'utilisateur dans Proxmox, vous devriez être en mesure de créer une machine. Les commandes Packer à lancer sont les suivantes :

# initialisation du dépôt pour récupérer les dépendances :
packer init debian12.pkr.hcl

# valider les fichiers Packer :
packer validate -var-file=customdeb12.pkrvars.hcl debian12.pkr.hcl

# construire l'image, en remplaçant l'artefact si déjà existant :
packer build -on-error=ask -force -var-file=customdeb12.pkrvars.hcl debian12.pkr.hcl

Retrouvez les fichiers de cet article à jour dans mon dépôt GitHub : https://github.com/julienhmmt/homelab/tree/main/packer

Partager l'article