Aller au contenu

Automatisation de la construction des images Incus

Mise à jour :

logo incus

J’ai besoin d’industrialiser la construction d’images Incus pour garantir que mes images soient prêtes et cohérentes. Pour cela, j’utilise habituellement Packer, un outil puissant qui permet de créer des images machine de manière automatisée. Grâce à l’existence du builder Incus, je peux facilement créer des images de conteneurs incus optimisées pour mon environnement de développement de rôles Ansible. L’objectif est d’automatiser complètement ce processus pour éviter les erreurs humaines et améliorer l’efficacité des déploiements.

Création d’une image personnalisée avec Packer

Dans cette section, je vais vous guider à travers le processus de création d’une image personnalisée avec Packer en utilisant le builder Incus. Ce builder permet de construire des images de conteneurs compatibles avec l’environnement Incus tout en offrant les avantages d’automatisation et de reproductibilité que Packer fournit.

Installation des prérequis

Avant de commencer, il est nécessaire d’installer Packer. Il est disponible sur le site officiel et peut être installé via des gestionnaires de paquets comme apt sur Linux, brew sur macOS, ou via un binaire sur Windows. Plus d’infos ici

Ensuite, assurez-vous que Incus est correctement installé et configuré dans ton environnement. Le builder Packer pour Incus nécessite que Incus soit accessible pour générer les images de conteneurs. Plus d’infos ici/docs/conteneurs/moteurs-conteneurs/incus/

Création du fichier de configuration Packer

La création d’une image avec Packer repose sur un template (modèle) au format JSON ou HCL qui définit comment l’image sera construite. Voici un exemple de fichier HCL simple pour une image Incus :

ubuntu-incus.pkr.hcl
packer {
required_plugins {
incus = {
version = ">= 1.0.0"
source = "github.com/bketelsen/incus"
}
}
}
source "incus" "noble" {
image = "images:ubuntu/noble"
output_image = "ubuntu-nobble-ansible"
reuse = true
}
build {
sources = ["incus.noble"]
provisioner "shell" {
scripts = [
"scripts/ubuntu-noble/init.sh",
]
}
}

Dans cet exemple, le builder Incus utilise une image de base Ubuntu 24.04 pour créer une nouvelle image. Le bloc provisioner exécute des commandes pour configuer l’image pour être utilisé avec Ansible.

Le script bash :

scripts/ubuntu-noble/init.sh
apt update
apt install -y openssh-server python3 sudo
useradd -m -s /bin/bash ansible
mkdir -p /home/ansible/.ssh
echo 'ssh-ed25519 xxxxxxxxxxxx' > /home/ansible/.ssh/authorized_keys
chown -R ansible:ansible /home/ansible/.ssh
chmod 600 /home/ansible/.ssh/authorized_keys
echo 'ansible ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/ansible
PASSWD=$(date | md5sum | cut -c1-8)
echo "ansible:$PASSWD" | chpasswd

Exécution du build avec Packer

Pour lancer la création de l’image, il suffit d’exécuter la commande suivante dans le terminal, à partir du répertoire où se trouve le fichier de configuration HCL :

Terminal window
packer build ubuntu-incus.pkr.hcl

Packer va alors lire le template, utiliser le builder incus pour télécharger l’image de base définie, exécuter le script de provisionnement pour configurer l’image, puis la rendre disponible localement.

Vérification de l’image générée

Une fois l’image créée, tu peux la retrouver dans Incus en vérifiant la liste des images disponibles. Tu peux utiliser la commande suivante pour lister les images dans Incus :

Terminal window
incus image list
+-----------------------+--------------+--------+----------------------------------------+--------------+-----------+-----------+-----------------------+
| ubuntu-nobble-ansible | c6602a03500c | no | Ubuntu noble amd64 (20241015_07:42) | x86_64 | CONTAINER | 203.71MiB | 2024/10/16 21:06 CEST |
+-----------------------+--------------+--------+----------------------------------------+--------------+-----------+-----------+-----------------------+

Options supplémentaires du builder Incus

En plus des options vu ci-dessus, e builder Incusoffre plusieurs paramètres optionnels pour ajuster la création d’images.

  1. reuse : Permet de réutiliser un alias d’image existant.
  2. publish_remote_name : Publie l’image sur un remote spécifique d’Incus.
  3. init_sleep : Définit le délai (en secondes) entre le lancement et le provisionnement.
  4. virtual_machine : Crée une image de machine virtuelle plutôt qu’une image de conteneur.

