Aller au contenu principal

Buil d'images KVM avec Packer

· 5 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Encore un outil DevOps de chez Hashicorp, cette fois, c'est Packer qui facilite la création d’images de système d’exploitations. Il vient en amont des autres produit Hashicorp que sont Vagrant et Terraform.

À partir d’un fichier de configuration, Packer est capable de construire en parallèle des images pour différents environnements que ce soit cloud ou on-premises.

Installation de packer

Packer est disponible comme tous les outils d'Hashicorp pour toutes les plateformes : Macos, Windows, Linux, FreeBSD, OpenBSD et même Solaris.

Personnellement je l'utilise dans ma vm de développement tournant sur une ubuntu qui intègre presque tous les prérequis. Il faudra que je complète avec les dernières trouvailles.

wget https://releases.hashicorp.com/packer/1.6.6/packer_1.6.6_linux_amd64.zip
unzip packer_1.6.6_linux_amd64.zip
sudo install packer /usr/local/bin

Fonctionnement général

Packer va tout simplement instancié une VM en utilisant la couche de virtualisation de votre choix. Moi, c'est avec libvirt. Il va monter l'image iso et lancer l'installation via un fichier kickstart. Le plus long est d'arriver à avoir une config kickstart générale permettant d'arriver à ses objectifs.

Le fichier kickstart se trouve dans le répertoire http.

En post-traitement on peut lancer des scripts pour faire le ménage par exemple. Mais vous pouvez faire tout ce que vous voulez.

Construction d'une image Oracle Linux 8 compatible cloud-init

Le code source de cette image est disponible sur mon compte gitlab.

Packer peut utiliser deux types de fichiers json ou hcl2. On retrouve plus de ressources avec du fichier json donc je prends ce format.

On retrouve trois sections :

  1. builders qui a la tâche de créer la machine virtuelle avec l'hyperviseur de son choix;
  2. provisioners qui permet de configurer la machine ;
  3. post-processors finalise le travail en stockant et en distribuant les images à destination de vagrant par exemple. Ici j'utilise des scripts shell, mais on pourrait aussi bien utiliser Ansible.

Si vous regardez l'exemple ci-dessous. On a un builder, un provisionner et pas de post-processor. En effet, mes images sont directement utilisées dans un cluster kvm.

Attention chaque builder possède ses propres paramètres. Vous pouvez consulter la liste ici. Dans mon exemple j'utilise qemu pour créer des images KVM.

On peut utiliser des variables soient prédéfinies soient que vous déclarez vous-même. Les variables sont entre doubles crochets {{}}.

À la fin du fichier se trouve la partie de déclaration de ces variables (on pourrait le mettre tout au début). On retrouve ensuite ses variables entre crochet commençant par {{ user kickstart.cfg }}

{
  "builders": [

    {
      "boot_command": [
        "<tab> {{ user `boot_command_prefix` }}=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ol/{{ user `kickstart_cfg` }} <enter><wait>"
      ],
      "disk_size": "{{ user `disk_size` }}",
      "headless": true,
      "http_directory": "http",
      "iso_checksum": "{{ user `iso_checksum_type` }}:{{ user `iso_checksum` }}",
      "iso_url": "{{ user `iso_url` }}",
      "output_directory": "output-{{ user `vm_name` }}-{{ build_type }}",
      "shutdown_command": "echo '/sbin/halt -h -p' > shutdown.sh; echo 'packer'|sudo -S bash 'shutdown.sh'",
      "ssh_password": "password",
      "ssh_timeout": "60m",
      "ssh_username": "root",
      "disk_compression": true,
      "vm_name": "{{ user `vm_name` }}-{{ timestamp }}.qcow2",
      "type": "qemu",
      "format": "qcow2",
      "accelerator": "kvm",
      "qemu_binary": "/usr/bin/qemu-system-x86_64",
      "net_device": "virtio-net",
      "disk_interface": "virtio",
      "qemuargs": [
        [
          "-chardev",
          "file,id=pts,path=serial.log"
        ],
        [
          "-device",
          "isa-serial,chardev=pts"
        ],
        [
          "-m",
          "2G"
        ],
        [
          "-smp",
          "cpus=2"
        ]
      ]
    }
  ],
  "provisioners": [
    {
      "scripts": [
        "scripts/base.sh",
        "scripts/cleanup.sh",
        "scripts/zerodisk.sh"
      ],
      "type": "shell"
    }
  ],
  "variables": {
    "disk_adapter_type": "sata",
    "disk_size": "5000",
    "boot_command_prefix": "inst.text inst.ks",
    "iso_checksum": "771e383adfd5406d1a360ff24822011a88cbb45b6019e2f1095758c46a406aaa",
    "iso_checksum_type": "sha256",
    "iso_url": "https://yum.oracle.com/ISOS/OracleLinux/OL8/u3/x86_64/OracleLinux-R8-U3-x86_64-dvd.iso",
    "kickstart_cfg": "ks.8.cfg",
    "vm_name": "ol8-server",
    "auto_build": "True",
    "description": "Oracle Linux 8",
    "short_description": "Oracle Linux 8"
  }
}

