1. Ensuring code quality #
Linters and tests.
yamllint and ansible-lint #
pip install ansible-lint yamllint molecule molecule-plugins[docker]
yamllint .
ansible-lint # analyse tout le projet
ansible-lint --profile production site.yml
ansible-lint --fix # corrige automatiquement ce qui peut l'être
# .ansible-lint
profile: production
skip_list:
- yaml[line-length]ansible-lint flags, for example, modules without an FQCN (apt instead of ansible.builtin.apt), tasks without a name, using shell instead of a module, or missing file permissions.
Testing a role with Molecule #
cd roles/nginx
molecule init scenario default --driver-name docker
# molecule/default/molecule.yml
driver: { name: docker }
platforms:
- name: instance
image: geerlingguy/docker-debian12-ansible:latest
pre_build_image: true
provisioner: { name: ansible }
verifier: { name: ansible }
molecule test # create -> converge -> idempotence -> verify -> destroy
molecule converge # appliquer seulement
molecule login # se connecter dans le conteneur pour déboguer# molecule/default/verify.yml
- name: Vérifier
hosts: all
tasks:
- name: nginx répond
ansible.builtin.uri: { url: http://localhost, status_code: 200 }
- name: Le service est activé
ansible.builtin.service_facts:
- ansible.builtin.assert:
that: ansible_facts.services['nginx.service'].state == 'running'Good to know: Molecule runs the role twice and fails if the second pass contains changes: this is the idempotence test.
2. Continuous integration and delivery #
A complete pipeline.
GitLab CI example #
# .gitlab-ci.yml
stages: [lint, test, deploy-preprod, deploy-prod]
lint:
stage: lint
image: python:3.12
script:
- pip install ansible ansible-lint yamllint
- yamllint . && ansible-lint
molecule:
stage: test
image: docker:27
services: [docker:27-dind]
script:
- pip install molecule molecule-plugins[docker] ansible
- cd roles/nginx && molecule test
deploy-preprod:
stage: deploy-preprod
script:
- ansible-galaxy install -r requirements.yml
- ansible-playbook site.yml -i inventories/preprod --check --diff
- ansible-playbook site.yml -i inventories/preprod
deploy-prod:
stage: deploy-prod
when: manual
only: [main]
script:
- ansible-playbook site.yml -i inventories/prod --vault-password-file <(echo "$VAULT_PASSWORD")3. Reproducibility: collections and execution environments #
Pinning dependencies.
Collections and requirements #
ansible-galaxy collection list
ansible-galaxy collection install -r requirements.yml -p ./collections
ansible-galaxy collection build # packager sa propre collection
ansible-galaxy collection init mon_ns.ma_collectionExecution environments #
An Execution Environment (EE) is a container image containing Ansible, its collections and the Python/system dependencies. The same EE runs on the developer's workstation and in AWX: no more « it works on my machine ».
pip install ansible-builder ansible-navigator
cat > execution-environment.yml <<'EOF'
version: 3
images:
base_image:
name: quay.io/fedora/fedora:40
dependencies:
ansible_core: { package_pip: ansible-core }
ansible_runner: { package_pip: ansible-runner }
galaxy: requirements.yml
python: requirements.txt
EOF
ansible-builder build -t registry.lab/ee-infra:1.0
ansible-navigator run site.yml -i inventories/lab --eei registry.lab/ee-infra:1.0 --mode stdout4. Performance #
Speeding up thousands of hosts.
ansible.cfg settings #
[defaults]
forks = 50
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
callbacks_enabled = profile_tasks, timer
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=120s
retries = 3ansible-playbook site.yml # avec profile_tasks, affiche les tâches les plus lentes
# TASKS RECAP *****************************
# Installer les paquets ................. 41.20s
# Déployer le template .................. 3.12sExecution strategies #
- hosts: all
strategy: free # chaque hôte avance à son rythme (sans attendre les plus lents)
gather_facts: false
tasks:
- ansible.builtin.package: { name: htop, state: present }
async: 300 # lancer en tâche de fond
poll: 0Common mistake: strategy: free is not suited to ordered deployments: a task may run on some hosts before it has finished elsewhere.Good practice: Measure before optimizing: enable profile_tasks and tackle the 3 longest tasks (often fact gathering and package installations).