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 tracking. 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