Description du fichier kickstart :

  • un clavier et lang en fr;
  • un timezone défini sur Paris en UTC;
  • les services ssh et network démarrés ;
  • le mot de pass root est password (à vous de le changer)
  • selinux est désactivé ainsi que firewalld
 Required settings
eula --agreed
auth --passalgo=sha512 --useshadow
lang fr_FR.UTF-8
keyboard --vckeymap=fr --xlayouts='fr'
timezone Europe/Paris --isUtc
rootpw password
authconfig --enableshadow --enablemd5
timezone UTC

 Optional settings
services --enabled=NetworkManager,sshd
autopart --type=lvm
bootloader --location=mbr
cdrom
clearpart --all --initlabel
firewall --disabled
firstboot --disabled
install
network --bootproto=dhcp --device=ens33 --activate --hostname=ol8.robert.local
reboot
selinux --disabled
skipx
text
zerombr

 Packages
%packages --ignoremissing --excludedocs
@Base
@Core
@Development Tools
openssh-clients
sudo
openssl-devel
readline-devel
zlib-devel
kernel-headers
kernel-devel
net-tools
vim
wget
curl
rsync
cloud-init
cloud-utils-growpart
epel-release
 unnecessary firmware
-aic94xx-firmware
-alsa-firmware
-alsa-tools-firmware
-atmel-firmware
-b43-openfwwf
-bfa-firmware
-ivtv-firmware
-ipw2100-firmware
-ipw2200-firmware
-iwl100-firmware
-iwl105-firmware
-iwl135-firmware
-iwl1000-firmware
-iwl2000-firmware
-iwl2030-firmware
-iwl3160-firmware
-iwl3945-firmware
-iwl4965-firmware
-iwl5000-firmware
-iwl5150-firmware
-iwl6000-firmware
-iwl6000g2a-firmware
-iwl6000g2b-firmware
-iwl6050-firmware
-iwl7260-firmware
-iwl7265-firmware
-libertas-usb8388-firmware
-ql2100-firmware
-ql2200-firmware
-ql23xx-firmware
-ql2400-firmware
-ql2500-firmware
-rt61pci-firmware
-rt73usb-firmware
-xorg-x11-drv-ati-firmware
-zd1211-firmware
%end

 Post
%post
/usr/bin/yum -y update
%end

Construction de l'image

Avant de lancer le build on peut contrôler notre fichier de configuration :

packer validate ol-server.json

On lance la construction :

packer build ol-server.json

Si vous rencontrez des difficultés il suffit d'ajouter devant PACKER_LOG=1

PACKER_LOG=1 packer build ol-server.json

Test de l'image

Maintenant on peut contrôler que notre image est fonctionnelle (remplacer avec votre fichier qcow2) :

/usr/bin/qemu-system-x86_64 -machine type=pc,accel=kvm -device \
 virtio-net,netdev=user.0 -m 512M -boot once=d -name packer-jessie-qemu \
 -netdev user,id=user.0,hostfwd=tcp::3213-:22 \
-drive file=ol8-server-1612774561.qcow2,if=virtio,cache=writeback,discard=ignore

Vous pouvez vous connecter dessus avec une simple commande ssh:

ssh -p 3213 localhost

Réduire la taille de vos images

Dans le cas où vous souhaiteriez réduire la taille de vos images, vous pouvez utiliser virt-sparsify

Vérifions que tout est installé et correctement paramétré :

sudo apt install libguestfs-tools
sudo dpkg-statoverride --update --add root root 0644 /boot/vmlinuz-`uname -r`

Pour le premier lancement, contrôlez que vous n'avez pas d'erreur (remplacer avec votre fichier qcow2) :

LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1 virt-sparsify --compress output-ol8-server-qemu/ol8-server-xxxxxxx.qcow2 ol8-server-xxxxxxx.qcow2

Aller plus loin

Je vous ai mis dans le projet gitlab des exemples de fichiers kickstart des principales distributions Linux.

La suite dans les prochains jours où nous verrons comment utiliser cette image sur un cluster kvm distant avec mise à jour du DNS PowerDNS.