Aller au contenu
Infrastructure as Code medium

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

11 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: quay.io/ansible/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
quay.io/ansible/community-ee-minimal:latestEE custom léger (~400 MB de base)
quay.io/ansible/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: quay.io/ansible/community-ee-minimal@sha256:abc123def456...

Empêche un repush silencieux upstream de modifier votre base.

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é)
ARG EE_BASE_IMAGE="quay.io/ansible/community-ee-minimal:latest"
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 “build OK” et pourtant ne pas contenir ansible-core (cas du version: 3 manquant — voir lab ee/debug). 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.ymlansible-galaxy install échoue mais erreur peut être noyée si verbosity basse.
  • Version PyPI inexistantepip 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.

Le lab ee/builder-custom (labs/ee/builder-custom/) fournit un projet EE complet (4 fichiers d’entrée + script de build + 9 tests pytest validant le pinning).

Le lab ee/debug (labs/ee/debug/) propose 3 bugs volontaires à diagnostiquer (version: 3 oublié, collection inexistante, version Python invalide).

  • 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 +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn