Aller au contenu
English
English
Infrastructure as Code medium

Construire un EE Ansible avec ansible-builder v3 : execution-environment.yml et multi-stage

95 min de lecture

Logo Ansible

Construire son propre EE est l'étape qui sépare un consommateur d'un opérateur Ansible 2026. Avec ansible-builder v3.1+ et un fichier execution-environment.yml au schéma v3, on assemble une image OCI custom : ansible-core pinné, collections Galaxy pinnées, dépendances Python pinnées, packages système pinnés. Le résultat est une image bit-pour-bit reproductible, pushée sur registre privé, signée, consommée par AAP, ansible-navigator, ou la CI/CD.

À la fin, vous saurez écrire un execution-environment.yml v3 propre, builder via Podman avec ansible-builder build, inspecter le Containerfile multi-stage généré, et tester l'EE construit.

  • Schéma version: 3 : sections images, dependencies, additional_build_files, additional_build_steps.
  • Pinning rigoureux : ansible-core, collections, Python, system.
  • ansible-builder build avec Podman.
  • Multi-stage Containerfile généré (4 stages : base, galaxy, builder, final).
  • Smoke tests post-build pour valider l'image.
  • Pièges courants v3.
mon-ee/
├── execution-environment.yml ← recette principale
├── requirements.yml ← collections Galaxy
├── requirements.txt ← deps Python (pip)
├── bindep.txt ← deps système (dnf/apt)
├── configs/
│ └── ansible.cfg ← (optionnel) config injectée
└── context/ ← (généré par ansible-builder)
├── Containerfile
└── _build/
├── requirements.yml
├── requirements.txt
└── bindep.txt

Quatre fichiers d'entrée que vous écrivez, plus le context/ généré par ansible-builder. Versionnez les 4 fichiers + ignorez context/ (généré à chaque build).

---
version: 3 # OBLIGATOIRE en 2026
images:
base_image:
name: ghcr.io/ansible-community/community-ee-minimal:latest
dependencies:
ansible_core:
package_pip: ansible-core==2.18.1
ansible_runner:
package_pip: ansible-runner==2.4.1
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
python_interpreter:
package_system: python3.11
python_path: /usr/bin/python3.11
additional_build_files:
- src: configs/ansible.cfg
dest: configs
additional_build_steps:
prepend_base:
- RUN echo "Build $(date)" > /etc/ee-build-info
append_final:
- COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg
- RUN chmod 0644 /etc/ansible/ansible.cfg

L'image point de départ. Choix typiques :

BaseCas d'usage
ghcr.io/ansible-community/community-ee-minimal:latestEE custom léger (~400 MB de base)
ghcr.io/ansible-community/community-ee-base:latestEE custom avec quelques collections de base
registry.redhat.io/.../ee-minimal-rhel9Production AAP entreprise

Bonne pratique : pinner par digest SHA-256 plutôt que par tag mutable :

base_image:
name: ghcr.io/ansible-community/community-ee-minimal:2.21.3-1@sha256:4359b29750dfc67b55e73bfc5a4ffa804c882c1376a33e59217c1087632241e6

Empêche un repush silencieux upstream de modifier votre base. La forme canonique porte le tag et le digest : le tag reste lisible par un humain, le digest est ce qui est réellement tiré. Relevez-le une fois, avec skopeo inspect --format '{{.Digest}}' docker://<image>:<tag>, puis rafraîchissez-le à chaque montée de version : un digest ne reçoit aucun correctif de sécurité, c'est la contrepartie assumée de l'épinglage.

ansible_core:
package_pip: ansible-core==2.18.1
ansible_runner:
package_pip: ansible-runner==2.4.1

Pinning strict avec ==. Sans cette section ou sans pin, vous récupérez la dernière version au moment du build → reproductibilité cassée.

Pointe sur requirements.yml :

requirements.yml
collections:
- name: ansible.posix
version: 2.0.0
- name: community.general
version: 10.5.0
- name: kubernetes.core
version: 5.1.1

Pinner toutes les versions. Une collection sans version → dernière au build, drift garanti à terme.

Pointe sur requirements.txt :

requirements.txt
kubernetes==31.0.0
PyYAML==6.0.2
jsonpatch==1.33

Format pip standard. Toujours ==, jamais >=.

Pointe sur bindep.txt :

bindep.txt
git [platform:rpm]
openssh-clients [platform:rpm]
sshpass [platform:rpm]

Format bindep : nom du paquet + profil. [platform:rpm] pour les bases UBI/RHEL/AlmaLinux ; [platform:dpkg] pour Ubuntu base.

Fichiers supplémentaires à copier dans le contexte de build :

additional_build_files:
- src: configs/ansible.cfg
dest: configs

src est relatif au répertoire du execution-environment.yml, dest est relatif au _build/ dans le context/. Utilisé pour des configs, certificats CA, scripts.

