
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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Schéma
version: 3: sectionsimages,dependencies,additional_build_files,additional_build_steps. - Pinning rigoureux : ansible-core, collections, Python, system.
ansible-builder buildavec 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.
Prérequis
Section intitulée « Prérequis »- Avoir lu Modes interactifs et Inspecter un EE.
ansible-builder ≥ 3.1.0(pipx install ansible-builder).- Podman installé.
Anatomie d’un projet EE
Section intitulée « Anatomie d’un projet EE »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.txtQuatre 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).
Le fichier execution-environment.yml v3
Section intitulée « Le fichier execution-environment.yml v3 »---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.cfgimages.base_image.name
Section intitulée « images.base_image.name »L’image point de départ. Choix typiques :
| Base | Cas d’usage |
|---|---|
quay.io/ansible/community-ee-minimal:latest | EE custom léger (~400 MB de base) |
quay.io/ansible/community-ee-base:latest | EE custom avec quelques collections de base |
registry.redhat.io/.../ee-minimal-rhel9 | Production 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.
dependencies.ansible_core et ansible_runner
Section intitulée « dependencies.ansible_core et ansible_runner »ansible_core: package_pip: ansible-core==2.18.1ansible_runner: package_pip: ansible-runner==2.4.1Pinning strict avec ==. Sans cette section ou sans pin, vous récupérez la dernière version au moment du build → reproductibilité cassée.
dependencies.galaxy
Section intitulée « dependencies.galaxy »Pointe sur requirements.yml :
collections: - name: ansible.posix version: 2.0.0 - name: community.general version: 10.5.0 - name: kubernetes.core version: 5.1.1Pinner toutes les versions. Une collection sans version → dernière au build, drift garanti à terme.
dependencies.python
Section intitulée « dependencies.python »Pointe sur requirements.txt :
kubernetes==31.0.0PyYAML==6.0.2jsonpatch==1.33Format pip standard. Toujours ==, jamais >=.
dependencies.system
Section intitulée « dependencies.system »Pointe sur 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.
additional_build_files
Section intitulée « additional_build_files »Fichiers supplémentaires à copier dans le contexte de build :
additional_build_files: - src: configs/ansible.cfg dest: configssrc est relatif au répertoire du execution-environment.yml, dest est relatif au _build/ dans le context/. Utilisé pour des configs, certificats CA, scripts.
additional_build_steps
Section intitulée « additional_build_steps »Lignes Containerfile injectées à des moments précis :
| Hook | Quand |
|---|---|
prepend_base | Tout début du stage base |
append_base | Fin du stage base |
prepend_galaxy | Avant ansible-galaxy collection install |
append_galaxy | Après l’install Galaxy |
prepend_final | Début du stage final |
append_final | Fin 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.
Le build avec ansible-builder
Section intitulée « Le build avec ansible-builder »ansible-builder build \ --tag localhost/my-ee:1.0.0 \ --container-runtime podman \ --file execution-environment.yml \ --context ./context \ --verbosity 2Options clés :
--tag: nom + tag de l’image résultat. Toujours semver (:1.0.0), jamais:latesten CI.--container-runtime:podman(recommandé 2026) oudocker.--file: fichier de définition (default :execution-environment.yml).--context: où ansible-builder écrit leContainerfilegénéré.--verbosity:0à3.2recommandé en CI pour capter les warnings.--no-cache: forcer rebuild complet (debug, ou changement de requirements caché).
Le Containerfile multi-stage généré
Section intitulée « Le Containerfile multi-stage généré »# context/Containerfile (extrait simplifié)ARG EE_BASE_IMAGE="quay.io/ansible/community-ee-minimal:latest"
FROM $EE_BASE_IMAGE AS baseRUN echo "Build $(date)" > /etc/ee-build-infoCOPY _build/bindep.txt /tmp/src/bindep.txtRUN microdnf install -y --nodocs \ $(grep '\[platform:rpm\]' /tmp/src/bindep.txt | awk '{print $1}')
FROM base AS galaxyCOPY _build/requirements.yml /tmp/requirements.ymlRUN ansible-galaxy collection install -r /tmp/requirements.yml \ --collections-path "/usr/share/ansible/collections"
FROM base AS builderCOPY _build/requirements.txt /tmp/src/requirements.txtRUN pip install --no-cache-dir -r /tmp/src/requirements.txt
FROM base AS finalCOPY --from=galaxy /usr/share/ansible/collections /usr/share/ansible/collectionsCOPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packagesCOPY _build/configs/ansible.cfg /etc/ansible/ansible.cfgRUN chmod 0644 /etc/ansible/ansible.cfg4 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).
Smoke tests post-build
Section intitulée « Smoke tests post-build »Toujours valider l’image après le build :
# 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.
Pièges courants v3
Section intitulée « Pièges courants v3 »version: 3oublié → 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, messageNo matching distribution. - Profil
[platform:rpm]manquant dansbindep.txt→ tous les paquets ignorés sur base UBI. - Cache Podman : modifier
requirements.ymlne déclenche pas toujours rebuild de la couche galaxy →--no-cacheou bumper une variable dansprepend_galaxy.
Lab pratique
Section intitulée « Lab pratique »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).
À retenir
Section intitulée « À retenir »version: 3obligatoire dansexecution-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:latesten CI.