Ces options apportent plus de flexibilité pour des scénarios avancés. Plus d’infos ici.

Je rencontre des difficultés à utiliser le provisioner Ansible de Packer en conjonction avec la connexion Incus. Bien que Packer semble bien créer et démarrer les conteneurs, le provisionnement avec Ansible ne fonctionne pas comme prévu. Il semble y avoir un problème de compatibilité entre le plugin Incus et la connexion Ansible attendue par Packer. Cela m’empêche d’automatiser correctement la configuration des images via Ansible, ce qui me pousse à vous proposer une solution alternative.

Création d’une image avec Ansible

On peut utiliser Ansible avec le plugin de connexion Incus pour automatiser la création et la configuration d’images directement sur des conteneurs, sans passer par Packer. Ce plugin permet à Ansible de traiter les conteneurs Incus comme des machines distantes, facilitant ainsi l’exécution des rôles et playbooks.

Avant, vous devez installer la collection community.general :

Terminal window
ansible-galaxy collection install community.general

Voici le playbook faisant les mêmes opérations que celles avec Packer.

---
- name: Create instance
hosts: localhost
connection: local
tasks:
- name: Create instance
ansible.builtin.shell:
cmd: incus launch images:debian/12 debian-ansible
creates: debian-ansible.ok
- name: Create instance
ansible.builtin.shell:
cmd: incus start debian-ansible
- name: Create flag
ansible.builtin.file:
path: debian-ansible.ok
state: touch
- name: Run command in container
hosts: debian-ansible
connection: community.general.incus
gather_facts: false
vars:
pwd: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters') }}"
tasks:
- name: Install packages
ansible.builtin.raw: apt install -y python3
args:
executable: /usr/bin/bash
- name: Create user
ansible.builtin.user:
name: ansible
shell: /usr/bin/bash
password: "{{ pwd }}"
state: present
create_home: true
home: /home/ansible
- name: Copy SSH Key
ansible.posix.authorized_key:
user: ansible
state: present
key: "{{ lookup('file', item) }}"
with_fileglob:
- ~/.ssh/id_ed25519.pub
- name: Add user to sudoers
community.general.sudoers:
user: ansible
name: ansible
nopassword: true
commands: ALL
state: present
- name: Create instance
hosts: localhost
connection: local
tasks:
- name: Stop instance
ansible.builtin.shell:
cmd: incus stop debian-ansible
removes: debian-ansible.ok
- name: Create Image
ansible.builtin.shell:
cmd: incus publish --alias debian_ansible debian-ansible --reuse
removes: debian-ansible.ok
- name: Delete flag
ansible.builtin.file:
path: debian-ansible.ok
state: absent

Ce code Ansible est divisé en plusieurs étapes :

  1. Création d’une instance : Le code lance une instance Debian 12 avec Incus et crée un fichier de flag pour marquer la création réussie de l’instance. L’instance est ensuite démarrée.

  2. Exécution de commandes dans le conteneur : Ansible se connecte à l’instance via Incus et exécute plusieurs tâches : installer Python3, créer un utilisateur ansible avec un mot de passe aléatoire, copier une clé SSH et ajouter l’utilisateur aux sudoers.

  3. Création de l’image : Une fois les configurations effectuées, l’instance est arrêtée et publiée en tant qu’image nommée debian_ansible dans Incus. Le fichier de flag est supprimé.

La commande reste habituelle car j’ai mis les paramètres dans le playbook :

Terminal window
ansible-playbook incus.yml -i debian-ansible,

Il faudrait juste encore trouver le code d’un inventaire dynamique.

Plus loin

Je suis en train de découvrir Incus et j’explore comment stocker les images créées localement dans une registry. Mon objectif est de trouver une manière simple et efficace de gérer ces images au sein de mon infrastructure. Une fois que j’aurai maîtrisé cette partie, je vais intégrer ce processus dans un pipeline CI/CD. Cela permettra d’automatiser complètement la création, le test et le déploiement des images, assurant ainsi un workflow de déploiement fluide et reproductible. Je vais aussi m’assurer que chaque nouvelle image est immédiatement disponible dans la registry pour les environnements de production.