Lignes Containerfile injectées à des moments précis :

HookQuand
prepend_baseTout début du stage base
append_baseFin du stage base
prepend_galaxyAvant ansible-galaxy collection install
append_galaxyAprès l'install Galaxy
prepend_finalDébut du stage final
append_finalFin du stage final (juste avant le tag)

Cas d'usage : injecter une config, un certificat CA d'entreprise, un script d'initialisation, un label OCI.

Fenêtre de terminal
ansible-builder build \
--tag localhost/my-ee:1.0.0 \
--container-runtime podman \
--file execution-environment.yml \
--context ./context \
--verbosity 2

Options clés :

  • --tag : nom + tag de l'image résultat. Toujours semver (:1.0.0), jamais :latest en CI.
  • --container-runtime : podman (recommandé 2026) ou docker.
  • --file : fichier de définition (default : execution-environment.yml).
  • --context : où ansible-builder écrit le Containerfile généré.
  • --verbosity : 0 à 3. 2 recommandé en CI pour capter les warnings.
  • --no-cache : forcer rebuild complet (debug, ou changement de requirements caché).
# context/Containerfile (extrait simplifié, écrit par ansible-builder)
ARG EE_BASE_IMAGE="ghcr.io/ansible-community/community-ee-minimal:latest"
# ... (les autres ARG et options posés par ansible-builder sont omis ici)
FROM $EE_BASE_IMAGE AS base
RUN echo "Build $(date)" > /etc/ee-build-info
COPY _build/bindep.txt /tmp/src/bindep.txt
RUN microdnf install -y --nodocs \
$(grep '\[platform:rpm\]' /tmp/src/bindep.txt | awk '{print $1}')
FROM base AS galaxy
COPY _build/requirements.yml /tmp/requirements.yml
RUN ansible-galaxy collection install -r /tmp/requirements.yml \
--collections-path "/usr/share/ansible/collections"
FROM base AS builder
COPY _build/requirements.txt /tmp/src/requirements.txt
RUN pip install --no-cache-dir -r /tmp/src/requirements.txt
FROM base AS final
COPY --from=galaxy /usr/share/ansible/collections /usr/share/ansible/collections
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg
RUN chmod 0644 /etc/ansible/ansible.cfg

4 stages : base, galaxy, builder, final. Le stage final ne contient que ce qui sert au runtime (pas les compilers Python, par exemple, qui ont servi à compiler les wheels).

Toujours valider l'image après le build :

Fenêtre de terminal
# 1. ansible-core présent ?
podman run --rm localhost/my-ee:1.0.0 ansible --version | head -1
# Attendu : ansible [core 2.18.1]
# 2. Collections présentes ?
podman run --rm localhost/my-ee:1.0.0 ansible-galaxy collection list
# 3. Module spécifique disponible ?
podman run --rm localhost/my-ee:1.0.0 ansible-doc kubernetes.core.k8s | head -3
# 4. Python version ?
podman run --rm localhost/my-ee:1.0.0 python3 --version

Critique : sans smoke tests, un EE peut « builder sans erreur » et pourtant ne pas contenir ansible-core, cas du version: 3 manquant détaillé dans Déboguer un EE cassé. Le smoke test attrape ce cas immédiatement.

  • version: 3 oublié → ansible-builder lit en mode v1, ignore les sections de deps modernes.
  • Collection inexistante dans requirements.yml → ansible-galaxy install échoue mais erreur peut être noyée si verbosity basse.
  • Version PyPI inexistante → pip install kubernetes==9999 échoue, message No matching distribution.
  • Profil [platform:rpm] manquant dans bindep.txt → tous les paquets ignorés sur base UBI.
  • Cache Podman : modifier requirements.yml ne déclenche pas toujours rebuild de la couche galaxy → --no-cache ou bumper une variable dans prepend_galaxy.

Un execution-environment.yml syntaxiquement correct ne garantit pas un environnement utilisable : seul le contrôle de l'artefact construit le dit. Le lab vous fait écrire les quatre fichiers d'entrée en schéma v3, épingler ansible-core ainsi que chaque collection et chaque dépendance PyPI, builder avec Podman, puis publier le résultat sur un registre. Les tests portent précisément sur l'épinglage : une version flottante restée quelque part fait échouer la validation.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • version: 3 obligatoire dans execution-environment.yml.
  • Pinner tout : ansible-core, collections, Python deps, system deps.
  • 4 fichiers d'entrée : execution-environment.yml, requirements.yml, requirements.txt, bindep.txt.
  • Multi-stage Containerfile généré : base, galaxy, builder, final.
  • Smoke tests post-build : podman run --rm $TAG ansible --version.
  • Tag semver : :1.0.0, jamais :latest en CI.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn