Ansible

Automation Ansible - Intro

Ansible est devenu un outil incontournable de gestion d’infrastructure pour des cibles Linux mais aussi pour des cibles du réseau et du centre de données, du nuage. Il est capable de représenter la gestion de la configuration d’une infrastructure sous forme de code, aisé à lire et à gérer. Ansible transforme l’esprit de l’admin traditionnel en dev.

Ce document est un guide de formation sur l’usage d’Ansible.

Prérequis

Pour aborder cette matière quelques pré-requis sont nécessaires :

Portée du document

Ce document a pour premier objectif de répondre aux sujets du programme de l’examen de certification RHCE EX294 (Ingénieur certifié Red Hat) pour Red Hat Enterprise Linux 8. Ceux-ci portent sur les compétences de base en administration d’un système Linux RHEL 8 à travers l’outil de gestion Ansible, en ce y compris la gestion du pare-feu, des services de base et de Selinux.

Toutefois, si Ansible est capable de gérer une infrastructure entière qui n’est probablement pas seulement composée de systèmes Red Hat, on ne manquera pas de développer d’autres objectifs comme :

Objectifs RHCE EX294

Le document s’aligne sur le programme de l’Examen RHCE EX294 (Ingénieur certifié Red Hat) pour Red Hat Enterprise Linux 8

Présentation du produit Ansible

 68 minutes de lecture

En guise d’introduction, on décrira dans ce document le projet Ansible, ses cas d’usage, ses composants, son principe de fonctionnement, son installation et les différents binaires qui l’accompagnent.

Monter un lab Linux pour Ansible

 33 minutes de lecture

Ce chapitre envisage cinq solutions de lab pour apprendre Ansible pour dans le cadre d’une gestion de Linux. Une première solution montée avec Docker, deux autres avec Libvirt/KVM gérées en Bash ou avec Ansible lui-même et deux dernières avec Vagrant et Virtualbox ou Libvirt/KVM.

Comprendre l’inventaire Ansible

 38 minutes de lecture

Un inventaire peut être une collection d’hôtes définis dans un fichier plat, dans un dossier organisé de manière conventionnelle ou peut être nourri d’un script dynamique (qui interroge un CMDB par exemple) qui génère une liste de périphériques à cibler dans un livre de jeu. Il peut donc être statique, soit défini d’avance ; il peut aussi se créer dynamiquement ou encore être mis à jour dynamiquement. La portée d’un jeu au sein du livre de jeu est limitée aux groupes d’hôt...

Modules Ansible

 35 minutes de lecture

Ce document poursuite l’objectif d’expliquer le concept, le rôle, la manipulation et les codes de retour des modules. Les modules Ansible sont des bouts de codes écrits principalement en Python (mais tout langage supportant les retours JSON est autorisé) pour modifier l’état d’une propriété d’un hôte. Les modules sont invoqués par l’exécution de tâches soit directement dans la ligne de commande ansible ou dans des livres de jeu avec la commande ansible-playbook.

Tâches Ansible Ad-Hoc Linux

 62 minutes de lecture

Ce chapitre envisage la prise de connaissance et la mise en oeuvre de tâches Ansible Ad-Hoc pour administrer un système Linux avec Ansible. Le mode ad-hoc permet d’exécuter des tâches ad-hoc. Rappelons qu’une tâche n’est rien d’autre que l’appel à un module.

Formats JSON et YAML Ansible

 30 minutes de lecture

Ansible présente en sortie standard les résultats de ses actions en format JSON. On présentera ici le format JSON et son traitement avec l’outil jq. Ansible utilise le format YAML pour ses fichiers de données statiques pour la facilité d’usage. Enfin, les variables sont présentées dans Ansible grâce aux modèles Jinja2.

Livres de Jeu Ansible

 79 minutes de lecture

Synthèse sur les livres de jeu. Jouer avec les facts, les variables, les conditions et les boucles.

Optimiser les projets Ansible

 49 minutes de lecture

Gestion des Erreurs Ansible : Ignorer les tâches en échec, Contrôler l’état changed, Contrôler l’état failed, Tâche en échec et handlers, Module fail. Gestion des connexions dans Ansible. Abstraire un livre de jeu avec des inclusions, des imports et des rôles Ansible-Galaxy. Collection Ansible-Galaxy. Ansible-vault est un outil intégré à Ansible qui permet de chiffrer les fichiers qui contiennent des données sensibles.

Ansible Tower AWX

 13 minutes de lecture

Ansible Tower est Ansible au niveau de l’entreprise. Il s’agit d’une solution Web permettant de gérer une organisation avec une interface utilisateur très simple qui fournit un tableau de bord avec des résumés de l’état de tous les hôtes, qui permet des déploiements rapides et surveille toutes les configurations. Tower permet de partager les informations d’identification SSH sans les exposer, de consigner tous les travaux, de gérer graphiquement les inventaires et de les ...

Configuration Ansible

 47 minutes de lecture

Ce document propose d’examiner les principales options de configuration de Ansible sur le noeud de contrôle. Son architecture sans agent laisse le soin à chacun de configurer finement et personnellement le comportement par défaut de la solution. Le comportement d’Ansible peut être influencé de différentes manières : en configurant des variables d’environnement, en passant directement les paramètres sur la ligne de commande ansible ou ansible-playbook, en définissant un fic...

Glossaire Ansible

 48 minutes de lecture

Glossaire des termes utilisés dans le contexte Ansible

Présentation du produit Ansible

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous étudiez pour une certification, on répond ici à l’objectif suivant :

Présentation du produit Ansible

En guise d’introduction, on décrira dans ce document le projet Ansible, ses cas d’usage, ses composants, son principe de fonctionnement, son installation et les différents binaires qui l’accompagnent.

Après des généealités, le document décrit la terminologie et les composants Ansible. Il indique les procédures d’installation de Ansible sur une machine de contrôle Red Hat, CentOS, Fedora, Debian, Ubuntu, via PIP ou encore sous Windows WSL.

On tentera ici de comprendre de manière sommaire les composants de base d’Ansible tels que les connexions selon les cibles, les inventaires, les modules, les collections, le mode ad-hoc, les variables, les “Facts”, les jeux, les playbooks ou livres de jeux, les fichiers de configuration YAML et INI, les sorties JSON et les modèles Jinja2, les rôles, Ansible Tower, l’idempotence.

Enfin, on ne manquera de décrire l’environnement d’exécution d’Ansible en décrivant les différentes commandes ansible* disponibles nativement.

1. Projet Ansible

1.1. Présentation générale

Ansible est un outil open-source de provisionnement de logiciels, de gestion des configurations et de déploiement d’applications qui permet de faire de l’infrastructure un code. Il fonctionne sur de nombreux systèmes de type Unix, et peut configurer aussi bien des systèmes de type Unix que Microsoft Windows. Il comprend son propre langage déclaratif pour décrire la configuration du système. Ansible est sans agent, se connectant temporairement à distance via SSH ou Windows Remote Management (permettant l’exécution à distance de PowerShell) pour effectuer ses tâches.

Ansible gère les différents noeuds avec un accès à distance natif (tels que les protocoles SSH ou Remote PowerShell ou encore des APIs natives). Il ne nécessite l’installation d’aucun logiciel supplémentaire à distance. Il offre des capacités de parallélisation, collecte de métadonnées et gestion des états. Cet aspect de conception “sans agent” installé sur le périphérique est important car il réduit les besoins d’infrastructure pour démarrer une gestion. Les modules fonctionnent grâce à JSON et à la sortie standard et peuvent être écrits dans n’importe quel langage de programmation.Le système utilise notamment YAML pour exprimer des descriptions réutilisables de systèmes, il fournit des sorties en JSON, il traite les variables grâce à des modèles Jinja2.

Le logiciel Ansible a été conçu par un ancien employé Red Hat, Michael DeHaan, également auteur de l’application de serveur de “provisionning” Cobbler et co-auteur du framework Func pour l’administration à distance. Le code source du logiciel est sous licence GNU General Public v3.0. Red Hat a racheté la société Ansible, Inc. en octobre 2015. 1

1.2. Le terme Ansible

Une “ansible” est un dispositif théorique permettant de réaliser des communications à une vitesse supraluminique (supérieure à la vitesse de la lumière) imaginé en 1966 par Ursula K. Le Guin dans son roman de science-fiction, Le Monde de Rocannon. Elle en détaillera plus tard le concept dans Les Dépossédés (1974). L’idée est notamment reprise par d’autres auteurs de livres de science-fiction et des jeux vidéos, la communication étant basée sur l’état d’énergie réciproque de deux particules jumelles. Par ailleurs, Ansible est le titre d’un magazine anglo-saxon consacré à la science-fiction. Enfin, le terme “ansible” peut faire référence à un système de communication hyperspace instantané fictif 2.

1.3. Version d’Ansible

Jusqu’à la version 2.9., le code source Ansible était situé dans un seul projet ansible/ansible avec deux mises à jour principales par an.3

Pour obtenir le numéro de la dernière version stable d’Ansible :

curl -s https://pypi.org/pypi/ansible-base/json | jq -r '.releases | keys | sort'

Ansible Classique (jusqu’en version 2.9) se caractérisait par :

La version 2.10 d’Ansible va fondamentalement changer la portée des plugins inclus dans le dépôt ansible/ansible, en déplaçant une grande partie des plugins dans des dépôts de “collection” plus petits et qui seront distribués via https://galaxy.ansible.com/.

Aussi depuis la version 2.10. la version majeure est maintenue pendant un cycle de publication. Lorsque la version suivante sort (par exemple, 2.11), la version plus ancienne (2.10 dans cet exemple) n’est plus maintenue.

Concrètement, le dépôt ansible/ansible (ansible-base) ne contient plus que :

Tous ces éléments seront connus sous le nom d’ansible-base.

Les autres modules et plugins ont été déplacés dans diverses “collections”.

Ainsi pour les “collections” :

Le paquet publié d’Ansible 2.10 va intégrer ansible-base et les diverses collections communautaires qui faisaient auparavant partie d’ansible/ansible.

Le paquet ansible (contiendra un sous-ensemble de collections) et dépend du nouveau paquet ansible-base (Ansible engine).

1.4. Objectifs de conception de Ansible

Les objectifs de conception de Ansible comprennent4 :

2. Automation d’infrastructures

Ansible s’interface avec du matériel, du logiciel ou des solutions d’un grand nombre de fournisseurs du domaine des infrastructures comme :

Il répond aux besoins les Admin système / Cloud, aux Net Ops, aux admins de stockage pour une automation des serveurs, du réseau et du stockage.

2.1. Péripériques physiques

2.2. Virtualisation

2.3. Systèmes d’exploitation

2.5. Containers

2.6. Cloud

2.7. Outils DevOps

3. Cas d’usage classiques

Ansible use cases

3.1. Provisioning

3.2. Configuration Management

3.3. App Deployment

3.4. Continuous Delivery

3.5. Security & Compliance

3.6. Orchestration

4. Automation Réseau

Si vous êtes responsable d’un réseau d’entreprise, vous savez probablement que de nombreuses opérations manuelles sont effectuées via l’interface de ligne de commande (CLI). Il n’est pas surprenant que le principal défi que rencontrent des utilisateurs en matière de réseau consiste à améliorer leur agilité, et cela est resté vrai au cours des deux dernières années.5

Ansible peut gérer des périphériques :

4.1. Cas d’usage habituels

Fondamentalement, toute opération manuelle peut être automatisée avec Ansible.

4.2. Cas d’usage habituels - automatiser des tâches discrètes

4.3. Meilleures pratiques

https://docs.ansible.com/ansible/2.5/network/user_guide/network_best_practices_2.5.html

5. Automatiser et coder l’infrastructure

Nous allons progressivement apprendre que manipuler Ansible consiste à écrire du texte géré sous forme de code informatique. L’ambition est donc de “coder l’infrastructure”. Alors comment coder l’infrastructure ? D’un point de vue historique, pour un parc de machines Linux, on commencera avec du Bash, ensuite probablement du Perl, et sans doute du Python. Or le personnel d’infrastructure s’il a choisi cette voie dans les métiers de l’informatique dipose de peu d’appétence pour une pratique de “développeur” d’autant plus que l’apprentissage d’un langage de programmation classique exigera une longue courbe d’apprentissage pour cetains profils.

5.1. Automatiser l’infrastructure

Or Ansible permettra au personnel informatique d’automatiser les infrastructures. Il a l’avantage d’être :

Le seul écueil est de connaître les solutions à gérer à travers cet outil de telle sorte qu’une maîtrise d’une solution Ansible passe aussi par une bonne maîtrise de ce qui est déployé par Ansible.

Il faut alors voir Ansible comme une surcouche sans état (mis en intermédiaire qui fonctionne de manière très simple) qui intègre d’autres solutions ou s’intègre à d’autres facilement. Ansible Tower et son fork OpenSource AWX renforcent cette capacité de manière crédible en ajoutant des fonctions de gestion des utilisateurs, du code et des secrets.

5.2. Coder l’infrastructure

D’ailleurs la gestion du code source (versions, SCM, git) et des secrets (vaults) sont des sujets connexes qui s’intègre à l’usage d’Ansible.

6. Composants Ansible

Fonctionnement de Ansible

En vue de contrôler des noeuds distants, des utilisateurs lancent des “playbooks” (livres de jeu) à partir d’un noeud de contrôle grâce à Ansible Engine.

Ansible Engine utilise différents composants comme :

Ansible Engine se connecte et se pratique de manière différente sur les hôtes terminaux (Linux/Unix et Windows) des périphériques du réseau. Hôtes terminaux et périphériques du réseau sont des cibles dont les missions et la gestion sont bien différentes.

6.1. Machine de contrôle

La machine de contrôle doit être un hôte Linux / Unix (par exemple, Red Hat Enterprise Linux, Debian, CentOS, OS X, BSD, Ubuntu), et Python 2.7 ou Python 3.5+ sont requis. Avec une version Windows 10 Pro, il est possible d’utiliser Ansible avec WSL, voir Can Ansible run on Windows?

Le chapitre Installation Ansible indique de manière plus détaillée l’installation de Ansible sur la machine de contrôle.

6.2. Noeuds gérés

Les noeuds gérés, s’ils sont en Linux/Unix, doivent disposer de Python 2.6 ou version ultérieure, mais de préférence aujourd’hui le pré-requis est Python 3. SSH est alors le mode de connexion préféré.

Depuis sa version 1.7, Ansible peut également gérer les noeuds Windows. Dans ce cas, on utilise PowerShell à distance de manière native au lieu de SSH.

Le matériel d’infrastructure réseau (“constructeurs”), pare-feux, serveurs de stockage, fournisseurs IaaS, solutions de virtualisation sont supportés via SSH, NETCONF/YANG ou encore via des API HTTP REST.

On trouvera ici un propos sur les Plugins de connexion.

Fonctionnement Ansible

Source : How Network Automation is Different

6.3. Clés SSH

La machine de contrôle et les noeuds gérés devraient a priori simplement communiquer grâce au service SSH.

Les mots de passe sont pris en charge, mais la gestion des clés SSH avec ssh-agent est l’un des meilleurs moyens d’utiliser Ansible. Il est également possible d’utiliser parmi beaucoup de possibilités des authentifications Kerberos ou autres. Les connexions “root” ne sont pas obligatoires, on peut se connecter en tant qu’utilisateur, et puis utiliser “su” ou “sudo”. Le module “authorized_key” d’Ansible est un excellent moyen d’utiliser Ansible pour contrôler les accès SSH.

ssh-agent bash
ssh-add ~/.ssh/id_rsa

Notez que ssh-agent s’utilise avec des clés avec des passphrase.

On trouvera sous les liens un document de formation détaillé sur l’usage du protocole avec OpenSSH (Linux/Windows) et d’autres sur la configuration de Cisco IOS pour supporter des connexions SSH.

Ansible supporte beaucoup d’autres types de connexions/communications avec ses cîbles.

6.4. Le dossier de projet

6.5. L’inventaire

Pour démarrer un gestion Ansible, vous avez besoin d’un inventaire Ansible.

Inventory (inventaire)

Par défaut, Ansible représente les machines qu’il gère à l’aide d’un fichier INI très simple qui place toutes les machines gérées dans des groupes de votre choix. On peut le représenter dans d’autres formats comme YAML.

Pour ajouter de nouvelles machines, aucun serveur de signature supplémentaire n’est nécessaire, alors que NTP ou DNS sont critiques au moment des authentifications.

S’il existe une autre source de confiance dans votre infrastructure, Ansible peut également y ajouter dynamiquement des éléments tels que des informations d’inventaire, de groupe et variables à partir de sources telles que EC2, Rackspace, OpenStack, vSphere, etc.

CMDB

Voici à quoi ressemble un fichier d’inventaire en format INI :

[webservers]
www1.example.com
www2.example.com

[dbservers]
db0.example.com
db1.example.com

Une fois que les hôtes d’inventaire sont répertoriés, des variables “d’inventaire”, c’est-à-dire portant sur les cibles de gestion, peuvent leur être attribuées dans des fichiers texte simples (dans un sous-répertoire appelé ‘group_vars/’ ou ‘host_vars/’) ou directement dans le fichier d’inventaire.

Les Fichiers d’inventaire sont développés plus loin dans le support de formation. Le détail concernant l’inventaire ne sont pas indispensable pour commencer. Toutefois, veuillez retenir que les commandes ansibleansible-playbook et ansible-inventory, qui permettent d’exploiter Ansible, exigent toujours un inventaire défini.

6.6. Modules

Modules et Tasks

Les modules sont “les outils dans la boîte-à-outils”.

Ansible fonctionne en se connectant aux noeuds à gérer et en leur envoyant des petits programmes, appelés “modules Ansible”. Ces programmes sont écrits pour être des modèles de ressources de l’état souhaité du système. Ansible exécute ensuite ces modules (via SSH par défaut) grâce au protocole JSON sur la sortie standard et les supprime lorsque l’action est terminée.

La bibliothèque de modules peut résider sur n’importe quelle machine et sans aucun serveur central, démon ou base de données. En général, l’administrateur travaille avec son programme de terminal favori, un éditeur de texte et probablement un système de gestion de version (SCM) come Git pour suivre les modifications apportées à son contenu.

Rien n’interdit d’écrire son propre module. Ces modules peuvent contrôler les ressources comme des services, des paquets ou des fichiers (n’importe quoi en réalité), ou encore exécuter des commandes système. 1

Le document de formation prévoit une initiation à l’usage des modules en mode ad-hoc et dans les livres de jeu.

A partir de la version 2.10, les modules Ansible font partie d’espace nommé comme des collections et se référence selon l’espace nommé qui leur sont réservés.

6.7. Plugins

Les Plugins apportent des fonctionnalités complémentaires à Ansible.

Plugins

On peut connaître tout type de plugins (qui sont des sortes de “modules” spécialisés) :

6.8. Les collections

Les “collections” sont un format de distribution pour du contenu Ansible qui peut inclure des livres de jeu, des rôles, des modules et des plugins. Les “collections” sont distribuées par Ansible Galaxy via ansible-galaxy collection install <namespace.collection>. Depuis la version 2.10., les “collections” comprennent les modules tiers au projet ansible-base.2

7. Exécution des tâches

Une tâche est l’appel à un module Ansible. Le module Ansible contient localement tout le code utile à l’exécution. Il est donc important de disposer du code à jour des modules.

7.1. Le mode Ad Hoc

Le mode Ad-Hoc permet l’exécution de tâches “ad-hoc” parallèles.

Dès qu’une instance est disponible, on peut lui parler immédiatement, sans aucune configuration supplémentaire, ici sur une instance Red Hat :

ansible all -m ping
ansible foo.example.com -m yum -a "name=httpd state=installed"
ansible foo.example.com -a "/usr/sbin/reboot"

Un accès à des modules de ressources basés sur des états, ainsi qu’à des commandes “raw” (brutes) est disponible. Ces modules sont assez faciles à écrire. Par ailleurs, Ansible est livré avec énormément de modules déjà développés de telle sorte qu’un bonne partie du travail est déjà réalisée.

Le document reprend de nombreux exemples de l’usage des tâches ad-hoc.

7.2. Playbooks (Livres de jeu)

Playbooks

Les livres de jeu (playbooks) sont écrits selon un langage d’automatisation simple et puissant. Les livres de jeu peuvent orchestrer avec précision plusieurs parties d’une topologie d’infrastructure, avec un contrôle très détaillé du nombre de machines à traiter à la fois.

L’approche d’Ansible en matière d’orchestration est une approche simple et précise : le code d’automatisation devrait être pérenne et il devrait y avoir très peu de choses à retenir sur la syntaxe ou des fonctionnalités spéciales.

Les livres de jeu sont écrits en langage YAML, Ain’t Markup Language. YAML expose un minimum de syntaxe et propose un modèle de configuration ou de processus plutôt qu’un langage de script ou de programmation.

Chaque livres de jeu est composé de une ou plusieurs “séances de jeu (plays)” exposés sous forme d’une liste. Un “livre de jeu” organise des tâches en jeux. Mais il de bonne pratique de l’organiser en “roles”. Un “role” ajoute un niveau d’abstraction dans l’exécution des tâches d’un livre de jeu.

Un jeu est une analogie sportive qui définit un état ou un modèle et qui se “rejoue” de différentes manières à d’autres moments.

Voici trois exemples de livres de jeu. Le premier exemple montre un livre de jeu avec un seul jeu nommé “DEPLOY VLANS” qui s’applique sur les hôtes “access” d’un inventaire. Celui-ci est constitué d’une seule tâche “ENSURE VLANS EXIST”

---
- name: DEPLOY VLANS
  hosts: access
  connection: network_cli
  gather_facts: no
  tasks:
    - name: ENSURE VLANS EXIST
      nxos_vlan:
        vlan_id: 100
        admin_state: up
        name: WEB

Le second exemple est un livre de jeu qui lance un seul jeu constitué de cinq tâches.

---
- name: Configure webserver with nginx
  hosts: webservers
  become: True
  become_method: sudo
  tasks:
    - name: install nginx
      apt:
        name: nginx
        update_cache: yes
    - name: copy nginx config file
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/sites-available/default
    - name: enable configuration
      file:
        dest: /etc/nginx/sites-enabled/default
        src: /etc/nginx/sites-available/default
        state: link
    - name: copy index.html
      template:
        src: templates/index.html.j2
        dest: /usr/share/nginx/html/index.html
        mode: 0644
    - name: restart nginx
      service:
        name: nginx
        state: restarted

Enfin, voici un livre de jeu qui lance un seul jeu constiué d’un nombre indéterminé de tâches appelées via des rôles : apache, mysql, php, …

---
- name: DEPLOY DRUPAL
  hosts: webserver
  vars_files:
    - vars/main.yml
  roles:
    - apache
    - mysql
    - php
    - php-mysql
    - composer
    - drush
    - drupal

7.3. Les rôles

Roles

Les rôles (roles) permettent de charger automatiquement des vars_files, des tasks, des handlers sur base d’une structure de fichier définie. Les rôles sont intéressants pour leur aspect “ré-utilisable”.

Par contre, peu d’éléments permettent d’évaluer la qualité de ces propositions de code “ré-utilisable”.

7.4. Ansible Tower

Ansible Tower est un API, un service Web et une console Web conçue pour rendre Ansible utilisable pour les équipes informatiques avec différents profils. Ansible Tower offre une solution complète de gestion des contrôles d’acccès basée sur des rôles. C’est une console centrale de gestion des tâches d’automatisation.

Ansible Tower est un produit commercial pris en charge par Red Hat, Inc. Red Hat a annoncé à l’AnsibleFest 2016 que Ansible Tower passait en Open Source. En 2017, Tower est publié en opensouce sous le nom de AWXSemaphore est une alternative open source à Ansible Tower, écrit en Go.3

agentless
Aperçu Ansible Tower
Ansible Tower
Roles

Voir AWX.

Sources des images : License MIT github.com/network-automation/linklight

8. Installation Ansible

8.1. Introduction

On se fonde ici sur la page officielle Ansible Installation Guide. Pour commencer une automation et une gestion avec Ansible, vous avez principalement besoin :

Pour la suite des exercices, vous aurez aussi besoin de cibles à gérer. Le document Monter un lab Linux pour Ansible peut vous aider à configurer un lab Ansible avec des machines virtuelles.

Pour connaitre les dernières versions d’Ansible :

curl -s https://pypi.org/pypi/ansible-base/json | jq -r '.releases | keys | sort'

8.2. Paquets Fedora, RHEL et CentOS

Sur RHEL8, CentOS8 et Fedora :

sudo dnf install ansible

Sur RHEL7 et CentOS7 :

sudo yum install ansible

Les fichiers RPMs pour RHEL 7 et RHEL 8 sont disponibles dans le repository Ansible Engine.

Pour l’activer sur RHEL 8 :

sudo subscription-manager repos --enable=ansible-2-for-rhel-8-x86_64-rpms

Pour l’activer sur RHEL 7 :

sudo subscription-manager repos --enable rhel-7-server-ansible-2.9-rpms

Pour construire le RPM à partir des sources et l’installer :

git clone https://github.com/ansible/ansible.git
cd ./ansible
make rpm
sudo rpm -Uvh ./rpm-build/ansible-*.noarch.rpm

8.3. Ubuntu : Dernières versions via Apt

Les programmes Ansible pour sont disponibles en PPA ici.

Pour configurer PPA et installer ansible :

sudo apt-get update
sudo apt-get -y install software-properties-common
sudo apt-add-repository -y ppa:ansible/ansible
sudo apt-get update
sudo apt-get -y install ansible

Les paquets Debian/Ubuntu packages peut être construits par les sources :

make deb

8.4. Debian : Dernières versions via Apt

La procédure Debian est indentique à celle Ubuntu PPA.

Ajouter cette ligne au fichier /etc/apt/sources.list :

deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main

Exécuter les commandes :

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
sudo apt-get update
sudo apt-get install ansible

Note : Cette méthode a été vérifiée avec des sources Trusty sources en Debian Jessie et Stretch mais pourrait ne pas être supportée dans les versions plus récentes.

8.5. Centos : Installation via PIP

sudo yum -y remove ansible
sudo yum install -y python3-pip
sudo alternatives --set python /usr/bin/python3
sudo pip3 install ansible --user

8.6. Debian/Ubuntu : Installation via PIP

sudo apt -y remove ansible
sudo apt update
sudo apt -y install python3-pip
sudo pip3 install --upgrade pip
sudo pip3 install ansible --user
sudo apt -y install sshpass

8.7. Windows 10 Pro WSL

Avec une version Windows 10 Pro, il est possible d’utiliser de commander Ansible avec WSL, voir Can Ansible run on Windows? :

Il n’est pas possible de faire fonctionner Ansible sur un hôte Windows alors qu’il peut le gérer. Mais Ansible peut être exécuté sous Windows Subsystem for Linux (WSL). Notons que WSL n’est pas supporté par Microsoft ou Ansible; il ne devrait pas être utilisé sur des systèmes en production.

Pour installer Ansible sur WSL, il est nécessaire d’exécuter ces commandes dans un terminal bash :

sudo apt-get update
sudo apt-get install python-pip git libffi-dev libssl-dev -y
pip install ansible pywinrm

Pour exécuter Ansible à partir des sources plutôt qu’une version pour WSL, il faut désinstaller la version présente de pip et puis cloner le repo git.

pip uninstall ansible -y
git clone https://github.com/ansible/ansible.git
source ansible/hacking/env-setup

# to enable Ansible on login, run the following
echo ". ~/ansible/hacking/env-setup -q' >> ~/.bashrc

8.8. Complétion bash pour Ansible

Complétion Bash pour Ansible

La procédure consiste à installer python-argcomplete et à le configurer :

cat << EOF > ./setup-python-argcomplete.sh
#!/bin/bash
if [[ ! $UID == 0 ]] ; then echo "you must be root" ; exit ; fi
if [[ -f /etc/redhat-release ]] ; then
yum -y install epel-release && yum -y install python-argcomplete
fi
if [[ -f /etc/lsb-release ]] ; then
apt-get update && \
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" && \
apt-get -y install python3-pip && \
pip3 install --upgrade pip && \
pip3 install argcomplete
fi
activate-global-python-argcomplete
EOF
bash -x ./setup-python-argcomplete.sh

8.9. Script d’installation automatique

Script d’installation automatique de Ansible pour Centos ou Ubuntu.

cat << EOF > ./setup-ansible.sh
#!/bin/bash
if [[ ! $UID == 0 ]] ; then echo "you must be root" ; exit ; fi
if [[ -f /etc/redhat-release ]] ; then
yum -y remove ansible
yum install -y python3-pip
alternatives --set python /usr/bin/python3
pip3 install ansible --user
pip3 install argcomplete
fi
if [[ -f /etc/lsb-release ]] ; then
apt-get -y remove ansible
apt-get update
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
apt-get -y install python3-pip
pip3 install --upgrade pip
pip3 install ansible --user
pip3 install argcomplete
apt-get -y install sshpass
fi
echo "PATH=$HOME/.local/bin:$PATH" >> $HOME/.bash_profile
export PATH="$HOME/.local/bin:$PATH"
activate-global-python-argcomplete
ansible --version
EOF
bash -x ./setup-ansible.sh

8.10. Image Docker

On propose ici d’utiliser une image de conteneur Docker comme environnement d’exécution d’Ansible. Cette méthode permet de garder l’environnement original indépendant et propre tout en bénéficiant d’un environnement d’exécution stable. On peut même utiliser d’ancienne version d’Ansible au cas où on devrait gérer des noeuds qui ne supporte pas de versions supérieures. On est libre de fabriquer ses images localement et des les adapter.

Installation de docker avec des droits privilégiés :

docker_user="ubuntu"
sudo yum -y install curl python3-pip || sudo apt update && sudo apt -y install curl python3-pip
curl -fsSL https://get.docker.com -o get-docker.sh ; sudo sh get-docker.sh
sudo pip3 install docker-compose
sudo usermod -aG docker ${docker_user}

Le projet des images Ansible Cytopia offre des configuration prêtes à l’emploi. Deux exemples simples :

sudo docker run --rm cytopia/ansible:latest-tools ansible -m ping localhost

Si on veut utiliser la version 2.3 par exemple :

sudo docker run --rm cytopia/ansible:2.3-tools ansible --version

9. Exécutables Ansible

Ansible vient avec plusieurs programmes. Cela peut sembler absurde mais le fait de prendre connaissance d’emblée de l’environnement complet peut favoriser l’apprentissage. Nous poursuivons toujours un objectif d’un examen de certification sur Ansible : Maîtrise des composants de base d’Ansible.

Références : Working with Command Line Tools.

Programme Description
ansible programme initial pour l’exécution de commandes ad-hoc
ansible-config Vérifie la configuration courante d’Ansible
ansible-inventory Liste les informations de l’inventaire en format JSON ou YAML
ansible-doc Permet de consulter la documentation hors-ligne
ansible-playbook Permet d’exécuter des livres de jeu
ansible-vault Permet de chiffrer les fichiers qui contiennent des données sensibles
ansible-galaxy Permet de gérer des rôles sur Ansible galaxy
ansible-console Offre une console interactive REPL pour l’exécution de tâches Ad-Hoc
ansible-pull ansible-pull est un petit script qui prend ses informations de configuration d’un repo git et qui exécute un livre de jeu Ansible sur ce contenu
ansible-test Utilitaire de test

ansible est le programme initial pour l’exécution de commandes ad-hoc, ansible-config Vérifie la configuration courante d’Ansible ; ansible-inventory Liste les informations de l’inventaire en format JSON ou YAML ; ansible-doc permet de consulter la documentation hors-ligne ; ansible-playbook permet d’exécuter des livres de jeu ; ansible-vault permet de chiffrer les fichiers qui contiennent des données sensibles ; ansible-galaxy permet de gérer des rôles et des collections sur Ansible galaxy ; ansible-console offre une console interactive REPL pour l’exécution de tâches Ad-Hoc ; ansible-pull ansible-pull est un petit script qui prend ses informations de configuration d’un repo git et qui exécute un livre de jeu Ansible sur ce contenu ; ansible-test est un tilitaire de test.

On ne manquera pas de citer l’outil ansible-lint qui valide des livres de jeu Ansible sur le plan syntaxique.

9.1. Programme ansible

Le programme ansible permet d’exécuter des tâches ad hoc.

ansible --version

La commande ansible attend toujours un hôte ou un groupe d’hôtes d’inventaire comme argument. Ici une option -m désigne l’usage du module ping.

ansible -m ping localhost

Ici l’usage implicite du module raw avec un argument :

ansible localhost -a "cat /etc/resolv.conf"

Le binaire ansible comprend de nombreuses options qui seront abordées dans le chapitre sur les exécutions ad hoc :

ansible --help
usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD]
               [--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts]
               [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-k]
               [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
               [-c CONNECTION] [-T TIMEOUT]
               [--ssh-common-args SSH_COMMON_ARGS]
               [--sftp-extra-args SFTP_EXTRA_ARGS]
               [--scp-extra-args SCP_EXTRA_ARGS]
               [--ssh-extra-args SSH_EXTRA_ARGS] [-C] [--syntax-check] [-D]
               [-e EXTRA_VARS] [--vault-id VAULT_IDS]
               [--ask-vault-password | --vault-password-file VAULT_PASSWORD_FILES]
               [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR]
               [-a MODULE_ARGS] [-m MODULE_NAME]
               pattern

Define and run a single task 'playbook' against a set of hosts

positional arguments:
  pattern               host pattern

optional arguments:
  --ask-vault-password, --ask-vault-pass
                        ask for vault password
  --list-hosts          outputs a list of matching hosts; does not execute
                        anything else
  --playbook-dir BASEDIR
                        Since this tool does not use playbooks, use this as a
                        substitute playbook directory.This sets the relative
                        path for many features including roles/ group_vars/
                        etc.
  --syntax-check        perform a syntax check on the playbook, but do not
                        execute it
  --vault-id VAULT_IDS  the vault identity to use
  --vault-password-file VAULT_PASSWORD_FILES, --vault-pass-file VAULT_PASSWORD_FILES
                        vault password file
  --version             show program's version number, config file location,
                        configured module search path, module location,
                        executable location and exit
  -B SECONDS, --background SECONDS
                        run asynchronously, failing after X seconds
                        (default=N/A)
  -C, --check           don't make any changes; instead, try to predict some
                        of the changes that may occur
  -D, --diff            when changing (small) files and templates, show the
                        differences in those files; works great with --check
  -M MODULE_PATH, --module-path MODULE_PATH
                        prepend colon-separated path(s) to module library (def
                        ault=~/.ansible/plugins/modules:/usr/share/ansible/plu
                        gins/modules)
  -P POLL_INTERVAL, --poll POLL_INTERVAL
                        set the poll interval if using -B (default=15)
  -a MODULE_ARGS, --args MODULE_ARGS
                        module arguments
  -e EXTRA_VARS, --extra-vars EXTRA_VARS
                        set additional variables as key=value or YAML/JSON, if
                        filename prepend with @
  -f FORKS, --forks FORKS
                        specify number of parallel processes to use
                        (default=5)
  -h, --help            show this help message and exit
  -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
                        specify inventory host path or comma separated host
                        list. --inventory-file is deprecated
  -l SUBSET, --limit SUBSET
                        further limit selected hosts to an additional pattern
  -m MODULE_NAME, --module-name MODULE_NAME
                        module name to execute (default=command)
  -o, --one-line        condense output
  -t TREE, --tree TREE  log output to this directory
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)

Privilege Escalation Options:
  control how and which user you become as on target hosts

  --become-method BECOME_METHOD
                        privilege escalation method to use (default=sudo), use
                        `ansible-doc -t become -l` to list valid choices.
  --become-user BECOME_USER
                        run operations as this user (default=root)
  -K, --ask-become-pass
                        ask for privilege escalation password
  -b, --become          run operations with become (does not imply password
                        prompting)

Connection Options:
  control as whom and how to connect to hosts

  --private-key PRIVATE_KEY_FILE, --key-file PRIVATE_KEY_FILE
                        use this file to authenticate the connection
  --scp-extra-args SCP_EXTRA_ARGS
                        specify extra arguments to pass to scp only (e.g. -l)
  --sftp-extra-args SFTP_EXTRA_ARGS
                        specify extra arguments to pass to sftp only (e.g. -f,
                        -l)
  --ssh-common-args SSH_COMMON_ARGS
                        specify common arguments to pass to sftp/scp/ssh (e.g.
                        ProxyCommand)
  --ssh-extra-args SSH_EXTRA_ARGS
                        specify extra arguments to pass to ssh only (e.g. -R)
  -T TIMEOUT, --timeout TIMEOUT
                        override the connection timeout in seconds
                        (default=10)
  -c CONNECTION, --connection CONNECTION
                        connection type to use (default=smart)
  -k, --ask-pass        ask for connection password
  -u REMOTE_USER, --user REMOTE_USER
                        connect as this user (default=None)

Some modules do not make sense in Ad-Hoc (include, meta, etc)

9.2. Programme ansible-config

Le programme ansible-config vérifie la configuration courante d’Ansible avec une des trois options listdump et view.

ansible-config dump

Un chapitre du document examine les options de configuration les plus intéressantes.

9.3. Programme ansible-doc

Le binaire ansible-doc permet de consulter la documentation hors-ligne, identique à la documentation en ligne.

Par défaut ansible-doc effectue sa recherche dans les plugins de type “module”. Ici, pour obtenir la liste des modules documentés :

ansible-doc -l | grep 'yum'

Ici pour lister par exemple les plugins de connexion :

ansible-doc -t connection -l

Pour afficher l’aide du module “yum” :

ansible-doc yum

Pour afficher uniqument les arguments du module :

ansible-doc yum -s

9.4. Programme ansible-playbook

Le programme ansible-playbook permet d’exécuter des livres de jeu.

ansible-playbook playbook.yml --list-hosts
ansible-playbook --list-tasks playbook.yml

Les options du binaire ansible-playbook sont semblables à celles du binaire ansible.

9.5. Programme ansible-galaxy

Le programme ansible-galaxy permet de gérer des rôles et des “collections”, notamment de modules tiers, avec Ansible galaxy.

ansible-galaxy init testrole
ls testrole/
defaults  files  handlers  meta  README.md  tasks  templates  tests  vars

9.6. Programme ansible-inventory

Le programme ansible-inventory liste les informations de l’inventaire en format JSON ou YAML.

Avec l’action –list, l’inventaire sort en format JSON :

ansible-inventory -i inventory --list

Mais il pourrait sortir en format YAML :

ansible-inventory -i inventory --list -y

Ou sous forme de graphe :

ansible-inventory -i inventory --graph

9.7. Programme ansible-vault

Le programme ansible-vault permet de chiffrer les fichiers qui contiennent des données sensibles.

9.8. Programme ansible-pull

ansible-pull est un petit script qui prend ses informations de configuration d’un repo git et qui exécute un livre de jeu Ansible sur ce contenu.

9.9. Programme ansible-console

Le programme ansible-console offre une console interactive REPL pour l’exécution de tâches Ad-Hoc.

9.10. Programme ansible-lint

ansible-lint est un utilitaire qui permet de tester la qualité des livres de jeu en proposant des suggestions de bonne pratique.

On installe ansible-lint avec pip :

pip3 install ansible-lint
  1. Working With Modules  2

  2. https://github.com/ansible-collections/overview  2

  3. https://fr.wikipedia.org/wiki/Ansible_(logiciel) 2

  4. Page README du projet Ansible 

  5. Gartner, Look Beyond Network Vendors for Network Innovation 

Monter un lab Linux pour Ansible

Monter un lab Linux pour Ansible

Ce chapitre envisage cinq solutions de lab pour apprendre Ansible pour dans le cadre d’une gestion de Linux. Une première solution montée avec Docker, deux autres avec Libvirt/KVM gérées en Bash ou avec Ansible lui-même et deux dernières avec Vagrant et Virtualbox ou Libvirt/KVM.

Cette documentation décrit des labs sous Linux Centos ou Debian/Ubuntu.

1. Comment monter un lab pour une gestion Ansible ?

Il existe de nombreuse façon d’expérimenter dans un lab une gestion multi-noeuds avec Ansible. Quelles sont ces possibilités ?

1.1. Topologie de lab

On trouvera ici la topologie du lab à monter, constituée de quatre noeuds Linux dont trois à gérer à partir d’un contrôleur avec Ansible. Pour économiser des ressources, il peut être conseillé de laisser l’hôte de virtualisation comme contrôleur et de monter seulement trois machines virtuelles. Le choix du système par défaut est celui de Centos 8 mais d’autres distributions de type Debian/Ubuntu devraient (aussi bien) fonctionner.

Topologie du lab Ansible

1.2. Examen des possibilités

Quelque soient vos choix, vous avez besoin d’un ordinateur, d’images de systèmes d’exploitation mais aussi différents logiciels.

Les logicels à installer sont un “provider” et un “provisionner”. Dans une moindre mesure, vous pourriez être tenté de fabriquer vos propres images avec un “builder” :

Il existe bien d’autres possibilités avec les prestataires en nuage (AWS, Azure, GCP, …), avec OpenStack ou encore avec LXD, ou encore des scripts ou des wrappers écrits en Python (comme kcli) qui pourraient “approvisionner” la topologie. Mais évoquer ces technologies ici dépasse largement notre objectif.

1.3. Evaluation

Pour être à la hauteur de nos ambitions, il est nécessaire d’établir des critères de choix parmi les combinaisons proposées :

Provider Provisionner Légèreté Facilité Réalité Solution
Docker Bash +++ +++ - - - goffinet/docker-ansible-lab
Docker Docker-compose +++ - - - - -
Qemu/KVM Bash - +++ +++ goffinet/virt-scripts
Qemu/KVM Ansible - ++ +++ goffinet/kvm-ansible-lab
Qemu/KVM Terraform - - +++ -
Virtualbox Vagrant - + +++ goffinet/vagrant-ansible-lab
Docker Vagrant +++ - - -
Qemu/KVM Vagrant - - - - - goffinet/vagrant-libvirt-ansible-lab
Docker Vagrant +++ - - - - -

Parmi ces multiples solutions, cinq d’entre elles ont été choisies et testées pour les besoins de l’ouvrage. Une première solution avec Docker et un script Bash lance le lab. Cette solution est facile à monter et assez robuste mais les labs auront les limites d’une gestion de interne des conteneurs.

En choix principal, pour se rapprocher de la réalité native à Linux, je conseille vivement d’éprouver la virtualisation matérielle avec Libvirt et Qemu/KVM. On peut approvisionner le lab facilement avec des scripts Bash ou, de manière plus robuste, mais plus lente, avec un livre de jeu Ansible.

Enfin, je ne manquerai pas de citer Vagrant avec Virtualbox dans tous les cas ou Vagrant avec KVM sous Linux.

2. Docker / Bash

Code source du projet : goffinet/docker-ansible-lab

Cette solution a le très grand avantage de fonctionner partout, très vite et au coût le plus réduit. Cette solution convient pour des exercices de gestion multi-noeuds au début de l’apprentissage.

Toutefois, les conteneurs avec Docker connaissent des limites1 tenant à leur nature notamment concernant le support du lancement des services à chaud. Cela signifie que toute perspective qui touche aux services (démarrage/arrêt) sera difficile à éprouver, de même que les redémarrages du conteneur lui-même à partir de l’espace du conteneur lui-même par exemple. Pour un alignement sur les sujets du RHCE, les conteneurs ne conviennent pas. D’ailleurs, cette solution est la démonstration d’une mauvaise pratique à éviter en environnement de production. Alors pourquoi utiliser des mauvaises idées, même faciles et tentantes ?

2.1. Prérequis

Le seul prérequis est d’installer Docker, ici sous Linux2 :

curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh

Vous pouvez aussi passer par http://play-with-docker.com (Cliquez sur “+ ADD NEW INSTANCE”).

2.2. Se procurer le script

curl -s https://raw.githubusercontent.com/goffinet/docker-ansible-lab/master/startlab.sh > ./startlab.sh
chmod +x startlab.sh

2.3. Lancer le lab

./startlab.sh

Et vous êtes directement dans le contrôleur dans le dossier /root/lab avec un inventaire prêt à l’emploi.

2.4. Nettoyage du lab

./startlab.sh --remove

2.5. Conteneurs

startlab.sh démarre quatre conteneurs docker et vous connecte à l’environnement du “controller”.

ansible.controller est un conteneur Alpine Linux dans lequel ansible est disponible. On trouve le Dockerfile dans ce même repo. C’est lui qui gère les trois autres noeuds.

node0node1 et node2 sont les conteneurs basés Centos 8 qui agissent comme des noeuds exploitables. Ces noeuds ont déjà été approvisionnés avec la clé ssh du conteneur ansible.controller. Ainsi, vous n’avez pas à vous occuper de l’installation des clés. Cette image est disponible sur Registre d’images de Docker et le Dockerfile est dans ce repo.

2.6. Port Mapping

Certains ports des conteneurs sont exposés en tant que ports “exposés” sur l’hôte :

Conteneur Port du conteneur Port de l’hôte
node0 80 $HOSTPORT_BASE
node1 80 $HOSTPORT_BASE+1
node2 80 $HOSTPORT_BASE+2
node0 8080 $HOSTPORT_BASE+3
node1 30000 $HOSTPORT_BASE+4
node2 443 $HOSTPORT_BASE+5

La variable HOSTPORT_BASE est fixée à la valeur 42726 par défaut et peut être changée en démarrant le lab comme suit :

./startlab.sh --remove # Make sure you shut down the previous ones
HOSTPORT_BASE=<some_other_value> ./startlab.sh

2.7. Dossier de l’espace de travail Workspace

Un dossier docker-ansible-lab/lab sur votre machine locale est monté en tant que /root/lab dans le conteneur ansible.controller. Ainsi, vous pouvez utiliser votre éditeur favori sur votre machine locale pour éditer des fichiers.

2.8. Fabriquer les images Docker

Cloner le code source et se rendre dans le dossier images :

git clone https://github.com/goffinet/docker-ansible-lab.git
cd docker-ansible-lab/images
make buil_all

3. Libvirtd avec les scripts virt-scripts

Code source du projet : goffinet/virt-scripts

Afin de mieux profiter des capacités de virtualisation d’un hôte Linux, il est recommandé d’installer le stack “libvirtd”. Dans ce déploiement, on utilise des machines virtuelles plus proches de la réalité.

On propose ici de laisser le contrôleur sur le serveur de virtualisation pour améliorer la performance du lab.

3.1. Installation des pré-requis

Installation des pré-requis :

echo "Go to the home folder"
cd
echo "Install Git"
apt-get -y install git || yum -y install git
echo "Clone the virt-script repo on Github"
git clone https://github.com/goffinet/virt-scripts
echo "Go to the virt-scripts folder"
cd virt-scripts
echo "Install the requirements"
./autoprep.sh
systemctl stop apache2 || systemctl stop httpd

3.2. Création de la topologie

Lancer trois invités :

echo "include virt-scripts in the PATH"
export PATH=$PATH:~/virt-scripts/ ; echo "PATH=$PATH:~/virt-scripts/" >> ~/.bashrc
echo "Download images Centos and Ubuntu images"
download-images.sh centos8 --force
#download-images.sh focal --force
echo "Launch three Centos 8 guests"
for x in node0 node1 node2 ; do define-guest-image.sh $x centos8 ; done

3.3. Récupérer les adresses IP des machines

Récupérer les adresses IP des noeuds avec jq :

yum -y install epel-release && yum -y install jq
export node0=$(jq -r '.[] | select(.hostname=="node0") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status  | tail -1)
export node1=$(jq -r '.[] | select(.hostname=="node1") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status | tail -1)
export node2=$(jq -r '.[] | select(.hostname=="node2") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status | tail -1)

3.4. Créer un fichier d’inventaire

Créer un dossier de travail ~/lab et créer un fichier d’inventaire inventory :

cd
mkdir lab
cd lab
cat << EOF > inventory
[nodes]
node0 ansible_host=${node0}
node1 ansible_host=${node1}
node2 ansible_host=${node2}

[all:vars]
ansible_connection=ssh
ansible_user=root
ansible_ssh_pass=testtest

EOF

Afficher cet inventaire en format JSON :

ansible-inventory -i inventory --list

3.5. Créer un script d’inventaire dynamique en Bash

Voici un simple nodes.sh script d’inventaire dynamique en Bash qui récupère l’adresse IPv4 des trois invités libvirt :

#!/bin/bash
#https://stackoverflow.com/questions/56280806/ansible-dynamic-inventory-with-bash-script

if [ "$1" == "--list" ]; then
node0=$(jq -r '.[] | select(.hostname=="node0") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status  | tail -1)
node1=$(jq -r '.[] | select(.hostname=="node1") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status | tail -1)
node2=$(jq -r '.[] | select(.hostname=="node2") | ."ip-address"' /var/lib/libvirt/dnsmasq/virbr0.status | tail -1)
cat<<EOF
{
    "_meta": {
        "hostvars": {
            "node0": {
                "ansible_connection": "ssh",
                "ansible_host": "${node0}",
                "ansible_ssh_pass": "testtest",
                "ansible_user": "root"
            },
            "node1": {
                "ansible_connection": "ssh",
                "ansible_host": "${node1}",
                "ansible_ssh_pass": "testtest",
                "ansible_user": "root"
            },
            "node2": {
                "ansible_connection": "ssh",
                "ansible_host": "${node2}",
                "ansible_ssh_pass": "testtest",
                "ansible_user": "root"
            }
        }
    },
    "all": {
        "children": [
            "nodes",
            "ungrouped"
        ]
    },
    "nodes": {
        "hosts": [
            "node0",
            "node1",
            "node2"
        ]
    }
}
EOF
elif [ "$1" == "--host" ]; then
  echo '{"_meta": {hostvars": {}}}'
else
  echo "{ }"
fi
chmod +x nodes.sh
ansible-inventory -i nodes.sh --list

3.6. Créer un fichier de configuration

Dans le même dossier créer un fichier de configuration :

cat << EOF >> ansible.cfg
[defaults]
inventory = ./nodes.sh
host_key_checking = False
private_key_file = /root/.ssh/id_rsa
EOF

3.7. Test de connectivité

Tester Ansible sur tous les hôtes de l’inventaire :

ansible -m ping all

Résultat :

node0 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
node1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

4. Solution de Lab avec KVM et Ansible lui-même

Code source du projet : goffinet/kvm-ansible-lab

4.1. Topologie de lab

Pour reproduire cette topologie avec libvirt/KVM, vous avez donc besoin d’un hôte de virtualisation physique (ou virtuel) avec les instructions de virtualisation activées et une installation Centos8 (ou Ubuntu 20.04) à jour.

Après avoir installé les pré-requis, nous allons créer trois machines virtuelles : controller, node1 et node2 et approvisionner la solution pour assurer une gestion des deux noeuds par la machine de contrôle.

Nous conseillons vivement d’utiliser des instances dans le nuage pour des serveurs “bare-metal”3 chez ScalewayPacketHetzner ou encore OVH.

Dans un premier temps, il s’agira de configurer l’hôte de virtualisation. Ensuite, on créera les trois machines virtuelles. Enfin, nous configurerons le “controller” en y installant Ansible, en y plaçant un fichier d’inventaire et un fichier de configuration par défaut. Une paire de clés SSH sera générée et permettra d’authentifier le controller auprès des noeuds.

Si vous avez un bon serveur de virtualisation sous la main installé en Centos 8, la configuration “se déroule” en quelques minutes.

Cette topologie est constituée de quatre machines virtuelles installée avec l’image Cloud de Centos 8 et des utilisateurs “root” et “ansible” configurés avec un mot de passe trivial.

4.2. Créer la topologie avec Ansible

S’il fallait créer manuellement cette topologie, il serait nécessaire de procéder par étapes :

  1. Installation de quelques pré-requis (Ansible, KVM, Libvirt)
  2. Téléchargement des images
  3. Création de quatre VM : “controller”, “node0”, “node1” et “node2”

Heureusement un livre de jeu a été préparé pour faciliter ce déploiement.

Sur l’hôte de virtualisation, nous allons “cloner” avec Git le livre de jeux kvm-ansible-lab dans le dossier ~/kvm-ansible-lab et exécuter celui-ci, il est conseillé d’être ‘root’ sur le serveur de virtualisation. Cela tient en quelques lignes.

yum -y install git || apt -y install git
git clone --recursive https://github.com/goffinet/kvm-ansible-lab.git \
~/kvm-ansible-lab
cd ~/kvm-ansible-lab
./run.sh

Le script run.sh vérifie que Ansible soit installé et vous propose le cas échéant d’y rémédier (en Bash). Ensuite, il lance un livre de jeu Ansible qui construit la topologie.

4.3. Prendre connaissance de l’infrastructure créée

Entretemps, vous pouvez vous informer sur ce que vous exécutez en consultant le projet kvm-ansible-lab :

La documentation nous indique que l’on peut modifier l’état de la topologie en manipulant une variable “virt_infra_state”. Ici pour détruire les machines virtuelles et les retirer de la gestion de libvirt, on valorise cette variable "virt_infra_state=undefined" sur l’exécution du livre de jeu :

./run.sh -e "virt_infra_state=undefined"

ou directement avec le livre de jeu :

ansible-playbook virt-infra.yml -e "virt_infra_state=undefined"

4.4. Gestion Ansible

Dès que le lab sera monté, les machines virtuelles seront directement gérables à partir de l’hôte de virtualisation :

ansible -m ping all
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
node0 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
controller | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
node1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Aussi, vous pourrez éventuellement vous connecter sur le contrôleur :

ssh root@controller

et créer un projet de base :

dnf -y install epel-release && dnf -y install ansible
PROJECT="lab"
mkdir ~/${PROJECT}
cd ~/${PROJECT}
cat <<  EOF > ~/${PROJECT}/ansible.cfg
[defaults]
inventory = ./inventory
host_key_checking = False
EOF
cat <<  EOF > ~/${PROJECT}/inventory
[nodes]
node0
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_ssh_pass=testtest
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
EOF
ansible -m ping all

4.5. Approvissionement du serveur

5. Vagrant Up avec Virtualbox

Code source du projet : goffinet/vagrant-ansible-lab

Le script Bash setup.sh installe Ansible sur un serveur de virtualisation fonctionnant avec Centos 8 ou Ubuntu Focal et lance le livre de jeu setup.yml Ansible qui installe et configure Virtualbox, Vagrant et Packer.

Le dossier ./lab contient ce lab géré par le serveur de virtualisation en tant que contrôleur Ansible.

yum -y install git || apt -y install git
git clone --recursive https://github.com/goffinet/vagrant-ansible-lab.git
cd vagrant-ansible-lab
./setup.sh
cd lab
vagrant up
ansible -m ping all

5.1. Démarrage

yum -y install git || apt -y install git
git clone --recursive https://github.com/goffinet/vagrant-ansible-lab.git
cd vagrant-ansible-lab
./setup.sh

5.2. Une première topologie

mkdir test
cd test
vagrant box add centos/8
vagrant init centos/8
vagrant up
vagrant ssh

5.3. Gestion de base

vagrant suspend
vagrant resume
vagrant halt
vagrant destroy

5.4. Approvisionnement

mkdir bootstrap
cd bootstrap
cat << EOF > bootstrap.sh
#!/usr/bin/env bash

yum -y install httpd
systemctl enable httpd
systemctl start httpd
EOF
cat << EOF > Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "centos/8"
  config.vm.provision :shell, path: "bootstrap.sh"
end
EOF
vagrant provision

5.5. Déploiement de plusieurs boxes

mkdir lab
cd lab
cat << EOF > ./Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # General Vagrant VM configuration.
  config.vm.box = "centos/8"
  config.ssh.insert_key = false
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.provider :virtualbox do |v|
    v.memory = 512
    v.linked_clone = true
  end

  # node0.
  config.vm.define "node0" do |app|
    app.vm.hostname = "node0"
    app.vm.network :private_network, ip: "192.168.12.10"
  end

  # node1.
  config.vm.define "node1" do |app|
    app.vm.hostname = "node1"
    app.vm.network :private_network, ip: "192.168.12.11"
  end

  # node2.
  config.vm.define "node2" do |app|
    app.vm.hostname = "node2"
    app.vm.network :private_network, ip: "192.168.12.12"
  end
end
EOF
cat << EOF > ./ansible.cfg
[defaults]
inventory = inventory
EOF
cat << EOF > ./inventory
[nodes]
node0 ansible_host=192.168.12.10
node1 ansible_host=192.168.12.11
node2 ansible_host=192.168.12.12

[all:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
EOF
vagrant up
ansible -m ping all

6. Vagrant KVM sur Ubuntu Bionic

Code Source : goffinet/vagrant-libvirt-ubuntu-bionic-ansible-lab

Uniquement sur Ubuntu Bionic.

6.1. Installer et configuration Vagrant et Libvirt/KVM

Ce installe différents composants (uniquement sur Ubuntu 18.04 Bionic) :

git clone https://github.com/goffinet/vagrant-libvirt-ubuntu-bionic-ansible-lab.git
cd vagrant-libvirt-ubuntu-bionic-ansible-lab
./setup.sh
reboot

6.2. Monter le lab

cd lab
vagrant up
ansible -m ping all
node0 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
  1. LXD serait une piste à explorer. 

  2. Docker Desktop est aussi disponible pour Windows Install Docker Desktop on Windows et pour Mac Install Docker Desktop on Mac

  3. Un “serveur bare-metal” est un serveur informatique qui est un “serveur physique à locataire unique”. Ce terme est utilisé de nos jours pour le distinguer des formes modernes de virtualisation et d’hébergement en nuage. (source

Comprendre l’inventaire Ansible

Inventaire Ansible

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification ce document tente de répondre à ceux-ci :

1. Inventaire Ansible

1.1. Définition d’un inventaire Ansible

Un inventaire est une source de données connue d’avance sur les cibles de gestion Ansible organisée en groupes. Les tâches sont exécutées pour des hôtes ou des groupes d’hôtes dans un inventaire défini.1

A quoi sert l’inventaire ?

  1. Sélectionner les hôtes qui subissent des tâches.
  2. Attribuer des valeurs communes ou spécifiques de variables à des hôtes.

La portée d’un jeu au sein du livre de jeu est limitée aux groupes d’hôtes définis dans l’inventaire (option --limit de ansible-playbook). Cela est aussi vrai pour le mode “ad-hoc” pour l’exécution de tâches “ad-hoc” (argument de la commande ansible).

Sans inventaire défini2, Ansible est inutilisable sur des machines distantes !

On trouve habituellement l’inventaire sous forme de fichiers en format YAML ou INI mais n’importe quelle source de données peut faire office d’inventaire.

L’inventaire par défaut est habituellement situé dans le fichier /etc/ansible/hosts.

1.2. Sources d’inventaire

Un inventaire peut être une collection d’hôtes définis, dans une liste, dans un fichier plat, dans un dossier organisé de manière conventionnelle ou être nourri d’un script dynamique (qui interroge un CMDB par exemple) qui génère une liste de périphériques à cibler dans un livre de jeu. Il peut donc être statique, soit défini d’avance ; il peut aussi se créer dynamiquement et même mis à jour dynamiquement.

Finalement, n’importe quelle source de données peut devenir un inventaire qu’Ansible comprend grâce aux plugins d’inventaire. Par défaut, les plugins d’inventaire3 suivants sont activés :

La commande ansible-config nous permet de vérifier cette configuration :

ansible-config view | grep -B 2 -A 2 'inventory plugins'

[inventory]
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
#enable_plugins = host_list, virtualbox, yaml, constructed

Voici des fournisseurs supportés par un inventaire dynamique : BSD Jails, DigitalOcean, Google Compute Engine, Linode, OpenShift, OpenStack, Ovirt, SpaceWalk, Scaleway, Vagrant (à ne pas confondre avec le “provisioner” dans Vagrant, qui est préféré), Zabbix, …

La commande ansible-doc -t inventory -l permet de lister les plugins d’inventaire disponibles :

Plugin d’inventaire Ansible Description
advanced_host_list Parses a ‘host list’ with ranges
auto Loads and executes an inventory plugin specified in a YAML config
aws_ec2 EC2 inventory source
aws_rds rds instance source
azure_rm Azure Resource Manager inventory plugin
cloudscale cloudscale.ch inventory source
constructed Uses Jinja2 to construct vars and groups based on existing inventory
docker_machine Docker Machine inventory source
docker_swarm Ansible dynamic inventory plugin for Docker swarm nodes
foreman foreman inventory source
gcp_compute Google Cloud Compute Engine inventory source
generator Uses Jinja2 to construct hosts and groups from patterns
gitlab_runners Ansible dynamic inventory plugin for GitLab runners
hcloud Ansible dynamic inventory plugin for the Hetzner Cloud
host_list Parses a ‘host list’ string
ini Uses an Ansible INI file as inventory source
k8s Kubernetes (K8s) inventory source
kubevirt KubeVirt inventory source
linode Ansible dynamic inventory plugin for Linode
netbox NetBox inventory source
nmap Uses nmap to find hosts to target
online Online inventory source
openshift OpenShift inventory source
openstack OpenStack inventory source
scaleway Scaleway inventory source
script Executes an inventory script that returns JSON
toml Uses a specific TOML file as an inventory source
tower Ansible dynamic inventory plugin for Ansible Tower
virtualbox virtualbox inventory source
vmware_vm_inventory VMware Guest inventory source
vultr Vultr inventory source
yaml Uses a specific YAML file as an inventory source

1.3. Appel direct à l’inventaire

L’option -i ou --inventory suivie du chemin du fichier d’inventaire est utilisée pour préciser le chemin de l’inventaire avec les programmes ansibleansible-playbook ou encore ansible-inventory.

1.4. Chemin par défaut de l’inventaire

La configuration du chemin d’un inventaire par défaut, ici démontrée avec la configuration d’une variable d’environnement ANSIBLE_INVENTORY :

echo "127.0.0.1 ansible_connection=local" > ~/ansible_hosts
export ANSIBLE_INVENTORY=~/ansible_hosts

On peut aussi préciser le chemin du fichier d’inventaire par défaut dans le fichier de configuration ansible.cfg avec l’entrée inventory sous la section [defaults] :

[defaults]
inventory = ~/ansible_hosts

1.5. Commande ansible-inventory

Le programme ansible-inventory liste les informations de l’inventaire en format JSON ou YAML.

Avec l’action –list, l’inventaire sort en format JSON :

ansible-inventory -i inventory --list

Mais il pourrait sortir en format YAML :

ansible-inventory -i inventory --list -y

Ou sous forme de graphe :

ansible-inventory -i inventory --graph

2. Structure d’un inventaire

2.1. Hôtes et groupes d’hôtes

L’inventaire Ansible est constitué d’hôtes (hosts:), de groupes qui comprennent les hôtes et des variables (vars:) attachées à ces hôtes et à ces groupes. un groupe peut imbriqué d’autres groupes (children:).

Un hôte appartient toujours au moins à deux groupes, car deux groupes d’hôtes sont toujours présents par défaut :

Un groupe permet de “regrouper” les hôtes sur base de :

On peut aussi placer des groupes dans d’autres groupes, soit imbriquer des groupes qui sont alors organisé selon une nomenclature parent/enfant.

2.2. Liste d’hôtes

L’inventaire le plus simple est une liste d’hôte en argument de l’option -i. Par exemple, pour désigner un inventaire directement comme une liste d’hôtes avec les binaires ansible-playbook ou ansible :

ansible -i '192.168.122.10,192.168.122.11,192.168.122.12,' -m ping all

ou encore par leur nom :

ansible -i 'node0,node1,node2,' -m ping all

ou encore avec une liste d’un seul hôte :

ansible -i '192.168.122.10,' -m ping all

2.3. Formats d’inventaire YAML ou INI

Dans cet exemple, on trouve cinq hôtes et deux groupes d’hôtes.

Les sections entre crochets identifient des groupes d’hôtes.

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

En réalité, il faudra compter sur les deux groupes par défaut all et ungrouped. Ici le même inventaire présenté en format YAML :

all:
  children:
    dbservers:
      hosts:
        one.example.com: {}
        three.example.com: {}
        two.example.com: {}
    ungrouped:
      hosts:
        mail.example.com: {}
    webservers:
      hosts:
        bar.example.com: {}
        foo.example.com: {}

On constate que le groupe all imbrique tous les autres. all est le parent de tous les autres groupes, il contient les trois autres groupes enfants dbserversungrouped et webservers.

2.4. Hôtes dans plusieurs groupes

Un hôte peut appartenir à plusieurs groupes, comme par exemple ici en format INI :

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

[east]
foo.example.com
one.example.com
two.example.com

[west]
bar.example.com
three.example.com

[prod]
foo.example.com
one.example.com
two.example.com

[test]
bar.example.com
three.example.com

L’inventaire sembable en format YAML :

all:
  children:
    dbservers:
      hosts:
        one.example.com: {}
        three.example.com: {}
        two.example.com: {}
    east:
      hosts:
        foo.example.com: {}
        one.example.com: {}
        two.example.com: {}
    prod:
      hosts:
        foo.example.com: {}
        one.example.com: {}
        two.example.com: {}
    test:
      hosts:
        bar.example.com: {}
        three.example.com: {}
    ungrouped:
      hosts:
        mail.example.com: {}
    webservers:
      hosts:
        bar.example.com: {}
        foo.example.com: {}
    west:
      hosts:
        bar.example.com: {}
        three.example.com: {}

2.5. Groupes imbriqués

La clé children indique les groupes qui s’imbriquent :

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

[east]
foo.example.com
one.example.com
two.example.com

[west]
bar.example.com
three.example.com

[prod:children]
east

[test:children]
west
all:
  children:
    dbservers:
      hosts:
        one.example.com: {}
        three.example.com: {}
        two.example.com: {}
    prod:
      children:
        east:
          hosts:
    test:
      children:
        west:
    ungrouped:
      hosts:
        mail.example.com: {}
    webservers:
      hosts:
        bar.example.com: {}
        foo.example.com: {}

Vous pouvez simplifier votre inventaire avec des motifs.

Ici sur une étendue :

[webservers]
www[01:50].example.com
...
  webservers:
    hosts:
      www[01:50].example.com:

Avec un incrément :

[webservers]
www[01:50:2].example.com
  webservers:
    hosts:
      www[01:50:2].example.com:

2.6. Hôte implicite localhost

Un inventaire implicite existe toujours pour l’hôte spécial localhost.4 Pour l’hôte local, soit la machine qui exécute Ansible, il n’est donc pas nécessaire de créer un inventaire. Autrement dit on peut exécuter des tâches sur localhost sans qu’il soit défini dans l’inventaire. Ceci dit, la variable ansible_connection valorisée avec local est conseillée pour une communication interne directe. Si la configuration ou l’inventaire ne le précise pas, il faudrait le faire dans le jeu.

...

hosts:
  localhost:
   vars:
     ansible_connection: local
     ansible_python_interpreter: "{{ ansible_playbook_python }}"

3. Variables d’inventaire

L’inventaire est sans doute l’endroit idéal pour valoriser des variables propres à des hôtes ou à des groupes d’hôtes.

3.1. Variable comportementales de connexion

Outre des variables personnalisées, on y trouvera volontiers des variables comportementales spéciales concernant les paramètres de connexion en vue de gérer les cibles.5

3.2. Variables d’inventaire sur les hôtes

Vous pouvez associer des variables aux hôtes mais seulement sur la ligne de l’hôte en format INI :

web ansible_port=22 ansible_host=192.168.122.50
all:
  children:
    ungrouped:
      hosts:
        web:
          ansible_host: 192.168.122.50
          ansible_port: 22

La variable ansible_host est un alias du nom d’hôte qui permet par exemple de définir l’adresse IP de l’hôte à gérer. La variable ansible_port défini le port de connexion de l’hôte.

Un autre exemple dans lequel on spécifie le type de connexion (ansible_connection) et l’utilisateur SSH (ansible_ssh_user) :

localhost ansible_connection=local

[targets]
other1.example.com ansible_connection=ssh ansible_ssh_user=user
other2.example.com ansible_connection=ssh ansible_ssh_user=user
all:
  children:
    targets:
      hosts:
        other1.example.com:
          ansible_connection: ssh
          ansible_ssh_user: user
        other2.example.com:
          ansible_connection: ssh
          ansible_ssh_user: user
    ungrouped:
      hosts:
        localhost:
          ansible_connection: local

3.3. Variables d’inventaire sur les groupes

On appliquera volontiers des variables comportementales de connexion sur les groupes :

Par exemple, dans ce fichier d’inventaire :

[webservers]
app1
app2

[dbservers]
db

[all:vars]
ansible_connection=ssh
ansible_ssh_user=root
ansible_ssh_pass=testtest

ou encore :

[debian]
app[01-02]

[rhel]
app[03-04]

[ubuntu]
app[05-06]

[webservers]
app[01-06]

[others:children]
debian
rhel

[ubuntu:vars]
ansible_ssh_user=ubuntu
ansible_ssh_pass=testtest
ansible_become=true

[others:vars]
ansible_ssh_user=root
ansible_ssh_pass=testtest

[all:vars]
ansible_connection=ssh

3.4. Précédence des variables

3.5. Organisation de variables d’inventaire en fichiers

Au lieu de placer les variables des hôtes et des groupes dans le fichier d’inventaire, il est possible de les encoder dans fichiers séparés prenant le nom de l’hôte ou du groupe dans les dossiers par défaut group_vars et host_vars en format YAML.6

group_vars/
  all.yml
  webservers.yml
  dbservers.yml
host_vars/
  www01.yml
  www02.yml
  db01.yml
  db02.yml

4. Inventaire dynamique

Ansible offre la possibilité d’utiliser un inventaire dynamique plutôt qu’une liste statique d’adresses.7 Si le chemin de l’inventaire est un exécutable, il l’exécutera et interprétera la sortie JSON dans un format particulier.

4.1. Retour en format JSON

Cela signifie que vous pouvez exécuter le code que vous souhaitez pour retourner votre inventaire, à condition de respecter le format, comme par exemple celui-ci :

{
    "_meta": {
        "hostvars": {
            "web": {
                "ansible_host": "192.168.122.50",
                "ansible_port": 22
            }
        }
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {
        "hosts": [
            "web"
        ]
    }
}

Les scripts d’inventaire doivent accepter les arguments --list et --host <hostname>.8

4.2. Exemple en Python

On trouvera ici un exemple formel en Python d’un script d’inventaire nommé sample-inventory.py.

#!/usr/bin/env python3

'''
Example custom dynamic inventory script for Ansible, in Python.
https://github.com/geerlingguy/ansible-for-devops/blob/master/dynamic-inventory/custom/inventory.py
'''

import os
import sys
import argparse
import json

class ExampleInventory(object):

    def __init__(self):
        self.inventory = {}
        self.read_cli_args()

        # Called with `--list`.
        if self.args.list:
            self.inventory = self.example_inventory()
        # Called with `--host [hostname]`.
        elif self.args.host:
            # Not implemented, since we return _meta info `--list`.
            self.inventory = self.empty_inventory()
        # If no groups or vars are present, return empty inventory.
        else:
            self.inventory = self.empty_inventory()

        print(json.dumps(self.inventory));

    # Example inventory for testing.
    def example_inventory(self):
        return {
            "_meta": {
                "hostvars": {
                    "web": {
                        "ansible_host": "192.168.122.50",
                        "ansible_port": 22
                    }
                }
            },
            "all": {
                "children": [
                    "ungrouped"
                ]
            },
            "ungrouped": {
                "hosts": [
                    "web"
                ]
            }
        }

    # Empty inventory for testing.
    def empty_inventory(self):
        return {'_meta': {'hostvars': {}}}

    # Read the command line args passed to the script.
    def read_cli_args(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--list', action = 'store_true')
        parser.add_argument('--host', action = 'store')
        self.args = parser.parse_args()

# Get the inventory.
ExampleInventory()
chmod +x sample-inventory.py
ansible-inventory -i sample-inventory.py --list

4.3. Exemple en Bash

On trouvera ici un même exemple formel en Bash nommé sample-inventory.sh.

#!/bin/bash
#https://stackoverflow.com/questions/56280806/ansible-dynamic-inventory-with-bash-script

if [ "$1" == "--list" ]; then
cat<<EOF
{
    "_meta": {
        "hostvars": {
            "web": {
                "ansible_host": "192.168.122.50",
                "ansible_port": 22
            }
        }
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {
        "hosts": [
            "web"
        ]
    }
}
EOF
elif [ "$1" == "--host" ]; then
  echo '{"_meta": {hostvars": {}}}'
else
  echo "{ }"
fi
chmod +x sample-inventory.sh
ansible-inventory -i sample-inventory.sh --list

4.4. Scripts d’inventaire communautaires

On trouvera dans le lien community.general/scripts/inventory/ toute une série de script d’inventaire maintenus par la communauté pour des prestataires ou des solutions comme Azure, Cobbler, Consul, Docker, Foreman, FreeIPA, GCP, Infoblox, Jail, LXC, LXD, Nagios, OpenShift, Ovirt, OpenStack, Packet, Proxmox, Scaleway, Vagrant, VirtualBox, …

5. Dossier d’inventaire et sources multiples

5.1. Sources multiples

On peut préciser plusieurs sources d’inventaire à l’exécution d’un livre de jeu[[9] :

ansible-playbook get_logs.yml -i staging -i production

5.2. Dossier d’inventaire

Si l’emplacement de l’inventaire est un dossier, Ansible peut utiliser plusieurs sources d’inventaire en même temps. Il est donc possible de mélanger des sources d’inventaire gérées de manière dynamique et statique dans la même exécution d’ansible. Par exemple ce dossier d’inventaire nommé inventory/ :

inventory/
  openstack.yml          # configure un plugin d'inventaire pour obtenir les hôtes d'un nuage Openstack
  dynamic-inventory.py   # ajoute dynamiquement des hôtes avec un script
  static-inventory       # ajoute des hôtes des groupes statiques
  group_vars/
    all.yml              # attribue des variables à tous les hôtes

Dans un dossier d’inventaire, les fichiers exécutables sont traités comme des sources d’inventaire dynamiques et la plupart des autres fichiers comme des sources statiques.

6. Variables magiques de l’inventaire

Ces variables sont dites “magiques”9 car elle ne peuvent pas être définies directement par l’utilisateur. Ansible les remplacera toujours pour refléter l’état interne réel.

Les variables dynamiques d’inventaire (dépendant du contexte d’exécution) comme hostvarsgroupsgroup_names et inventory_hostname permettent d’accéder à des variables appartenant à d’autres hôtes que ceux du jeu lui-même.

Parmi elles, nous citons celles qui dépendent de l’inventaire :

6.1. Variable hostvars et groups

Il ne faut pas confondre la variable hostvars avec le nom par défaut du dossier qui contient les variables des hôtes host_vars/.

La variable magique hostvars est dictionnaire avec tous les hôtes en inventaire et les variables qui leur sont attribuées. On peut la combiner avec l’autre variable magique groups qui est un dictionnaire avec tous les groupes en inventaire, chacun a la liste des hôtes qui lui appartiennent.

Grâce à ces variables, on peut donc accéder à des variables d’hôtes qui ne font pas partie d’un jeu.10

Avec cet inventaire par exemple :

[nodes]
node1 ansible_ssh_host=192.168.122.11 node_name=foo
node2 ansible_ssh_host=192.168.112.12 node_name=bar

[nodes:vars]
custom_var=asdasdasd

On peut accéder à la variable custom_var du groupe nodes avec cette variable en Jinja2 :

{{ hostvars['nodes'].custom_var }}

Et si on avait d’une valeur spécifique d’un hôte, par exemple la variable node_name du premier hôte du groupe nodes, on pourrait invoquer cette formule :

{{ hostvars[groups['nodes'][0]].node_name }}

Avec groups, une liste de tous les groupes (et hôtes) dans l’inventaire, vous pouvez énumérer tous les hôtes d’un groupe. Par exemple, on peut énumérer tous les hôtes d’un groupe :

{% for host in groups['app_servers'] %}
   # something that applies to all app servers.
{% endfor %}

6.2. Récupérer les facts d’un hôte

Si on souhaite configurer un serveur en utilisant la valeur d’un “fact” d’un autre nœud, ou la valeur d’une variable d’inventaire assignée à un autre nœud, vous pouvez utiliser les hostvars dans un template ou sur une ligne d’action :

{{ hostvars['test.example.com']['ansible_facts']['distribution'] }}

Vous pouvez utiliser groups et hostvars ensemble pour trouver toutes les adresses IP d’un groupe, ici dans un template Jinja2 :

{% for host in groups['nodes'] %}
   {{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }}
{% endfor %}

6.3. Variables de noms

Avec group_names, une liste (un tableau) de tous les groupes dans lesquels se trouve l’hôte actuel, on peut créer des fichiers modèles qui varient en fonction de l’appartenance au groupe (ou du rôle) de l’hôte :

{% if 'nodes' in group_names %}
   # some part of a configuration file that only applies to nodes group
{% endif %}

On peut utiliser la variable magique inventory_hostname, le nom de l’hôte tel que configuré dans votre inventaire, comme alternative à ansible_hostname lorsque la collecte de “facts” (fact-gathering) est désactivée. Si vous avez un FQDN long, vous pouvez utiliser la variable inventory_hostname_short, qui contient la partie jusqu’à la première tranche, sans le reste du domaine.

7. Ajout d’hôtes et de groupes d’inventaire par le livre de jeu

Les modules add_host et group_by permettent de créer des hôtes et des groupes d’inventaire par un livre de jeu lui-même, et de les réutiliser dans sa suite.

7.1. Module add_host

L’intérêt du module add_host est d’être en mesure de créer des hôtes et des groupes dans un livre de jeu courant, sans qu’ils soient définis d’avance et les utiliser comme cible dans des jeux ultérieurs du même livre de jeu. Ce sont des variables qui nourissent l’inventaire. Le module permet aussi d’attribuer des variables à ces nouveaux hôtes.

7.2. Module group_by

Le module group_by utilise des “facts” pour créer des groupes correspondants qui seront utilisés comme cible dans les jeux ultérieurs d’un livre de jeu. Autrement dit, group_by permettrait de créer dynamiquement des groupes cibles d’inventaire en fonction de “facts” communs comme la famille et la version de la distribution par exemple ou d’autres caractéristiques communes.

8. Exécution limitée

On peut limiter l’exécution des jeux sur certains hôtes seulement (option -l--limit).

ansible-playbook playbook.yml --limit node0
  1. How to build your inventory 

  2. Excepté pour les tâches sur l’hôte local, l’hôte spécial “localhost” est une cible qu’il n’est pas nécessaire de définir dans l’inventaire. 

  3. Inventory Plugins 

  4. Implicit localhost 

  5. Connecting to hosts: behavioral inventory parameters 

  6. Best Practices, Content Organization et Working with Inventory, Splitting Out Host and Group Specific Data 

  7. ansible.builtin.script – Executes an inventory script that returns JSON 

  8. Working with dynamic inventory 

  9. Magic variables 

  10. Information about Ansible: magic variables 

Modules Ansible

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification voici ceux qui sont suggérés ici :

1. Introduction

Les modules Ansible sont des “bouts de codes” écrits principalement en Python (mais tout langage supportant les retours JSON est autorisé) pour modifier l’état d’une propriété d’un hôte. Les modules sont invoqués par l’exécution de tâches soit directement dans la ligne de commande ansible ou dans des livres de jeu avec la commande ansible-playbook.

2. Documentation des modules

Certains d’entre nous trouveront la documentation des modules plus agréable à lire en ligne : https://docs.ansible.com/ansible/latest/modules/modules_by_category.html. Mais on obtient exactement le même résultat hors-ligne, sur la machine de contrôle avec la commande ansible-doc :

Pour lister les modules :

ansible-doc -l

Pour appeler directement la documentation d’un module.

ansible-doc <module>
ansible-doc -s <module>

Veuillez vous informer sur les modules suivants :

3. Exécutions de tâches Ad-Hoc

La commande ansible offre la possibilité d’exécuter des modules ansible de manière “ad-hoc”, c’est à dire tâche par tâche sur un groupe d’hôtes, une sorte de livre de jeux à tâche unique. L’intérêt est de pouvoir exécuter la même tâche en parallèle sur un inventaire (constitué d’un certain nombre de cibles) en tout ou en partie.

ansible --help

La commande offre ce résultat sur la sortie :

Usage: ansible <host-pattern> [options]

Define and run a single task 'playbook' against a set of hosts

En général, la commande s’écrit ansible suivie d’un hôte ou d’un groupe d’inventaire suivie -m nom_de_module suivie l’option d’argument -a "clé=valeur clé=valeur clé=valeur", par exemple de manière formelle :

ansible <group_inventaire> -m nom_de_module -a "clé=valeur clé=valeur clé=valeur"

Trois autres exemples fonctionnels :

ansible localhost -m debug -a "msg='Hello World!'"
ansible localhost -m file -a "path=/tmp/test state=touch"
ansible localhost -m file -a "path=/tmp/test state=absent"

Attention, l’inventaire doit exister et doit être nourri des hôtes à gérer !

On trouvera aussi des options utiles de la ligne de commande comme celles-ci :

Dans les exemples qui précèdent les tâches portent sur l’hôte spécial “localhost” qui n’a pas besoin d’être référencé dans un inventaire.

4. Valeurs de retour de l’exécution des tâches et des modules

Comme constaté, Ansible génère des valeurs de retour à la suite de l’exécution des tâches et des modules.

Valeurs de retour, Return Values

Valeurs de retour Signication de la valeurs de retour  
backup_file (fichier_de_sauvegarde) Pour les modules qui implémentent backup=no yes lors de la manipulation de fichiers, un chemin vers le fichier de sauvegarde créé.
changed (modifié) Un booléen indiquant si la tâche a dû effectuer des modifications.  
failed (échoué) Un booléen qui indique si la tâche a échoué ou non.  
invocation Informations sur la manière dont le module a été invoqué.  
msg Une chaîne avec un message générique relayé à l’utilisateur.  
rc Certains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter des commandes directement (raw, shell, commande, etc), ce champ contient le ‘code de retour’ de ces utilitaires.  
results Si cette clé existe, elle indique qu’une boucle était présente pour la tâche et qu’elle contient une liste du module normal’résultat’ par élément.  
skipped (évité) Un booléen qui indique si la tâche a été ignorée ou non.  
stderr Certains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter des commandes directement (raw, shell, commande, etc), ce champ contient la sortie d’erreur de ces utilitaires.  
stderr_lines Lorsque stderr est retourné, nous fournissons aussi toujours ce champ qui est une liste de chaînes de caractères, un élément par ligne de l’original.  
stdout Certains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter directement des commandes (raw, shell, commande, etc). Cette zone contient l’édition normale de ces utilitaires.  
stdout_lines Lorsque stdout est retourné, Ansible fournit toujours une liste de chaînes de caractères, chacune contenant un élément par ligne de la sortie originale.  

5. Modules Linux

Les modules Ansible sont donc des “morceaux de code” pour modifier l’état d’une propriété d’un hôte.

On recense ici 240 modules utiles pour l’administration d’un système Linux.

5.1. Paquets logiciels et repos

5.2. Services

5.3. Règles de pare-feu

5.4. Systèmes de fichiers

5.5. Périphériques de stockage

5.6. Contenus de fichiers

5.7. Archives

5.8. Tâches planifiées

5.9. SSH

5.10. SELINUX

5.11. Utilisateurs et groupes

5.12. Administration divers

5.13. Commandes

5.14. Réseau

5.15. Informations

5.16. Modules internes

5.17. Include/Import

5.18. Autres Packagers et gestionnaires de dépendances

5.19. MySQL

5.20. Docker Containers

5.20. LXC/LXD

5.21. LDAP

5.22. Web Servers

5.23. PostgreSQL

5.24. RHSM Red Hat software channels

5.25. Fichiers ini, csv, xml

5.26. Gestion des processus et du noyau

5.27. Surveillance

5.28. XFS / ZFS

5.29. Debian/Ubuntu

5.30. Crypto TLS

6. Modules Réseau

Les modules Ansible pour l’automation du réseau sont désignés par le constructeur de l’OS suivi d’un nom de module :

Selon le nom des plateformes :

Mais on trouve aussi un grand nombre des modules spécialisés.

7. Modules Windows

8. Modules VMWare

9. Développement de modules

Développement de modules

Exemple de développement d’un module simple.

Tâches Ansible Ad-Hoc Linux

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification voici ceux qui sont suggérés ici :

1. Mise en place de la topologie de lab

1.1. Topologie de lab

Topologie du lab Ansible

1.2. Solution de lab

Il est recommandé d’utiliser Libvirt/KVM selon le chapitre Monter un lab Linux pour Ansible.

1.3. Mode ad-hoc

La commande ansible offre la possibilité d’exécuter des modules ansible de manière “ad-hoc”, c’est à dire tâche par tâche sur un groupe d’hôtes. L’intérêt est de pouvoir exécuter la même tâche en parallèle sur un inventaire (constitué d’un certain nombre de cibles) en tout ou en partie.

En général, la commande s’écrit ansible suivie d’un hôte ou d’un groupe d’inventaire suivie -m nom_de_module suivie l’option d’argument -a "clé=valeur clé=valeur clé=valeur", par exemple de manière formelle :

ansible <group_inventaire> -m nom_de_module -a "clé=valeur clé=valeur clé=valeur"

Concrètement :

ansible localhost -m debug -a "msg='Hello World'"
localhost | SUCCESS => {
    "msg": "Hello World"
}

La commande ansible attend comme argument un groupe cible de l’inventaire, une option de module et ses arguments.

On trouvera aussi des options utiles de la ligne de commande comme celles-ci :

Ansible génère des valeurs de retour à la suite de l’exécution des tâches et des modules.

1.4. Documentation des modules

Pour entammer cette activité, il est conseillé de prendre le temps de lire la documentation disponible sur votre machine de contrôle avant toute exécution avec la commande ansible-doc <nom du module>. On sera attentif aux arguments obligatoires (mandatory, =) et aux exemples proposés par rapport à l’objectif à atteindre.

2. Configuration du Contrôleur

La configuration du projet sur le contrôleur a toute son importance. Dans cette étape on propose de créer le projet avec Ansible lui-même, tant que faire se peut … On comprendra peut-être mieux que la solution Ansible joue le rôle d’interface intelligible entre l’infrastructure et ses administrateurs. Cette étape n’est pas nécessaire si le contrôleur est déjà approvisionné.

Nous avons besoin de deux fichiers : un inventaire et un fichier de configuration que nous allons créer dans un dossier dédié.

2.1. Installation de Ansible sur controller

Sur “controller”, nous allons travailler dans le dossier ~/lab, c’est une manière d’initier notre projet Ansible :

mkdir ~/lab
cd ~/lab

Il est nécessaire d’identifier la distribution CentOS 8 :

. /etc/os-release ; echo $PRETTY_NAME
CentOS Linux 8 (Core)

Si le “controller” est bien une machine Centos 8, on peut passer à l’installation du dépôt “Extra” nommés epel-release :

dnf -y install epel-release

Enfin, Ansible s’installe avec le gestionnaire de paquets :

dnf -y install ansible

Vérifions la version du moment :

ansible --version | head -1
ansible 2.9.15

2.2. Premières tâches ad-hoc

On aurait pu créer ce dossier ~/lab avec le module ansible.builtin.file et son argument state=directory :

ansible -m file -a "path=~/lab state=directory" localhost

Avec Ansible, on aurait utilisé le module ansible.builtin.setup qui récupère les “facts” pour obtenir la version de la distribution :

ansible -m setup -a "filter=ansible_distribution*" localhost

2.3. Création d’un fichier d’inventaire

Avant toute chose, un projet de gestion avec ansible a besoin d’un inventaire pour gérer des hôtes distants. On notera que la cible “localhost” (127.0.0.1) est implicite, c’est-à-dire qu’elle ne doit pas nécessairement être citée dans l’inventaire pour en assurer la gestion.

Pour cette opération de création d’inventaire sur le contrôleur, on utilisera le module ansible.builtin.copy avec l’argument content qui crée le fichier en ajoutant du contenu. La séquence \n réalise des retours charriot dans le texte.

L’option -e ou --extra-vars dans le format "user=${user}" permet de passer le contenu des variables Bash dans des variables Jinja2 que les arguments des modules peuvent comprendre.

Ici,la cible est “localhost” et n’a pas besoin d’être défini dans l’inventaire.

user="root"
ansible -m ansible.builtin.copy -e "user=${user}" -a "dest=~/lab/inventory content='[nodes]\nnode0\nnode1\nnode2\n\n[all:vars]\nansible_connection=ssh\nansible_user={{ user }}\nansible_ssh_pass=testtest\n'" localhost

Veuillez vérifier vous-même :

cat ~/lab/inventory
[nodes]
node0
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=root
ansible_ssh_pass=testtest

Cet inventaire désigne trois hôtes :

Il désigne explicitement aussi deux groupes :

On sera attentif aux variables d’inventaire du groupe all :

2.4. Création d’un fichier de configuration minimal

De manière semblable avec le module ansible.builtin.copy, on peut créer un fichier de configuration par défaut ansible.cfg

ansible -m ansible.builtin.copy -a "dest=~/lab/ansible.cfg content='[defaults]\ninventory = ./inventory\nhost_key_checking = False\n#private_key_file = ~/nodes.key\ndeprecation_warnings=False\n'" localhost

Veuillez vérifier vous-même :

cat ~/lab/ansible.cfg
[defaults]
inventory = ./inventory
host_key_checking = False
#private_key_file = ~/nodes.key
deprecation_warnings=False

Ce fichier indique l’emplacement de l’inventaire (./inventory), désactive la vérification des hôtes SSH et désactive les avertisssement concernant les changements futurs du comportement de Ansible. Tant que la clé publique de “controller” n’est pas placée sur le compte l’utilisateur de gestion “ansible” des deux noeuds, le paramètre de clé privée est désactivé dans le fichier de configuration. C’est celle de l’utilisateur root, déjà connue des cibles, qui est utilisée pour l’instant. Nous créerons la clé privée d’un utilisateur de gestion dédié dans les étapes suivantes.

2.5. Test de connectivité

Le module ansible.builtin.ping teste la connexion de gestion par Ansible vers la cible, avec une tentative de connexion qui vérifie l’existence de Python. Attention, le résultat dépend du plugin de connexion utilisé. On sera attentif aux paramètres de connexion utilisateur, de ses privilèges et de mode d’élévation de privilèges, de mot de passe ou de clé privée de la configuration, etc. La réponse attendue est “pong”. Attention, il ne s’agit pas d’un test ICMP Echo Request !

En toute logique, si le lab est bien monté, un résulat positif “pong” doit vous parvenir :

ansible all -m ping
node0 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

Notez que le binaire “ansible” demande un hôte ou un groupe d’inventaire, ici all :

ansible -m ping all

Le code source du module Ping sur Github nous indique que la charge utile du module tient en une vingtaine de lignes.

3. Commandes brutes

Toutes les actions qui suivent se déroulent désormais sur le “controller” dans le dossier de travail ~/lab qui dispose d’un fichier de configuration et d’un inventaire.

cd ~/lab

3.1. Commandes brutes

Si on ne précise pas le nom du module dans la commande ansible, c’est le module ansible.builtin.raw par défaut qui est utilisé avec les commandes brutes comme option d’argument.

Le module raw exécute les arguments directement en “brut” (low-down and dirty) avec le plugin de connexion, ici dans une session SSH.

Quand on ne précise pas le module avec -m <module> et que l’option -a est utilisée, Ansible suppose alors le module raw :

ansible nodes -a "hostname"
ansible nodes -a "df -h"

Installer Python3-pip comme pré-requis à une gestion ansible :

ansible nodes -a "dnf -y install python3-pip"
ansible all -a "uptime"
ansible all -a "date"
ansible all -a "cat /etc/redhat-release"
ansible all -a "mount"
ansible all -a "getenforce"

3.2. Redémarrage en mode brut

Pour redémarrer tous les serveurs dans le groupe [nodes] :

ansible nodes -a "/sbin/reboot"

Il est logique que cette opération brutale de redémarrage rompe la connexion entre le contrôleur et ses noeuds en cours gestion et rende un retour “UNREACHABLE” ou “FAILED/rc=-1” !

nnode2 | FAILED | rc=-1 >>
Failed to connect to the host via ssh: ssh: connect to host 192.168.122.223 port 22: Connection reset by peer
node0 | FAILED | rc=-1 >>
Failed to connect to the host via ssh: ssh: connect to host 192.168.122.37 port 22: Connection refused
node1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.122.222 closed.",
    "unreachable": true
}

Nous verrons dans la suite comment mieux gérer le redémarrage des systèmes sans générer une erreur de connexion.

3.3. Faire une pause

Le module ansible.builtin.pause permet de faire une pause dans le livre de jeu.

ansible nodes -a "/sbin/reboot"
ansible -m pause -a "minutes=2" nodes

3.4. Attendre une connection

Le module ansible.builtin.wait_for_connection est un module qui attend qu’un système distant soit joignable/utilisable.

ansible nodes -a "/sbin/reboot"
ansible nodes -m wait_for_connection

Plus fin, on peut attendre la bannière du serveur SSH avec le module ansible.builtin.wait_for qui attend une condition avant de continuer.

ansible nodes -a "/sbin/reboot"
ansible nodes -m wait_for -a "port=22 host='{{ inventory_hostname }}' search_regex=OpenSSH delay=60"

On remarquera ici l’usage de la variable magique inventory_hostname qui nous assure que le nom d’hôte référencé dans l’inventaire servira à la connexion.

3.5. Redémarrer un hôte avec le module approprié

Le module ansible.builtin.reboot redémarre de manière appropriée le système distant :

ansible nodes -m reboot

3.6. Processus simultanés

Par défaut, Ansible utilise 5 processus simultanés (forks).

ansible-config dump | grep FORKS
DEFAULT_FORKS(default) = 5

On peut manipuler ce paramètre comme variable d’environnement, dans le fichier de configuration ou directement sur la ligne de commande avec -f ou --forks. Ici pour redémarrer les serveurs du groupe nodes avec 10 forks :

ansible nodes -m reboot -f 10

Le nombre de communication simultanées peut être contrôlé dans le fichier de configuration dans la section [default] :

[defaults]
forks = 10

Il est également possible de contrôler la manière dont les tâches se succèdent sur les cibles avec les plugins de stratégie “serial” ou “free”. Avec “serial”, il est possible de diviser la charge simultanée de connexion à partir du contrôleur de manière relative ou absolue.1

3.7. Autres modules de commandes

On trouve au moins deux autres modules de commandes comme ansible.builtin.command et ansible.builtin.shell.

Le module command permet d’exécuter une commande ou un script dans le même shell que les autres tâches alors que le module shell ouvre un nouveau shell (/bin/sh). Le module shell autorise probablement des commandes plus complexes avec des tubes et des redirections par exemple sur une même ligne.

ansible localhost -m command -a "echo test"
ansible localhost -m shell -a "echo test | tee /tmp/test "

En comportement normal, s’ils réussisent (rc=0), quel que soit le véritable état, les modules shell et command offrent un état “CHANGED” :

ansible localhost -m command -a "echo test"
localhost | CHANGED | rc=0 >>
test

Même s’ils ne changent rien en cas de réussite (rc=0), ces modules donnent toujours un état “CHANGED”, ce qui rend par nature la tâche non-idempotente.2 En effet, ces modules ne peuvent pas être idempotent naturellement. Heureusement, les livres de jeu permettent de jouer avec ce comportement. On tentera d’éviter l’usage de ces modules en utilisant des modules natifs pour la tâche ou en développant soi-même des modules plus robustes.

4. Utilisateurs de gestion

4.1. Gérer les utilisateurs et les groupes

Les modules ansible.builtin.user et ansible.builtin.group gèrent les utilisateurs et les groupes.

Ici on crée un utilisateur “louise” dans le groupe “team” :

ansible nodes -m group -a "name=louise state=present"
ansible nodes -m group -a "name=team state=present"
ansible nodes -m user -a "name=louise groups=team,louise createhome=yes append=true"

Ajouter un utilisateur dans un groupe :

ansible nodes -m user -a "name=louise groups=wheel append=true"

Supprimer un utilisateur et son groupe :

ansible nodes -m user -a "name=louise state=absent remove=yes"
ansible nodes -m group -a "name=team state=absent"

4.2. Configurer de sudo

Au prélable, on s’assurera que sudo est installé avec le module ansible.builtin.package :

ansible nodes -m package -a "name=sudo"

Notre intention est de créer un utilisateur de gestion qui pourrait obtenir une élévation de privilège sans qu’un mot de passe soit demandé. On ne discutera pas ici de l’opportunité de cette configuration.

Tentons de trouver de manière récursive une entrée “ALL=(ALL) NOPASSWD:ALL” dans un fichier situé dans le chemin /etc avec le module Ansible ansible.builtin.find qui identifie le rôle sudo sans mot de passe :

ansible node0 -m find -a "paths=/etc contains='.*ALL=\(ALL\)\s*NOPASSWD\:\s*ALL' recurse=yes"

On apprend que le fichier /etc/sudoers contient cette occurence :

ansible node1 -a "cat /etc/sudoers"

On pourrait créer un entrée spécifique dans ce fichier ou encore adapter les droits du groupe wheel et ajouter l’utilisateur dans le groupe wheel.

Une autre solution consiste à créer un fichier dans /etc/sudoers.d/. La dernière recherche nous donne un autre résultat sur le fichier /etc/sudoers.d/90-cloud-init-users. Comment est-il écrit, d’où vient-il ?

ansible node1 -a "cat /etc/sudoers.d/90-cloud-init-users"
node1 | CHANGED | rc=0 >>
# Created by cloud-init v. 19.4 on Sat, 21 Nov 2020 11:36:16 +0000

# User rules for ansible
ansible ALL=(ALL) NOPASSWD:ALL

Créons plutôt un utilisateur “ansible” pour une gestion privilégiée :

ansible nodes -m package -a "name=sudo"
ansible nodes -m user -a "name=ansible"
ansible -m ansible.builtin.copy -a "dest=/etc/sudoers.d/ansible content='ansible  ALL=(ALL) NOPASSWD:ALL'" nodes

Le module ansible.builtin.file permet d’effacer le fichier /etc/sudoers.d/90-cloud-init-users :

ansible -m file -a "path=/etc/sudoers.d/90-cloud-init-users state=absent" nodes

4.3. Création des clés SSH de gestion

Controller doit disposer d’une clé privée associée à une clé publique placée dans le fichier ~/.ssh/authorized_keys d’un utilisateur de gestion sur les cibles, ici root bien que cela ne soit pas recommandé.

La création d’une paire de clés SSH peut se réaliser avec le module community.crypto.openssh_keypair en utilisant les arguments path et size. Le module ansible.builtin.file permet de manipuler les propriétés des fichiers et des dossiers, comme déjà évoqué.

ansible -m openssh_keypair -a "path=~/nodes.key size=2048" localhost
ansible -m file -a "path=~/nodes.key mode=0600" localhost

4.4. Copie de la clé publique

Le module ansible.posix.authorized_key se charge de placer la clé publique du contrôleur dans /home/ansible/.ssh/authorized_keys sur chaque noeud, car c’est l’utilisateur ansible qui est utilisé pour la gestion :

ansible -m authorized_key -a "user=ansible key='{{ lookup('file', '~/nodes.key.pub') }}'" nodes

On remarque l’usage d’une variable Jinja2 avec un filtre lookup qui lit un fichier local : la clé publique qui a été générée précédemment sur le système de fichier local.

4.5. Modification du fichier d’inventaire

Nous avons besoin de modifier le fichier d’inventaire pour exploiter nos changements. Il devrait ressembler à ceci :

[nodes]
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo

Dans cette opération, on utilisera le module ansible.builtin.lineinfile avec les arguments regexp et state pour effacer la ligne qui indique le mot de passe, ainsi que le module community.general.ini_file avec les paramètres sectionoption et value :

ansible localhost -m lineinfile -a "path=~/lab/inventory regexp='^ansible_ssh_pass=.*$' state=absent"
ansible localhost -m lineinfile -a "path=~/lab/inventory regexp='^ansible_user=' line='ansible_user=ansible'"
ansible localhost -m ini_file -a "path=~/lab/inventory section='all:vars' option='ansible_become' value='yes'"
ansible localhost -m ini_file -a "path=~/lab/inventory section='all:vars' option='ansible_become_user' value='root'"
ansible localhost -m ini_file -a "path=~/lab/inventory section='all:vars' option='ansible_become_method' value='sudo'"

Veuillez vérifier vous-même :

cat ~/lab/inventory
[nodes]
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo

4.6. Modification du fichier de configuration minimal

De manière semblable avec le module ansible.builtin.replace, on peut modifier le fichier de configuration par défaut ansible.cfg en décommentant la ligne private_key_file :

ansible localhost -m replace -a "path=~/lab/ansible.cfg regexp='#private_key_file.*$' replace='private_key_file= ~/nodes.key'"

Veuillez vérifier vous-même :

cat ~/lab/ansible.cfg
[defaults]
inventory = ./inventory
host_key_checking = False
private_key_file = ~/nodes.key
deprecation_warnings=False

Vérifions la connectivité :

ansible -m ping all

4.7. Paramètres d’élévation de privilèges

Par défaut, Ansible utilise le compte courant comme utilisateur distant. Notre inventaire utilise un autre compte (le compte distant “ansible”).

On peut manipuler le type de connexion, le nom d’utilisateur, l’usage de l’élévation de privilège, le mot de passe de l’utilisateur et/ou la clé publique dans des variables d’inventaire ou de configuration comme nous l’avons configuré dans ce lab.

Si l’on désire contrôler l’élévation de privilèges sur la ligne de commande, on peut utiliser les options suivantes :

Pour se connecter avec l’utilisateur “ansible” :

ansible nodes -m reboot -u ansible

Redémarrer un serveur exigerait probablement une élévation de privilèges

ansible nodes -m reboot -u ansible --become

Si vous ajoutez --ask-become-pass ou -K, Ansible demande le mot de passe pour l’élévation de privilège (sudo/su/pfexec/doas/etc).

ansible nodes -m reboot -u ansible --become --ask-become-pass

5. Manipulation de fichiers

5.1. Trouver des fichiers

Nous avons déjà vu que le module ansible.builtin.find permet de trouver des fichiers en fonction d’un contenu identifié par expression rationnelle. Mais on l’utilise communément à la manière de la commande find.

Par exemple, pour trouver dans le dossier /usr/share de manière récursive tous les fichiers plus vieux de 4 semaines et plus grand ou égaux à 10 Mo.

ansible all  -m find -a "paths=/usr/share age=4w size=10m recurse=yes"

5.2. Prendre des informations sur un fichier

C’est le module ansible.builtin.stat qui offre une sortie complète sur la nature d’un fichier ou d’un dossier :

ansible nodes -m stat -a "path=/etc/shadow"
ansible nodes -m stat -a "path=/etc"
ansible nodes -m stat -a "path=/opt/random"

5.3. Créer des dossiers et des fichiers

Sur node0 uniquement nous créons un dossier /tmp/test :

ansible nodes -m file -a "dest=/tmp/test mode=644 state=directory"
ansible nodes -m stat -a "path=/tmp/test"

Et puis, nous créons un lien symbolique /opt/tmp qui pointe sur /tmp/test :

ansible nodes -m file -a "src=/tmp/test dest=/opt/tmp owner=root group=root state=link"
ansible nodes -m stat -a "path=/opt/tmp"

5.4. Fixer des droits

Toujours sur node0 uniquement, nous créons trois utilisateurs “alpha”, “beta” et “gamma” dans un groupe “omega”, nous ajoutons notre clé publique dans chaque profil :

group="omega"
users="alpha beta gamma"
ansible node0 -e "group=${group}" -m group -a "name={{ group }}"
for user in ${users} ; do
ansible node0 -e "user=${user}" -m group -a "name={{ user }}"
ansible node0 -e "group=${group} user=${user}" -m user -a "name={{ user }} group={{ user }} groups={{ group }} append=yes"
ansible node0 -e "user=${user}" -m authorized_key -a "user={{ user }} key='{{ lookup('file', '~/nodes.key.pub') }}'"
done

Toujours sur node0 uniquement, nous retirerons le lien symbolique /opt/tmp vers /tmp/test :

ansible node0 -m file -a "src=/tmp/test dest=/opt/tmp state=absent"

Et nous configurons le dossier partagé avec le Sticky bit et le SGID activés ; aussi, nous créons un lien symbolique /opt/omega vers le dossier partagé.

ansible node0 -m file -a "dest=/tmp/test state=directory group=omega mode=3770"
ansible node0 -m file -a "src=/tmp/test dest=/opt/omega group=omega state=link follow=yes"
ansible node0 -m stat -a "path=/opt/omega"

On peut réaliser quelques tests avec le module shell :

ansible all -i 'node0,' -m shell -u alpha -a "echo alpha > /opt/omega/init.txt"
ansible all -i 'node0,' -m shell -u beta -a "echo beta >> /opt/omega/init.txt"
ansible all -i 'node0,' -m shell -u gamma -a "cat /opt/omega/init.txt"
ansible all -i 'node0,' -m shell -u gamma -a "rm -f /opt/omega/init.txt"
ansible all -i 'node0,' -m shell -u beta -a "rm -f /opt/omega/init.txt"
ansible all -i 'node0,' -m shell -u alpha -a "rm -f /opt/omega/init.txt"

5.5. ACLs sur les fichiers

ansible.posix.acl

5.6. Effacer des dossiers et des fichiers

Un état “absent” efface le fichier ou le dossier de manière récursive par défaut :

ansible node0 -m file -a "dest=/opt/omega state=absent"
ansible node0 -m stat -a "path=/opt/omega"

5.7 Archiver des fichiers

for format in gz zip xz bz2 ; do
ansible nodes -e "format=${format}" -m archive -a "path=/usr/share/doc dest=/tmp/doc.{{ format }} format={{ format }}"
done
for format in gz zip xz bz2 ; do
ansible node0 -e "format=${format}" -m shell -a "ls -lh /tmp/doc."
done

5.8. Copier un fichier local sur l’hôte distant

ansible nodes -m ansible.builtin.copy -a "src=/etc/hostname dest=/tmp/hostname"
ansible nodes -m stat -a "path=/tmp/hostname"

5.9. Rapatrier un fichier sur le contrôleur

ansible node1 -m fetch -a "src=/etc/hostname dest=/tmp/{{ inventory_hostname }}"
ansible localhost -m stat -a "path=/tmp/node1"

5.10. Décompacter des archives

ansible.builtin.unarchive copying it from the local machine.

Synchroniser des fichiers

ansible.posix.synchronize

6. Manipuler des volumes

6.1. Créer un disque virtuel léger en format fichier

ansible nodes -m shell -a "dd if=/dev/zero of=/opt/fs_1.img bs=1M seek=8192 count=0"
ansible nodes -m shell -a "losetup -fP /opt/fs_1.img"

6.2. Vérifier que les outils LVM2 sont installés

ansible nodes -m package -a "name=lvm2"

6.3. Créer un volume physique et un volume group

ansible nodes -m lvg -a "vg=vg.fs_1 pvs=/dev/loop0 pesize=256K"

6.4. Ajout d’un volume logique

ansible nodes -m lvol -a "vg=vg.fs_1 lv=lv.ext4 size=1g"

6.5. Système de fichiers EXT4

ansible nodes -m filesystem -a "dev=/dev/vg.fs_1/lv.ext4 fstype=ext4 resizefs=yes force=yes"

6.6. Point de montage et fstab

ansible nodes -m file -a "path=/mnt/fs_1/ext4 state=directory"
ansible nodes -m mount -a "path=/mnt/fs_1/ext4 src=/dev/vg.fs_1/lv.ext4 fstype=ext4 opts=defaults state=mounted boot=yes"
ansible node1 -m setup -a "filter=ansible_mounts"

6.7. Même exercice avec un volume logique XFS

ansible nodes -m lvol -a "vg=vg.fs_1 lv=lv.xfs size=1g"
ansible nodes -m filesystem -a "dev=/dev/vg.fs_1/lv.xfs fstype=xfs resizefs=yes force=yes"
ansible nodes -m file -a "path=/mnt/fs_1/xfs state=directory"
ansible nodes -m mount -a "path=/mnt/fs_1/xfs src=/dev/vg.fs_1/lv.xfs fstype=xfs opts=defaults state=mounted boot=yes"
ansible node1 -m setup -a "filter=ansible_mounts"

6.8. Extension d’un volume logique

ansible nodes -m lvol -a "vg=vg.fs_1 lv=lv.xfs size=2g"
ansible nodes -m filesystem -a "dev=/dev/vg.fs_1/lv.xfs fstype=xfs resizefs=yes force=yes"
ansible nodes -m file -a "path=/mnt/fs_1/xfs state=directory"
ansible nodes -m mount -a "path=/mnt/fs_1/xfs src=/dev/vg.fs_1/lv.xfs fstype=xfs opts=defaults state=mounted boot=yes"
ansible node1 -m setup -a "filter=ansible_mounts"

6.9. Eliminer la configuration

fsname="fs_1"
for fstype in ext4 xfs ; do
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m mount -a "state=absent path=/mnt/{{ fsname }}/{{ fstype }} src=/dev/vg.fs_1/lv.{{ fstype }}"
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m file -a "path=/mnt/{{ fsname }}/{{ fstype }} state=absent"
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m lvol -a "vg=vg.{{ fsname }} lv=lv.{{ fstype }} state=absent force=yes"
done
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m lvg -a "vg=vg.{{ fsname }} pvs=/dev/loop0 state=absent"
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m shell -a "losetup -D"
ansible nodes -e "fstype=${fstype} fsname=${fsname}" -m file -a "path=/opt/{{ fsname }}.img state=absent"

6.10. Créer un partage NFS

7. Récupérer des Facts

7.1. Facts sur les hôtes

Par défaut, le module ansible.builtin.gather_facts est exécuté implicitement dans les livres de jeu avec ansible-playbook. Ce comportement peut se contrôler dans un jeu ou dans la configuration.

Mais avec le binaire ansible, c’est le module ansible.builtin.setup qui affiche les variables collectées.

ansible node1 -m setup

L’argument filter est utile sur la ligne de commande dans un usage Ad Hoc.

ansible node1 -m setup -a 'filter=ansible_mounts'
ansible node1 -m setup -a "filter=*ipv4*"
ansible node1 -m setup -a "filter=ansible_distribution*"
ansible node1 -m setup -a "filter=ansible_swapfree_mb"

7.2. Module Debug

Le module ansible.builtin.debug permet d’afficher des messages. Il prend comme argument soit var qui affiche les variables ou msg qui affiche un message avec des variables.

Voyez la différence :

ansible nodes -m debug -a "var='inventory_hostname'"
ansible nodes -m debug -a "msg='Hello {{ inventory_hostname }}'"

8. Installer et gérer un service

8.1. Installation du service Chrony

Nous allons ici installer le service de temps Chrony sur Centos8 avec les modules ansible.builtin.dnf ou ansible.builtin.yum ou encore le module générique ansible.builtin.package :

ansible all -m dnf -a "name=chrony state=present"
ansible all -m package -a "name=chrony state=present"

8.2. Démarrer et activer le service

Démarrer et activer le service avec les modules ansible.builtin.service ou ansible.builtin.systemd :

ansible all -m service -a "name=chronyd state=started enabled=yes"
ansible all -m systemd -a "name=chronyd state=started enabled=yes"

Pour synchroniser les hôtes, on fera appel aux modules service et raw :

ansible all -m service -a "name=chronyd state=stopped"
ansible all -a "chronyd -q 'server 0.fr.pool.ntp.org iburst'"
ansible all -m service -a "name=chronyd state=started"

Ou encore :

ansible all -a "chronyc -a makestep"

Si nous étions invité à réaliser ces opérations sur des cibles Debian/Ubuntu ou en Centos 7, on proposerait ceci :

Installation de NTPD sur Debian/Ubuntu :

ansible all -m apt -a "name=openntpd state=present"
ansible all -m service -a "name=openntpd state=started enabled=yes"

Installation de NTPD sur RHEL7 :

ansible all -m yum -a "name=ntp state=present"
ansible all -m service -a "name=ntpd state=started enabled=yes"

Et puis les tentatives de synchronisation :

ansible all -m service -a "name=ntpd state=stopped"
ansible all -a "ntpdate -q 0.eu.pool.ntp.org"
ansible all -m service -a "name=ntpd state=started"

9. Limites et tâches en arrière-plan

9.1. Limiter une tâche à un hôte

Dans cette démarche on exécute l’inventaire contre un groupe d’hôte (ici “all”) en le limitant uniquement à “node1” :

ansible all -m service -a "name=chronyd state=restarted" --limit node1

L’option --limit accepte des expressions rationnelles (regexp) :

ansible all -m service -a "name=chronyd state=restarted" --limit "node*"

L’option --limit accepte une liste :

ansible all -m service -a "name=chronyd state=restarted" --limit "node1,node2"

9.2. Tâches en arrière-plan

L’option -B permet d’activer la tâche en arrière-plan avec un délai maximum.

ansible nodes -B 3600 -m dnf -a "name="*" state=latest"

10. Tâches planifiées

11. Manipulation de contenu de fichiers

12. Installation d’un service applicatif LAMP

12.1. Installation du service Apache HTTPd

Sous Centos :

ansible nodes -m yum -a "name=httpd state=present"

12.2. Lancer le service Apache HTTPd

Sous Centos :

ansible nodes -m service -a "name=httpd state=started"

12.3. Tester le service Apache HTTPd

ansible nodes -m uri -a "url=http://{{ansible_host}} return_content=yes status_code=200,403"

12.4. Arrêter le service

Sous Centos :

ansible nodes -m service -a "name=httpd state=stopped"

12.5. Retirer Apache

Sous Centos :

ansible nodes -m yum -a "name=httpd state=absent"

12.6. Installation et gestion du service MariaDB

ansible db -m yum -a "name=mariadb-server state=present"
ansible db -m service -a "name=mariadb state=started enabled=yes"

12.7. Configurer les serveurs d’application

!!!

ansible nodes -m dnf -a "name=python2-mysql state=present"
ansible nodes -m dnf -a "name=python2-setuptools state=present"
ansible nodes -m easy_install -a "name=virtualenv executable=easy_install-2"
ansible nodes -m easy_install -a "name=django state=present virtualenv=/opt"

12.8. Pare-feu Firewalld

12.9. Pare-feu Iptables

!!!

ansible db -a "iptables -F"
ansible db -a "iptables -A INPUT -s 192.168.60.0/24 -p tcp -m tcp --dport 3306 -j ACCEPT"

Comment traduire les commandes iptables en module Ansible ?

ansible db -m iptables -a "flush=yes"
ansible db -m iptables -a "chain=INPUT protocol=tcp source=192.168.60.0/24 destination_port=3306 jump=ACCEPT"
ansible db -m yum -a "name=MySQL-python state=present"
ansible db -m mysql_user -a "name=django host=% password=12345 priv=*.*:ALL state=present"
  1. Setting the batch size with serial 

  2. “Être idempotent permet à une tâche définie d’être exécutée une seule fois ou des centaines de fois sans créer un effet contraire sur le système cible, ne provoquant un changement qu’à une seule reprise. En d’autres mots, si un changement est nécessaire pour obtenir le système dans un état désiré, alors le changement est réalisé ; par contre si le périphérique est déjà dans l’état désiré, aucun changement n’intervient. Ce comportement est différent des pratiques de scripts personnalisés et de copier/coller de lignes de commandes. Quand on exécute les mêmes commandes ou scripts sur un même système de manière répétée, le taux d’erreur est souvent élevé.” Extrait de: Jason Edelman, “Network Automation with Ansible”, O’Reilly Media, 2016. 

Formats JSON et YAML Ansible

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification, vous pourriez trouvez ici des bases qui vous aideront pour :

1. Formats de présentation des données

On présentera brièvement dans cette section trois formats de présentation de données :

Le format JSON sera plus amplement étudié dans une section suivante.

1.1. XML

L’Extensible Markup Language, généralement appelé XML est un métalangage informatique de balisage générique qui est un sous-ensemble du Standard Generalized Markup Language (SGML). Sa syntaxe est dite “extensible” car elle permet de définir différents langages avec chacun leur vocabulaire et leur grammaire, comme XHTML, XSLT, RSS, SVG… Elle est reconnaissable par son usage des chevrons (<>) encadrant les noms des balises. L’objectif initial de XML est de faciliter l’échange automatisé de contenus complexes (arbres, texte enrichi, etc.) entre systèmes d’informations hétérogènes (interopérabilité). Avec ses outils et langages associés, une application XML respecte généralement certains principes :

Exemple :

<menu id="file" value="File">
  <popup>
    <menuitem value="New" onclick="CreateNewDoc()"/>
    <menuitem value="Open" onclick="OpenDoc()"/>
    <menuitem value="Close" onclick="CloseDoc()"/>
  </popup>
</menu>

1.2. JSON

JavaScript Object Notation (JSON) est un format de données textuelles dérivé de la notation des objets du langage JavaScript. Il permet de représenter de l’information structurée comme le permet XML par exemple.

{
    "menu": {
        "id": "file",
        "value": "File",
        "popup": {
            "menuitem": [
                { "value": "New", "onclick": "CreateNewDoc()" },
                { "value": "Open", "onclick": "OpenDoc()" },
                { "value": "Close", "onclick": "CloseDoc()" }
            ]
        }
    }
}

Remarque : JSON ne supporte pas les commentaires au contraire de YAML ou de XML.

1.3. YAML

YAML, acronyme de “Yet Another Markup Language” dans sa version 1.0, il devient l’acronyme récursif de “YAML Ain’t Markup Language” (“YAML n’est pas un langage de balisage”) dans sa version 1.1, est un format de représentation de données par sérialisation Unicode. Il reprend des concepts d’autres langages comme XML.

YAML est facilement à lire et à encoder pour un humain.

menu:
    id: file
    value: File
    popup:
        menuitem:
           - value: New
             onclick: CreateNewDoc()
           - value: Open
             onclick: OpenDoc()
           - value: Close
             onclick: CloseDoc()

1.4. Valider, convertir et travailler avec les formats de données

Code Beautify

2. JavaScript Object Notation (JSON)

JavaScript Object Notation (JSON) est donc un format de données textuelles dérivé de la notation des objets du langage JavaScript. Il permet de représenter de l’information structurée. On en trouvera une description dans le RFC 8259.

JSON se base sur deux structures:

  1. Une collection de couples nom/valeur. Divers langages la réifient par un “objet”, un “enregistrement”, une “structure”, un “dictionnaire”, une “table de hachage”, une “liste typée” ou un “tableau associatif”.
  2. Une liste de valeurs ordonnées. La plupart des langages la réifient par un “tableau”, un “vecteur”, une “liste” ou une “suite”.

2.1. Types de données JSON

Les types de données de base de JSON sont les suivantes :

  1. Objet : une collection non ordonnée de paires nom-valeur dont les noms (aussi appelés clés) sont des chaînes de caractères.1 Les objets sont délimités par des accolades (“{” et “}”) et séparés par des virgules, tandis que dans chaque paire, le caractère deux-points “:” sépare la clé ou le nom de sa valeur. La valeur peut être un tableau, un nombre, une chaîne de caractère, ou une valeur boléenne, ou nulle.
  2. Tableau (array) : une liste ordonnée de zéro ou plus de valeurs, dont chacune peut être de n’importe quel type. Les tableaux utilisent la notation par crochets (“[” et “]”) et les éléments sont séparés par des virgules (“,”).
  3. Nombre : est déclaré sans protection des guillemets.
  4. Chaîne de caractères (string) : une séquence de zéro ou plus de caractères Unicode. Les chaînes de caractères sont délimitées par des guillemets (“"” et “"”) et supportent une barre oblique inversée comme caractère d’échappement (“\”).
  5. Booléen : valeur binaire, l’une ou l’autre des valeurs “true” ou “false”, sans guillemets.
  6. Nulle : Une valeur vide, en utilisant le mot “null”, sans guillemets.

Les espaces limités sont autorisés et ignorés autour ou entre les éléments syntaxiques (valeurs et ponctuation, mais pas à l’intérieur d’une valeur de chaîne). Seuls quatre caractères spécifiques sont considérés comme des espaces : espace, tabulation horizontale, saut de ligne et retour chariot.

JSON ne fournit pas de syntaxe pour les commentaires.

2.2. Exemple de format JSON

{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
  },
  "phoneNumber": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "fax",
      "number": "646 555-4567"
    }
  ],
  "gender": {
    "type": "male"
  }
}

Ces données sont constituées de 6 clés :

L’objet “address” est consitué de 4 clés.

L’objet “phoneNumber” est constitué d’un tableau à deux entrées contenant chacune deux clés.

{
  "type": "home",
  "number": "212 555-1234"
},
{
  "type": "fax",
  "number": "646 555-4567"
}

2.3. Traitement avec jq

Le logiciel jq est outil de traitement du texte à la manière de sed mais pour traiter des sorties JSON.

ansible localhost -b -m package -a "name=jq state=present"

Avant tout il faut des données à traiter, par exemple l’exemple qui précède :

cat << EOF > data
{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
  },
  "phoneNumber": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "fax",
      "number": "646 555-4567"
    }
  ],
  "gender": {
    "type": "male"
  }
}
EOF

Les données à traiter viennent en entrée de la commande :

cat data | jq
{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
  },
  "phoneNumber": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "fax",
      "number": "646 555-4567"
    }
  ],
  "gender": {
    "type": "male"
  }
}

Le filtre se place entre trémas pour incorporer des filtres divers :

cat data | jq '.'

Par exemple le filtre keys affiche les clés d’un objet :

cat data | jq '. | keys'
[
  "address",
  "age",
  "firstName",
  "gender",
  "lastName",
  "phoneNumber"
]

Par exemple le filtre length compte les objets :

cat data | jq '. | length'
6

On peut afficher la valeur d’une clé :

cat data | jq '.firstName'
"John"
cat data | jq '.address'
{
  "streetAddress": "21 2nd Street",
  "city": "New York",
  "state": "NY",
  "postalCode": "10021"
}
cat data | jq '.address.city'
"New York"

Cela fonctionne tant que l’on a à faire avec des objets. Mais cela ne va plus avec des dictionnaires :

cat data | jq '.phoneNumber'
[
  {
    "type": "home",
    "number": "212 555-1234"
  },
  {
    "type": "fax",
    "number": "646 555-4567"
  }
]
cat data | jq '.phoneNumber.type'
jq: error (at <stdin>:24): Cannot index array with string "type"

Reprenons. L’objet “phoneNumber” est consitué d’un dictionnaire de deux objets :

cat data | jq '.phoneNumber'
[
  {
    "type": "home",
    "number": "212 555-1234"
  },
  {
    "type": "fax",
    "number": "646 555-4567"
  }
]

Affichons les objets séparément :

cat data | jq '.phoneNumber[]'
{
  "type": "home",
  "number": "212 555-1234"
}
{
  "type": "fax",
  "number": "646 555-4567"
}

Filtrons par la clé “type” :

cat data | jq '.phoneNumber[] | .type'
"home"
"fax"

Filtrons sur le second objet :

cat data | jq '.phoneNumber[1]'
{
  "type": "fax",
  "number": "646 555-4567"
}

Quelle est la valeur de la seconde clé du champ “number” ?

cat data | jq '.phoneNumber[1].number'
"646 555-4567"

2.4. Module setup

Le module setup exécuté en mode ad-hoc permet de récupérer des méta-données sur les hôtes en format JSON.

ansible localhost -m setup

Ce traitement traite la sortie standard en seule ligne moins la chaîne de caractère | SUCCESS =>

ansible localhost -m setup -o | sed 's/.*> {/{/g'

Pour traitement ultérieur, on valorise une variable $FACTS :

FACTS=$(ansible localhost -m setup -o | sed 's/.*> {/{/g')

Voyez vous-même :

echo $FACTS

2.5. Sorties JSON

Formatage JSON

Début des donées :

echo $FACTS | jq . | head -20
{
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
      "10.6.19.161",
      "172.17.0.1",
      "192.168.122.1"
    ],
    "ansible_all_ipv6_addresses": [
      "fe80::207:cbff:fe0b:1b57"
    ],
    "ansible_apparmor": {
      "status": "enabled"
    },
    "ansible_architecture": "x86_64",
    "ansible_bios_date": "01/17/2018",
    "ansible_bios_version": "00.00.00.0012",
    "ansible_cmdline": {
      "boot": "local",
      "console": "ttyS1,9600n8",
      "nbd.max_part": "16",

Fin des données :

echo $FACTS | jq . | tail -20
      "hw_timestamp_filters": [],
      "macaddress": "52:54:00:c9:73:7e",
      "mtu": 1500,
      "promisc": true,
      "timestamping": [
        "tx_software",
        "rx_software",
        "software"
      ],
      "type": "ether"
    },
    "ansible_virtualization_role": "host",
    "ansible_virtualization_type": "kvm",
    "gather_subset": [
      "all"
    ],
    "module_setup": true
  },
  "changed": false
}

Quel est le nombre d’objets ?

echo $FACTS | jq '. | length'
2

Quels sont ces deux objets ?

echo $FACTS | jq '. | keys'
[
  "ansible_facts",
  "changed"
]

Quels sont les objets imbriqués dans “ansible_facts” ?

echo $FACTS | jq '.ansible_facts | keys'
[
  "ansible_all_ipv4_addresses",
  "ansible_all_ipv6_addresses",
  "ansible_apparmor",
  "ansible_architecture",
  "ansible_bios_date",
  "ansible_bios_version",
  "ansible_cmdline",
  "ansible_date_time",
  "ansible_default_ipv4",
  "ansible_default_ipv6",
  "ansible_device_links",
  "ansible_devices",
  "ansible_distribution",
  "ansible_distribution_file_parsed",
  "ansible_distribution_file_path",
  "ansible_distribution_file_variety",
  "ansible_distribution_major_version",
  "ansible_distribution_release",
  "ansible_distribution_version",
  "ansible_dns",
  "ansible_docker0",
  "ansible_domain",
  "ansible_effective_group_id",
  "ansible_effective_user_id",
  "ansible_env",
  "ansible_eth0",
  "ansible_fips",
  "ansible_form_factor",
  "ansible_fqdn",
  "ansible_hostname",
  "ansible_interfaces",
  "ansible_is_chroot",
  "ansible_iscsi_iqn",
  "ansible_kernel",
  "ansible_lo",
  "ansible_local",
  "ansible_lsb",
  "ansible_lvm",
  "ansible_machine",
  "ansible_machine_id",
  "ansible_memfree_mb",
  "ansible_memory_mb",
  "ansible_memtotal_mb",
  "ansible_mounts",
  "ansible_nodename",
  "ansible_os_family",
  "ansible_pkg_mgr",
  "ansible_processor",
  "ansible_processor_cores",
  "ansible_processor_count",
  "ansible_processor_threads_per_core",
  "ansible_processor_vcpus",
  "ansible_product_name",
  "ansible_product_serial",
  "ansible_product_uuid",
  "ansible_product_version",
  "ansible_python",
  "ansible_python_version",
  "ansible_real_group_id",
  "ansible_real_user_id",
  "ansible_selinux",
  "ansible_selinux_python_present",
  "ansible_service_mgr",
  "ansible_ssh_host_key_dsa_public",
  "ansible_ssh_host_key_ecdsa_public",
  "ansible_ssh_host_key_ed25519_public",
  "ansible_ssh_host_key_rsa_public",
  "ansible_swapfree_mb",
  "ansible_swaptotal_mb",
  "ansible_system",
  "ansible_system_capabilities",
  "ansible_system_capabilities_enforced",
  "ansible_system_vendor",
  "ansible_uptime_seconds",
  "ansible_user_dir",
  "ansible_user_gecos",
  "ansible_user_gid",
  "ansible_user_id",
  "ansible_user_shell",
  "ansible_user_uid",
  "ansible_userspace_architecture",
  "ansible_userspace_bits",
  "ansible_virbr0",
  "ansible_virbr0_nic",
  "ansible_virtualization_role",
  "ansible_virtualization_type",
  "gather_subset",
  "module_setup"
]

De quoi est composé l’objet “ansible_facts.ansible_all_ipv4_addresses” ?

echo $FACTS | jq '.ansible_facts.ansible_all_ipv4_addresses | keys'
[
  "10.6.19.161",
  "172.17.0.1",
  "192.168.122.1"
]

C’est un tableau à trois entrées. Quelle est la seconde entrée ?

echo $FACTS | jq '.ansible_facts.ansible_all_ipv4_addresses[1]'

3. Format YAML pour Ansible

Pour Ansible, presque tous les fichiers YAML commencent par une liste. Chaque élément de la liste est une liste de paires clé / valeur, communément appelée “hash” ou “dictionary”. Il est donc nécessaire de savoir écrire des listes et des dictionnaires en YAML.2

On ici trouvera le minimum à connaître pour utiliser Ansible avec des fichiers de variables de données.

3.1. Début du fichier

Tous les fichiers YAML (indépendamment de leur association avec Ansible ou non) peuvent éventuellement commencer par --- et se terminer par .... Cela fait partie du format YAML et indique le début et la fin d’un document.

3.2. Listes

Tous les membres d’une liste sont des lignes commençant au même niveau d’indentation avec un tiret et un espace : 

---
## A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
...

3.3. Dictionnaires

Un dictionnaire est représenté sous une forme simple (les deux points doivent être suivis d’un espace): key: valueclé: valeur

## An employee record
martin:
  name: Martin D'vloper
  job: Developer
  skill: Elite

La relation entre les données se fait via indentation (2 ou 4 espaces par niveau).

Les valeurs de type “string” (texte) peuvent être encadrées par des guillemets " ou des trémas '. La valeurs numérique ne doivent pas être encadrées.

3.4. Dictionnaires et listes

Des structures de données plus complexes sont possibles, telles que des listes de dictionnaires, c’est-à-dire des dictionnaires dont les valeurs sont des listes ou un mélange des deux:

## Employee records
- martin:
  name: Martin D'vloper
  job: Developer
  skills:
    - python
    - perl
    - pascal
- tabitha:
  name: Tabitha Bitumen
  job: Developer
  skills:
    - lisp
    - fortran
    - erlang

3.5. Forme abrégée

Les dictionnaires et les listes peuvent également être représentés sous une forme abrégée si vous voulez vraiment:

---
martin: {name: Martin D'vloper, job: Developer, skill: Elite}
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango']

Celles-ci sont appelées “collections de flux”.

3.6. Valeur booléenne

On peut également spécifier une valeur booléenne (true / false) sous plusieurs formes :

---
create_key: yes
needs_agent: no
knows_oop: True
likes_emacs: TRUE
uses_cvs: false

3.7. Valeurs sur plusieurs lignes

Les valeurs peuvent couvrir plusieurs lignes en utilisant | ou >. Le fait de couvrir plusieurs lignes en utilisant un “Literal Block Scalar” | inclura les nouvelles lignes et tous les espaces de fin. L’utilisation d’un “Folded Block Scalar” > pliera les lignes nouvelles dans les espaces; il est utilisé pour rendre ce qui serait autrement une très longue ligne plus facile à lire et à éditer. Dans les deux cas, l’indentation sera ignorée. Les exemples sont:

---
include_newlines: |
            exactly as you see
            will appear these three
            lines of poetry

fold_newlines: >
            this is really a
            single line of text
            despite appearances

Alors que dans l’exemple ci-dessus toutes les nouvelles lignes sont repliées dans des espaces, il existe deux manières de faire respecter une nouvelle ligne à conserver :

---
fold_some_newlines: >
    a
    b

    c
    d
      e
    f
same_as: "a b\nc d\n  e\nf\n"

3.8. Synthèse du format YAML

Combinons ce que nous avons appris jusqu’ici dans un exemple YAML arbitraire. Cela n’a vraiment rien à voir avec Ansible, mais cela vous donnera une idée du format de présentation de données à maîtriser.

---
## An employee record
name: Martin D'vloper
job: Developer
skill: Elite
employed: True
foods:
  - Apple
  - Orange
  - Strawberry
  - Mango
languages:
  perl: Elite
  python: Elite
  pascal: Lame
education: |
  4 GCSEs
  3 A-Levels
  BSc in the Internet of Things

3.9. Appel aux variables

Les variables sont appelées dans les livres de jeu Ansible écrit en YAML en étant encadré par des doubles accolades avec espace (Jinja2).

- name: "print ansible_hostname variable play"
  hosts: all
  gather_facts: True
  tasks:
    - name: "print ansible_hostname variable task"
      debug:
        msg: "{{ ansible_hostname }}"

4. Modélisation Jinja2

Jinja2

Exemple3.

apt update && apt install npm
npm install -g json2yaml
cat << EOF > template.j2
{{ ansible_facts.ansible_hostname }}

{% for capability in ansible_facts.ansible_system_capabilities %}
{{ capability }}
{% endfor %}
EOF
ansible localhost -m setup -o | sed 's/.*> {/{/g' | json2yaml > facts.yaml
ansible localhost -m template -a "src=template.j2 dest=/tmp/file.tmp" -e "@facts.yaml"
cat /tmp/file.tmp
  1. Puisque les objets sont destinés à représenter des tableaux associatifs, il est recommandé, bien que non requis, que chaque clé soit unique dans un objet. 

  2. YAML Syntax 

  3. Voir Ansible snippets - manipulating JSON data et Json parsing in Ansible

Optimise projet Ansible

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification, vous pourriez trouvez ici des bases qui vous aideront pour :

1. Gestion des Erreurs Ansible

1.1. Blocs (blocks) de tâches

Blocks

Les blocs permettent le regroupement logique des tâches et la gestion des erreurs dans le jeu. Tout ce que l’on peut appliquer à une tâche unique peut être appliqué au niveau du bloc ce qui facilite également la définition de données ou de directives communes aux tâches. Cela ne signifie pas que la directive affecte le bloc lui-même, mais elle est héritée par les tâches délimitées dans un bloc. Les directives seront appliqué aux tâches, mais pas au bloc lui-même.

  tasks:
    - name: Install Apache
      block:
        - yum:
            name:
              - httpd
              - memcached
            state: installed
        - template:
            src: templates/src.j2
            dest: /etc/foo.conf
        - service:
            name: httpd
            state: started
            enabled: True
      when: ansible_distribution == 'CentOS'
      become: true
      become_user: root

Dans l’exemple ci-dessus, chacune des 3 tâches sera exécutée après l’ajout de la condition when: du bloc et son évaluation dans le contexte de la tâche. En outre, elles héritent des directives relatives à l’élévation des privilèges root (become: et become_user:) .

1.2. Gestion des exceptions

Les blocs permettent également de gérer les sorties d’erreurs de manière similaire aux exceptions de la plupart des langages de programmation.

  tasks:
    - name: Attempt and graceful roll back demo
      block:
        - debug:
            msg: 'I execute normally'
        - command: /bin/false
        - debug:
            msg: 'I never execute, due to the above task failing'
      rescue:
        - debug:
            msg: 'I caught an error'
        - command: /bin/false
        - debug:
            msg: 'I also never execute :-('
      always:
        - debug:
            msg: "This always executes"

Les tâches du block: s’exécutent normalement.

En cas d’erreur, la section rescue: s’exécute avec tout ce que vous devez faire pour résoudre l’erreur précédente.

La section always: est exécutée quelle que soit l’erreur précédente qui s’est produite ou non dans les sections de block: et rescue:.

Il convient de noter que le jeu continue si une section rescue: s’achève avec succès car elle “efface” le statut de l’erreur (mais n’en fait pas le rapport), ce qui signifie qu’elle ne déclenchera ni les configurations max_fail_percentage ni any_errors_fatal, mais elle apparaîtra dans les statistiques du livre de jeu.

Un autre exemple de la prise en charge des erreurs.

  tasks:
    - name: Attempt and graceful roll back demo
      block:
        - debug:
            msg: 'I execute normally'
          notify: run me even after an error
        - command: /bin/false
      rescue:
        - name: make sure all handlers run
          meta: flush_handlers
  handlers:
    - name: run me even after an error
      debug:
        msg: 'This handler runs even on error'

Ansible fournit également quelques variables pour les tâches de la partie rescue: d’un bloc.

1.3. Ignorer les tâches en échec

Une tâche qui échoue (failed) arrête la lecture du livre de jeu.

ignore_errors permet d’outrepasser ce comportement.

- name: this will not be counted as a failure
  command: /bin/false
  ignore_errors: yes

1.4. Contrôler l’état changed

Annulation du résultat “changed”. Lorsqu’un shell, une commande ou un autre module s’exécute, il indique généralement le statut “changed” selon qu’il pense ou non qu’il affecte l’état de la machine.

En fonction du code de retour ou de la sortie, on sait que celui-ci n’a apporté aucune modification et on souhaiterait alors remplacer le résultat “changed” de sorte qu’il n’apparaisse pas dans la sortie du rapport ou ne provoque pas le déclenchement des handlers.

Il s’agit donc d’une condition qui contrôle l’état “changed” certainement en vue de rendre les livres de jeu idempotents.

   - name: Create DB if not exists
     ....
     register: db_create_result
     changed_when: "not db_create_result.stdout|search('already exists. Skipped')"

ou encore :

---
- hosts: web
  tasks:
    - name: "Start the Apache HTTPD Server"
      register: starthttpdout
      shell: "httpd -k start"
      changed_when: "'already running' not in starthttpdout.stdout"
    - debug:
        msg: "{{starthttpdout.stdout}}"

1.5. Contrôler l’état failed

De manière semblable, failed_when permet de contrôler l’état “failed”.

On trouvera ici un exemple de tâches qui créent un état “failed” qui en temps normal réussi toujours. Il s’agit de les utiliser comme “requirements” en espace de stockage d’un application Weblogic de Oracle.

---
- hosts: app
  tasks:
    - name: Making sure the /tmp has more than 1gb
      shell: "df -h /tmp|grep -v Filesystem|awk '{print $4}'|cut -d G -f1"
      register: tmpspace
      failed_when: "tmpspace.stdout|float < 1"

    - name: Making sure the /opt has more than 4gb
      shell: "df -h /opt|grep -v Filesystem|awk '{print $4}'|cut -d G -f1"
      register: tmpspace
      failed_when: "tmpspace.stdout|float < 4"

    - name: Making sure the Physical Memory more than 2gb
      shell: "cat /proc/meminfo|grep -i memtotal|awk '{print $2/1024/1024}'"
      register: memory
      failed_when: "memory.stdout|float < 2"

1.6. Tâche en échec et handlers

Une tâche qui échoue arrête la lecture du livre de jeu et empêche la prise en charge de “handlers” notifié par les tâches précédentes qui interviennent à la fin du jeu.

On peut placer la directive force_handlers: True dans le jeu pour qu’une tâche qui échoue n’empêche pas l’exécution de “handlers” notifiés.

Lorsque les “handlers” sont forcés, ils s’exécutent lorsqu’ils sont notifiés, même si une tâche échoue sur cet hôte.

1.7. Module fail

Le module fail met en échec la progression du jeu avec un message personnalisé.

---
- name: "End play if condition is meet"
  fail:
    msg: "End play with failed status"
  when: variable is defined

2. Gestion Connexions Ansible

2.1. Contrôle de l’élévatation de privilège dans le livre de jeu.

https://docs.ansible.com/ansible/latest/user_guide/become.html

- name: "PLAY 1: demo playbook"
  hosts: localhost
  remote_user: user
  become: yes
  become_user: root
  become_method: sudo

2.2. Contrôle de l’élévatation de privilège dans l’inventaire

2.3. Types de connexions Ansible

- name: "PLAY 1: demo playbook"
  hosts: localhost
  connection: local

Connection Plugins

Les plug-ins de connexion permettent à Ansible de se connecter aux hôtes cibles afin d’exécuter des tâches sur ceux-ci. Ansible est livré avec de nombreux plugins de connexion, mais un seul peut être utilisé par hôte à la fois. Les plus utilisés sont les types de connexion Paramiko SSH, ssh natif (appelé simplement ssh) et local.

On peut utiliser ansible-doc -t connection -l pour voir la liste des plugins disponibles. ansible-doc -t <nom du plug-in> permet d’afficher une documentation détaillée et des exemples.

2.4. Actions Locales et Délegation

Delegation, Rolling Updates, and Local Actions

Si vous souhaitez exécuter une tâche sur un hôte en faisant référence à d’autres hôtes, utilisez le mot-clé delegate_to sur une tâche.

---

- hosts: webservers
  serial: 5

  tasks:

  - name: take out of load balancer pool
    command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
    delegate_to: 127.0.0.1

  - name: actual steps would go here
    yum:
      name: acme-web-stack
      state: latest

  - name: add back to load balancer pool
    local_action: command /usr/bin/add_back_to_pool {{ inventory_hostname }}

Il existe également une syntaxe abrégée local_action: qui inclut la tâche locale au lieu delegate_to: 127.0.0.1 au niveau du module.

---
# ...
  tasks:

  - name: Send summary mail
    local_action:
      module: mail
      subject: "Summary Mail"
      to: "{{ mail_recipient }}"
      body: "{{ mail_body }}"
    run_once: True

La procédure delegate_to: est de toute façon celle qui sera préférée.

2.5. Parralélisme et le plug-in de stratégies.

ansible-doc -t strategy -l
debug       Executes tasks in interactive debug session
free        Executes tasks without waiting for all hosts
host_pinned Executes tasks on each host without interruption
linear      Executes tasks in a linear fashion

Avec le plug-in de stratégie linear, on peut utiliser la directive de jeu serial: <nombre> pour limiter le nombre tâche à exécuter à la fois.

2.6. Paramètre de fork

3. Abstraction des tâches

Il est possible d’ inclure ou d’importer dans un livre de jeu des fichiers qui comprennent une liste de jeux ou de tâches. L’importation est statique et l’inclusion est dynamique. Ansible Galaxy fait référence au site Web de Galaxy https://galaxy.ansible.com à partir duquel les utilisateurs peuvent partager des rôles. Il fait aussi référence à un outil en ligne de commande pour l’installation, la création et la gestion de rôles à partir de dépôts git. Les rôles permettent de charger automatiquement certains fichiers vars_files, tasks et handlers en fonction d’une structure de fichier connue. Le regroupement de contenu par rôles permet également de les partager facilement avec d’autres utilisateurs. En bref, une organisation en rôle n’est jamais qu’une manière d’abstraire son livre de jeu. Toutes les règles de conception d’un livre de jeu sont respectées sur base d’une structure de fichiers et de dossiers connue.

3.1. Inclusions

Il est possible d’ “inclure” dans un livre de jeu des fichiers qui comprennent une liste de jeux ou de tâches avec un module include*. Notons que le module include est déprécié depuis la version Ansible 2.4 au profit de include_tasks et import_tasks

Le module include_tasks est appelé sur le bon niveau hiérarchique. Un liste de tâches se trouvera sous la directive tasks.

Par exemple :

---
#inclusions.yaml
- hosts: all
  tasks:
    - debug:
        msg: task1
    - name: Include task list in play
      include_tasks: stuff.yaml
    - debug:
        msg: task10
- hosts: all
  gather_facts: true
  tasks:
    - debug:
        msg: task1
    - name: Include task list in play only if the condition is true
      include_tasks: other_stuff.yaml
      when: hostvar is defined

Ce livre de jeu est constitué de deux jeux. Le premier jeu comprend trois tâches. La seconde est un “include” d’un fichier stuff.yaml constitué d’une liste de tâches. Le second jeu comprend deux tâches dont la dernière prend la liste de tâche d’un autre fichier other_stuff.yaml.

3.2. Imports

On connait aussi le module import_tasks dont le fonctionnement est similaire à include_tasks.

---
#imports.yaml
- hosts: all
  tasks:
    - debug:
        msg: task1
    - name: Import task list in play
      import_tasks: stuff.yaml
    - debug:
        msg: task10

ou encore :

tasks:
- import_tasks: wordpress.yml
  vars:
    wp_user: timmy
- import_tasks: wordpress.yml
  vars:
    wp_user: alice
- import_tasks: wordpress.yml
  vars:
    wp_user: bob

3.3. Import ou Include ?

Quelle est la différence entre import_* et include_* ?

Toutes les instructions import_* sont pré-traitées au moment de l’analyse des livres de jeu. Toutes les instructions include_* sont traitées au fur et à mesure lors de l’exécution du livre de jeu.

Autrement dit, l’importation est statique, l’inclusion est dynamique.

Sur base de l’expérience, on devrait utiliser import lorsque l’on traite avec des “unités” logiques. Par exemple, une longue liste de tâches dans des fichiers de sous-tâches :

main.yml:

- import_tasks: prepare_filesystem.yml
- import_tasks: install_prerequisites.yml
- import_tasks: install_application.yml

Mais on utiliserait de préférence include pour traiter différents flux de travail et prendre des décisions en fonction de “gathered facts” de manière dynamique :

install_prerequisites.yml:

- include_tasks: prerequisites_{{ ansible_os_family | lower }}.yml

3.4. import_playbook

Le module import_playbook intervient plus au niveau du livre de jeu :

---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml

ou encore :

- hosts: localhost
  tasks:
    - debug:
        msg: play1

- name: Include a play after another play
  import_playbook: otherplays.yaml

3.5. Roles Ansible-Galaxy

Ansible Galaxy fait référence au site Web de Galaxy à partir duquel les utilisateurs peuvent partager des rôles. Il fait aussi référence à un outil en ligne de commande pour l’installation, la création et la gestion de rôles à partir de dépôts git.

Les rôles permettent de charger automatiquement certains fichiers vars_filestasks et handlers en fonction d’une structure de fichier connue. Le regroupement de contenu par rôles permet également de les partager facilement avec d’autres utilisateurs.

En bref, une organisation en rôle n’est jamais qu’une manière d’abstraire les tâches d’un livre de jeu. Toutes les règles de conception d’un livre de jeu sont respectées sur base d’une structure de fichiers et de dossiers connue. On se base alors sur une structure de dossier et de fichiers par convention.

Exemple de structure d’un projet utilisant des rôles.

site.yml
webservers.yml
fooservers.yml
roles/
    common/               # Cette hiérarchie représente un "role"
        tasks/            #
            main.yml      #  <-- peut inclure des fichiers de tâches plus petits si cela est justifié
        handlers/         #
            main.yml      #  <-- fichiers de handlers
        templates/        #  <-- fichiers à utiliser avec le module "template"
            ntp.conf.j2   #  <------- modèles finissent en .j2
        files/            #
            bar.txt       #  <-- fichiers à utiliser avec le module "copy"
            foo.sh        #  <-- fichiers de script à utiliser avec le module "script"
        vars/             #
            main.yml      #  <-- variables associées avec le rôle
        defaults/         #
            main.yml      #  <-- variables par défaut (avec la plus basse priorité)
        meta/             #
            main.yml      #  <-- dépendances du rôle
    webservers/
         tasks/
         defaults/
         meta/

Les rôles s’attendent à ce que les fichiers se trouvent dans certains répertoires dont le nom est connu. Les rôles doivent inclure au moins un de ces répertoires, mais il est parfaitement fonctionnel d’exclure ceux qui ne sont pas nécessaires. Lorsqu’il est utilisé, chaque répertoire doit contenir un fichier main.yml, qui contient le contenu adéquat :

D’autres fichiers YAML peuvent être inclus dans certains répertoires. Par exemple, il est courant d’inclure des tâches spécifiques à la plate-forme à partir du fichier tasks/main.yml :

# roles/example/tasks/main.yml
- name: added in 2.4, previously you used 'include'
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'

# roles/example/tasks/redhat.yml
- yum:
    name: "httpd"
    state: present

# roles/example/tasks/debian.yml
- apt:
    name: "apache2"
    state: present

On appelle des rôles à partir d’un livre de jeu par exemple webservers.yml :

---
# file: webservers.yml
- hosts: webservers
  roles:
    - common
    - webtier

Le livre de jeu principal de l’infrastrucure site.yml serait le suivant :

---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml

3.6. include_role

Le module include_role permet de charger un rôle comme une tâche dynamique.

- name: Run tasks/other.yaml instead of 'main'
  include_role:
    name: myrole
    tasks_from: other

3.7. Création d’un rôle

Création d’un rôle :

cd roles
ansible-galaxy init newrole
- newrole was created successfully
tree newrole/
newrole/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

4. Tags Ansible

Tags

4.1. Tags Ansible

Si vous avez un grand livre de jeu, il peut s’avérer utile de ne pouvoir en exécuter qu’une partie spécifique plutôt que de tout lire dans le livre. Ansible prend en charge un attribut tags: pour cette raison.

Lorsque vous exécutez un livre de jeu, vous pouvez filtrer les tâches en fonction des “tags” de deux manières:

Les “tags” peuvent être appliqués à de nombreuses structures dans Ansible :

Mais son utilisation la plus simple est avec des tâches individuelles. Voici un exemple qui balise deux tâches avec des “tags” différentes:

- hosts: host
  tasks:
    - yum:
        name:
          - httpd
          - memcached
        state: installed
      tags:
       - packages

    - template:
        src: templates/src.j2
        dest: /etc/foo.conf
      tags:
        - configuration

Si vous souhaitez simplement exécuter les parties “configuration” et “packages” d’un très long livre de jeu, vous pouvez utiliser l’option --tags sur la ligne de commande :

ansible-playbook example.yml --tags "configuration,packages"

Si vous souhaitez exécuter un livre de jeu sans certaines tâches marquées, vous pouvez utiliser l’option de ligne de commande --skip-tags :

ansible-playbook example.yml --skip-tags "packages"

4.2. Réutilisation des tags

Vous pouvez appliquer le même “tag” à plusieurs tâches. Lors de l’exécution d’une livre de jeu à l’aide de l’option de ligne de commande --tags, toutes les tâches portant ce nom de “tag” seront exécutées. Cet exemple balise plusieurs tâches avec un “tag” “ntp” :

---
# file: roles/common/tasks/main.yml

- name: be sure ntp is installed
  yum:
    name: ntp
    state: installed
  tags: ntp

- name: be sure ntp is configured
  template:
    src: ntp.conf.j2
    dest: /etc/ntp.conf
  notify:
    - restart ntpd
  tags: ntp

- name: be sure ntpd is running and enabled
  service:
    name: ntpd
    state: started
    enabled: yes
  tags: ntp

4.3. Héritage des tags

On peut ajouter des “tags” à un jeu ou à des rôles ou des tâches importées statiquement. Toutes leurs tâches hériterons des ces “tags”.

L’héritage des “tags” ne s’applique pas aux inclusions dynamiques comme include_role et include_tasks.

On peut aussi appliquer des tags aux autres structures que les tâches mais retenons que ces “tags” sont fondamentalement appliqués aux tâches.

Cet exemple identifie toutes les tâches des deux jeux. La première partie dispose de toutes les tâches étiquetées avec “bar”, et la seconde toutes les tâches étiquetées avec “foo” :

- hosts: all
  tags:
    - bar
  tasks:
    ...

- hosts: all
  tags: ['foo']
  tasks:
    ...

On peut aussi appliquer les “tags” aux tâches importées par des rôles :

roles:
  - role: webserver
    vars:
      port: 5000
    tags: [ 'web', 'foo' ]

Ainsi que sur les directives import_role: et import_tasks: :

- import_role:
    name: myrole
  tags: [web,foo]

- import_tasks: foo.yml
  tags: [web,foo]

Ces “tags” sont appliqués aux tâches par héritage. Cela sert à exécuter un livre de jeu de manière sélective.

Les “tags” sont appliqués tout au long de la chaîne de dépendance. Pour qu’un “tag” soit hérité des tâches d’un rôle dépendant, il doit être appliqué à la déclaration de rôle ou à l’importation statique, et non à toutes les tâches du rôle.

Vous pouvez voir quelles “tags” sont appliqués aux tâches, aux rôles et aux importations statiques en exécutant ansible-playbook avec l’option --list-tasks. Vous pouvez afficher toutes les balises appliquées aux tâches avec l’option --list-tags.

L’héritage ne fonctionnant pas avec include_tasksinclude_roles et les autres “includes” dynamiques, ceux-ci doivent être appliqués sur chaque tâche ou sur des blocks:

Voici un exemple de marquage de tâches de rôle avec le “tag” “mytag”, à l’aide d’une instruction de bloc, à utiliser ensuite avec un “include” dynamique:

- hosts: all
  tasks:
  - include_role:
      name: myrole
    tags: mytag
- block:
    - name: First task to run
    ...
    - name: Second task to run
    ...
  tags:
    - mytag

4.4. Tags spéciaux

Note : Ansible fonctionne par défaut comme si --tags all était précisé.

5. Ansible Vault

Ansible-Vault et Variables and Vaults best practices sont des références à visiter.

5.1. Présentation de la problématique

Ansible-vault est un outil intégré à Ansible qui permet de chiffrer les fichiers qui contiennent des données sensibles.

Il y a certainement beaucoup d’approches pour protéger des variables confidentielles utilisées dans les livres de jeu :

Une autre approche fonctionnelle consisterait à placer son mot de passe dans un fichier dans un emplacement protégé avec des droits restreints, en tout cas en dehors du livre de jeu et de dépôt de contrôle de version. Il s’agira d’appeler ce fichier à chaque exécution du livre de jeu. Un fichier de variables confidentielle aura été chiffré avec ansible-vault à l’aide du fichier qui contient le mot de passe. Ce fichier de variables est inclus comme d’habitude dans un jeu ou ailleurs comme tout fichier de variables.

Une approche selon nous plus complexe est évoquée ici comme “Best Practice” : Variables and Vaults best practices

5.2. Approche par fichiers de mot de passe et de variables confidentiels

Placer ses variables confidentielles dans un fichier (ici secret.yml) :

secret: |
  Ceci est le message chiffré sur plusieurs lignes
  Voici la seconde ligne

Créer un fichier qui contient les mots de passe :

echo "testtest" > ~/.vault_passwords.txt ; chmod 600 ~/.vault_passwords.txt

Chiffrer le fichier secret.yml avec le mot de passe contenu dans l’endroit protégé ~/.vault_passwords.txt :

ansible-vault encrypt secret.yml --vault-password-file ~/.vault_passwords.txt

Pour preuve :

cat secret.yml | head -n 3
$ANSIBLE_VAULT;1.1;AES256
38633337663964663931613733393934383236653434393534393035643137333966633061653762
3536326465643930343062663065303538393831353863360a303036396362353934353163633730

Playbook de démonstration :

---
- name: "demo ansible-vault"
  hosts: localhost
  vars_files:
    - secret.yml
  gather_facts: False
  tasks:
    - name: "affiche le secret"
      debug:
        msg: "{{ secret }}"

Le message chiffré s’affiche !

ansible-playbook test-vault.yml --vault-password-file ~/.vault_passwords.txt

PLAY [demo ansible-vault] ******************************************************

TASK [affiche le secret] *******************************************************
ok: [localhost] => {
    "msg": "Ceci est le message chiffré sur plusieurs lignes\nVoici la seconde ligne\n"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0


5.3. Fichier de mot de passe par défaut

On peut indiquer dans la configuration de Ansible l’emplacement par défaut du fichier de mot de passe via la directive vault_password_file = /path/to/vault_password_file.

5.4. Garder les variables protégées visibles en toute sécurité

On devrait chiffrer les variables sensibles ou secrètes avec Ansible Vault. Mais en chiffrant les noms des variables ainsi que leurs valeurs, il est difficile de trouver la source de ces valeurs. On pourrait garder les noms de vos variables accessibles (par grep, par exemple) sans révéler de secrets en ajoutant une couche indirecte1 :

5.5. Articles

  1. Keep vaulted variables safely visible 

Ansible Tower AWX

Ansible Tower est Ansible au niveau de l’entreprise. Il s’agit d’une solution Web permettant de gérer une organisation avec une interface utilisateur très simple qui fournit un tableau de bord avec des résumés de l’état de tous les hôtes, qui permet des déploiements rapides et surveille toutes les configurations. Tower permet de partager les informations d’identification SSH sans les exposer, de consigner tous les travaux, de gérer graphiquement les inventaires et de les synchroniser avec un large éventail de fournisseurs clouds.

 

nsible Tower AWX Ansible

Si vous poursuivez des objectifs de certification, ce document rencontre les suivants :

1. Description

Ansible Tower est Ansible au niveau de l’entreprise. Il s’agit d’une solution Web permettant de gérer une organisation avec une interface utilisateur très simple qui fournit un tableau de bord avec des résumés de l’état de tous les hôtes, qui permet des déploiements rapides et surveille toutes les configurations. Tower permet de partager les informations d’identification SSH sans les exposer, de consigner tous les travaux, de gérer graphiquement les inventaires et de les synchroniser avec un large éventail de fournisseurs clouds.

2. Pré-requis

3. Versions et support

ANSIBLE TOWER PRICING

Red Hat® Ansible® Tower est disponible en trois éditions différenciées par le support et les fonctionnalités. La tarification est basée sur le nombre de noeuds (systèmes, hôtes, instances, ordinateurs virtuels, conteneurs ou périphériques) que vous gérez. Il existe en version d’essai.

ANSIBLE TOWER PRICING

4. Installation

Ansible Tower Installation

Prérequis pour Ubuntu :

apt-get -y install software-properties-common
apt-add-repository -y ppa:ansible/ansible
apt-get update
apt-get install -y ansible postgresql postgresql-contrib

5. AWX

AWX Project - AWX en abrégé - est un projet de communauté open source, sponsorisé par Red Hat, qui permet aux utilisateurs de mieux contrôler l’utilisation de leurs projets Ansible dans des environnements informatiques. AWX est le projet en amont à partir duquel l’offre Red Hat Ansible Tower est finalement dérivée.

Installing AWX

6. Installation Ansible AWX

Avant de passer à l’installation quelques étapes sont nécessaires :

6.1. Installation de Ansible

Sous Ubuntu :

# Acquisition des droits
sudo -i
# Installer Ansible
export DEBIAN_FRONTEND="noninteractive"
apt-add-repository -y ppa:ansible/ansible
apt-get update
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
apt-get -y install ansible

Sous Centos 8 :

sudo -i
dnf install -y epel-release
dnf install -y git make gcc gcc-c++ nodejs gettext device-mapper-persistent-data lvm2 bzip2 python3-pip python3 pwgen ansible
alternatives --set python /usr/bin/python3

6.2. Installation des composants Docker

Sous Ubuntu :

# Installer les composants Docker
apt -y install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
apt remove docker docker-engine docker.io containerd runc
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository -y "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update
apt -y install docker-ce docker-ce-cli containerd.io curl
usermod -aG docker $USER
#newgrp docker
apt update
apt -y install curl
curl -s https://api.github.com/repos/docker/compose/releases/latest \
  | grep browser_download_url \
  | grep docker-compose-Linux-x86_64 \
  | cut -d '"' -f 4 \
  | wget -qi -
chmod +x docker-compose-Linux-x86_64
mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
apt install -y nodejs npm
npm install npm --global
apt -y install python-pip git pwgen vim
pip install requests==2.14.2
pip install docker-compose==$(docker-compose version --short)
service docker start

Sous Centos 8 :

dnf -y config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf -y install docker-ce --nobest
systemctl start docker
systemctl enable --now docker.service
usermod -aG docker $USER
pip3 install --user docker-compose

6.3. Génération d’un certificat TLS Let’s Encrypt

Installer Certbot pour Ubuntu :

add-apt-repository -y ppa:certbot/certbot && apt update && apt -y install certbot

Installer Certbot pour Centos 8 :

firewall-cmd --zone=public --permanent --add-service=http
firewall-cmd --zone=public --permanent --add-service=https
firewall-cmd --reload
dnf -y install epel-release
dnf update
curl -O https://dl.eff.org/certbot-auto
mv certbot-auto /usr/local/bin/certbot
chmod 0755 /usr/local/bin/certbot
# Générer un certificat TLS Let's Encrypt
FQDN="awx.$(curl -s https://ipinfo.io/ip).nip.io"
certbot certonly --standalone --preferred-challenges http --register-unsafely-without-email --agree-tos -d $FQDN

6.4. Installation de Nginx et configuration en reverse-proxy

Le port TCP80 est redirigé vers le port TCP443 qui va chercher les pages sur le port TCP8000 (du serveur AWX) :

Sous Ubuntu :

# Installer Nginx et le configurer comme reverse-proxy 80->443->8000
apt -y install nginx
cat << EOF > /etc/nginx/sites-available/reverse-proxy.conf
server {
listen 80;
server_name ${FQDN}; # Edit this to your domain name
rewrite ^ https://\$host\$request_uri permanent;
}

server {
listen 443 ssl;

server_name ${FQDN};
ssl_certificate /etc/letsencrypt/live/${FQDN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${FQDN}/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
# Log Location. Can be anywhere. Make sure the nginx user defined in /etc/nginx/nginx.conf has r/w permissions
location / {
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_pass http://localhost:8000/;
proxy_read_timeout 90;
}
}
EOF
ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf
unlink /etc/nginx/sites-enabled/default
systemctl restart nginx

Sous Centos 8 :

dnf -y install nginx
systemctl enable nginx
systemctl start nginx
firewall-cmd --permanent --zone=public --add-service=http --add-service=https
firewall-cmd --reload
cat << EOF > /etc/nginx/sites-available/reverse-proxy.conf
server {
listen 80;
server_name ${FQDN}; # Edit this to your domain name
rewrite ^ https://\$host\$request_uri permanent;
}

server {
listen 443 ssl;

server_name ${FQDN};
ssl_certificate /etc/letsencrypt/live/${FQDN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${FQDN}/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
# Log Location. Can be anywhere. Make sure the nginx user defined in /etc/nginx/nginx.conf has r/w permissions
location / {
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_pass http://localhost:8000/;
proxy_read_timeout 90;
}
}
EOF
ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf
unlink /etc/nginx/sites-enabled/default
systemctl restart nginx
setsebool -P httpd_can_network_connect on

6.5. Téléchargement du code source d’AWX

# Télécharger AWX
mkdir awx-install
cd awx-install
apt-get -y install git || dnf -y install git
git clone https://github.com/ansible/awx.git
git clone https://github.com/ansible/awx-logos.git

6.6. Configuration de l’installation d’AWX

# Configurer l'installation d'AWX
PSQL_DATA_PATH="/opt/awx-psql-data"
mkdir -p ${PSQL_DATA_PATH}
SECRETKEY=$(pwgen -N 1 -s 30)
STRONGPASSWD=$(pwgen -N 1 -s 12)
mkdir -p /var/lib/awx/projects
mv ~/awx-install/awx/installer/inventory ~/awx-install/awx/installer/inventory.old
cat << EOF > ~/awx-install/awx/installer/inventory
localhost ansible_connection=local ansible_python_interpreter="/usr/bin/env python"
[all:vars]
awx_task_hostname=awx
awx_web_hostname="${FQDN}"
awx_official=true
postgres_data_dir="${PSQL_DATA_PATH}"
host_port=8000
docker_compose_dir="~/.awx/awxcompose"
pg_username=awx
pg_password=awxpass
pg_database=awx
pg_port=5432
rabbitmq_password=awxpass
rabbitmq_erlang_cookie=cookiemonster
admin_user=admin
admin_password=${STRONGPASSWD}
create_preload_data=True
secret_key=${SECRETKEY}
project_data_dir=/var/lib/awx/projects
EOF

6.7. Lancement de l’installation

cd ~/awx-install/awx/installer/
ansible-playbook -i inventory install.yml -v

Pour déployer rapidement AWX sans Reverse Proxy HTTPS, on propose ce script : awx-setup.sh.

7. Démarrage rapide Ansible AWS / Ansible Tower

Ansible Tower Quick Start

Configuration Ansible

Configuration Ansible

Ce document propose d’examiner les principales options de configuration de Ansible sur le noeud de contrôle. Son architecture sans agent laisse le soin à chacun de configurer finement et personnellement le comportement par défaut de la solution.

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous préparez un examen de certification, ce document poursuit les objectifs suivant :

Le comportement d’Ansible peut être influencé de différentes manières, dans l’ordre de précédence :

  1. En configurant des variables d’environnement ;
  2. en passant directement les paramètres sur la ligne de commande ansible ou ansible-playbook ;
  3. en définissant des fichiers de configuration ansible.cfg dont la précédence dépend de l’emplacement.

1. Commande ansible-config

La commande ansible-config dump donne la liste des variables de configuration chargées dans le système de contrôle. Voyez-vous même :

ansible-config dump

Beaucoup de ces variables sont dans un format ANSIBLE_* ou DEFAULT_*.

2. Fichier de configuration ansible.cfg

On peut changer ces variables de configuration en renseignant un fichier de configuration. Ansible cherchera dans l’ordre (le premier trouvé sera utilisé et les autres seront ignorés)1 :

  1. Le contenu de la variable d’environnement ANSIBLE_CONFIG (Si la variable d’environnement est valorisée)
  2. L’emplacement ./ansible.cfg (dans le dossier courant, le répertoire de travail)
  3. L’emplacement ~/.ansible.cfg (à la racine du dossier utilisateur comme fichier caché)
  4. L’emplacement /etc/ansible/ansible.cfg (dans le dossier de configuration du logiciel)

La commande ansible-config view permet de visualiser le contenu du fichier de configuration courant.

Le dépot GitHub d’Ansible offre un exemple de fichier de configuration commenté. Il est directement disponible sur cet URL :

curl -s https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg | less

On y trouvera une dizaine de sections dans le format INI :

3. Section [defaults]

La section [defaults] est la plus intéressante, le sigle # au début de chaque ligne mettant le paramètre en commentaire :

3.1. Valeurs habituelles

La section “defaults” définit des variables utiles.

[defaults]

#inventory      = /etc/ansible/hosts
#library        = /usr/share/my_modules/
#module_utils   = /usr/share/my_module_utils/
#remote_tmp     = ~/.ansible/tmp
#local_tmp      = ~/.ansible/tmp
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml

#forks          = 5
#poll_interval  = 15

#sudo_user      = root
#ask_sudo_pass = True
#ask_pass      = True
#transport      = smart
#remote_port    = 22
#module_lang    = C
#module_set_locale = False

On retiendra les variables :

3.2. Récupération des “facts”

Toujours dans la section default, la récupération des “facts” est activée par défaut : gathering = implicit. On peut aussi définir le type de “facts” collectés (gather_subset).

# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit

# This only affects the gathering done by a play's gather_facts directive,
# by default gathering retrieves all facts subsets
# all - gather all subsets
# network - gather min and network facts
# hardware - gather hardware facts (longest facts to retrieve)
# virtual - gather min and virtual facts
# facter - import facts from facter
# ohai - import facts from ohai
# You can combine them using comma (ex: network,virtual)
# You can negate them using ! (ex: !hardware,!facter,!ohai)
# A minimal set of facts is always gathered.
#gather_subset = all

# some hardware related facts are collected
# with a maximum timeout of 10 seconds. This
# option lets you increase or decrease that
# timeout to something more suitable for the
# environment.
# gather_timeout = 10

# Ansible facts are available inside the ansible_facts.* dictionary
# namespace. This setting maintains the behaviour which was the default prior
# to 2.5, duplicating these variables into the main namespace, each with a
# prefix of 'ansible_'.
# This variable is set to True by default for backwards compatibility. It
# will be changed to a default of 'False' in a future release.
# ansible_facts.
# inject_facts_as_vars = True

3.3. Vérification des clés SSH

Il n’est pas nécessaire d’agréer manuellement les clés SSH des hôtes à gérer. Par défaut, le host_key_checking SSH est désactivé.

# uncomment this to disable SSH key host checking
#host_key_checking = False

3.4. Callback plugins

On peut activer la configuration des sorties à la suite des exécutions des tâches : horodatage, envoi de courriel, etc.

# change the default callback, you can only have one 'stdout' type  enabled at a time.
#stdout_callback = skippy
## Ansible ships with some plugins that require whitelisting,
## this is done to avoid running all of a type by default.
## These setting lists those that you want enabled for your system.
## Custom plugins should not need this unless plugin author specifies it.

# enable callback plugins, they can output to stdout but cannot be 'stdout' type.
#callback_whitelist = timer, mail

3.5. Handlers manquants

# Controls if a missing handler for a notification event is an error or a warning
#error_on_missing_handler = True

3.6. Timeout SSH

# SSH timeout
#timeout = 10

3.7. Utilisateur cible par défaut

Par défaut, c’est l’utilisateur local qui agit du même nom sur la cible distante.

# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
#remote_user = root

3.8. Logging

Le logging est désactivé tant qu’un chemin n’est pas défini dans la variable log_path.

# logging is off by default unless this path is defined
# if so defined, consider logrotate
#log_path = /var/log/ansible.log

3.9. Extensions Jinja2

On définit ici l’extension reconnue par Ansible des fichiers de modèles (templates) Jinja2.

# list any Jinja2 extensions to enable here:
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

3.10. Fichier de mot de passe ansible-vault

Pour simplifier la gestion des secrets, on peut encoder son mot de passe ansible-vault dans un emplacement protégé.

# If set, configures the path to the Vault password file as an alternative to
# specifying --vault-password-file on the command line.
#vault_password_file = /path/to/vault_password_file

3.11. Affichage

Ces paramètres permettent de contrôler finement l’affichage dans l’exécution des tâches.

# by default, ansible-playbook will display "Skipping [host]" if it determines a task
# should not be run on a host.  Set this to "False" if you don't want to see these "Skipping"
# messages. NOTE: the task header will still be shown regardless of whether or not the
# task is skipped.
#display_skipped_hosts = True

# by default, if a task in a playbook does not include a name: field then
# ansible-playbook will construct a header that includes the task's action but
# not the task's args.  This is a security feature because ansible cannot know
# if the *module* considers an argument to be no_log at the time that the
# header is printed.  If your environment doesn't have a problem securing
# stdout from ansible-playbook (or you have manually specified no_log in your
# playbook on all of the tasks where you have secret information) then you can
# safely set this to True to get more informative messages.
#display_args_to_stdout = False

# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False

# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
#system_warnings = True

# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
#deprecation_warnings = True

# (as of 1.8), Ansible can optionally warn when usage of the shell and
# command module appear to be simplified by using a default Ansible module
# instead.  These warnings can be silenced by adjusting the following
# setting or adding warn=yes or warn=no to the end of the command line
# parameter string.  This will for example suggest using the git module
# instead of shelling out to the git command.
# command_warnings = False

3.12. Types et emplacements des plugins

# set plugin path directories here, separate with colons
#action_plugins     = /usr/share/ansible/plugins/action
#cache_plugins      = /usr/share/ansible/plugins/cache
#callback_plugins   = /usr/share/ansible/plugins/callback
#connection_plugins = /usr/share/ansible/plugins/connection
#lookup_plugins     = /usr/share/ansible/plugins/lookup
#inventory_plugins  = /usr/share/ansible/plugins/inventory
#vars_plugins       = /usr/share/ansible/plugins/vars
#filter_plugins     = /usr/share/ansible/plugins/filter
#test_plugins       = /usr/share/ansible/plugins/test
#terminal_plugins   = /usr/share/ansible/plugins/terminal
#strategy_plugins   = /usr/share/ansible/plugins/strategy

3.13. Stratégie

Ansible propose principalement deux “stratégies” d’exécution :

# by default, ansible will use the 'linear' strategy but you may want to try
# another one
#strategy = free

3.14. Callbacks

# by default callbacks are not loaded for /bin/ansible, enable this if you
# want, for example, a notification or logging callback to also apply to
# /bin/ansible runs
#bin_ansible_callbacks = False

3.15. Cows

# don't like cows?  that's unfortunate.
# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
#nocows = 1

# set which cowsay stencil you'd like to use by default. When set to 'random',
# a random stencil will be selected for each task. The selection will be filtered
# against the `cow_whitelist` option below.
#cow_selection = default
#cow_selection = random

# when using the 'random' option for cowsay, stencils will be restricted to this list.
# it should be formatted as a comma-separated list with no spaces between names.
# NOTE: line continuations here are for formatting purposes only, as the INI parser
#       in python does not support them.
#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
#              hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
#              stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www

3.16. Couleurs

# don't like colors either?
# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
#nocolor = 1

3.17. Mise en cache des “facts”

# if set to a persistent type (not 'memory', for example 'redis') fact values
# from previous runs in Ansible will be stored.  This may be useful when
# wanting to use, for example, IP information from one group of servers
# without having to talk to them in the same playbook run to get their
# current IP information.
#fact_caching = memory

#This option tells Ansible where to cache facts. The value is plugin dependent.
#For the jsonfile plugin, it should be a path to a local directory.
#For the redis plugin, the value is a host:port:database triplet: fact_caching_connection = localhost:6379:0

#fact_caching_connection=/tmp

3.18. Fichier Retry

# retry files
# When a playbook fails by default a .retry file will be created in ~/
# You can disable this feature by setting retry_files_enabled to False
# and you can change the location of the files by setting retry_files_save_path

#retry_files_enabled = False
#retry_files_save_path = ~/.ansible-retry

3.19. Squash action

# squash actions
# Ansible can optimise actions that call modules with list parameters
# when looping. Instead of calling the module once per with_ item, the
# module is called once with all items at once. Currently this only works
# under limited circumstances, and only with parameters named 'name'.
#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper

3.20. Journalisation des tâches

# prevents logging of task data, off by default
#no_log = False

# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
#no_target_syslog = False

3.21. Divers

# controls whether Ansible will raise an error or warning if a task has no
# choice but to create world readable temporary files to execute a module on
# the remote machine.  This option is False by default for security.  Users may
# turn this on to have behaviour more like Ansible prior to 2.1.x.  See
# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user
# for more secure ways to fix this than enabling this option.
#allow_world_readable_tmpfiles = False

# controls the compression level of variables sent to
# worker processes. At the default of 0, no compression
# is used. This value must be an integer from 0 to 9.
#var_compression_level = 9

# controls what compression method is used for new-style ansible modules when
# they are sent to the remote system.  The compression types depend on having
# support compiled into both the controller's python and the client's python.
# The names should match with the python Zipfile compression types:
# * ZIP_STORED (no compression. available everywhere)
# * ZIP_DEFLATED (uses zlib, the default)
# These values may be set per host via the ansible_module_compression inventory
# variable
#module_compression = 'ZIP_DEFLATED'

# This controls the cutoff point (in bytes) on --diff for files
# set to 0 for unlimited (RAM may suffer!).
#max_diff_size = 1048576

# This controls how ansible handles multiple --tags and --skip-tags arguments
# on the CLI.  If this is True then multiple arguments are merged together.  If
# it is False, then the last specified argument is used and the others are ignored.
# This option will be removed in 2.8.
#merge_multiple_cli_flags = True

# Controls showing custom stats at the end, off by default
#show_custom_stats = True

# Controls which files to ignore when using a directory as inventory with
# possibly multiple sources (both static and dynamic)
#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo

# This family of modules use an alternative execution path optimized for network appliances
# only update this setting if you know how this works, otherwise it can break module execution
#network_group_modules=eos, nxos, ios, iosxr, junos, vyos

# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as
# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain
# jinja2 templating language which will be run through the templating engine.
# ENABLING THIS COULD BE A SECURITY RISK
#allow_unsafe_lookups = False

# set default errors for all plays
#any_errors_fatal = False

4. Section [inventory]

[inventory]
# enable inventory plugins, default: 'host_list', 'script', 'auto', 'yaml', 'ini', 'toml'
#enable_plugins = host_list, virtualbox, yaml, constructed

# ignore these extensions when parsing a directory as inventory source
#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry

# ignore files matching these patterns when parsing a directory as inventory source
#ignore_patterns=

# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise.
#unparsed_is_failed=False

5. Section [privilege_escalation]

[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False

6. Connexion SSH

[paramiko_connection]

# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered.  Increases performance on new host additions.  Setting works independently of the
# host key checking setting above.
#record_host_keys=False

# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
# line to disable this behaviour.
#pty=False

# paramiko will default to looking for SSH keys initially when trying to
# authenticate to remote devices.  This is a problem for some network devices
# that close the connection after a key failure.  Uncomment this line to
# disable the Paramiko look for keys function
#look_for_keys = False

# When using persistent connections with Paramiko, the connection runs in a
# background process.  If the host doesn't already have a valid SSH key, by
# default Ansible will prompt to add the host key.  This will cause connections
# running in background processes to fail.  Uncomment this line to have
# Paramiko automatically add host keys.
#host_key_auto_add = True
[ssh_connection]

# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it, -C controls compression use
#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s

# The base directory for the ControlPath sockets.
# This is the "%(directory)s" in the control_path option
#
# Example:
# control_path_dir = /tmp/.ansible/cp
#control_path_dir = ~/.ansible/cp

# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
# port and username (empty string in the config). The hash mitigates a common problem users
# found with long hostnames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
# In those cases, a "too long for Unix domain socket" ssh error would occur.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path =

# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False

# Control the mechanism for transferring files (old)
#   * smart = try sftp and then try scp [default]
#   * True = use scp only
#   * False = use sftp only
#scp_if_ssh = smart

# Control the mechanism for transferring files (new)
# If set, this will override the scp_if_ssh option
#   * sftp  = use sftp to transfer files
#   * scp   = use scp to transfer files
#   * piped = use 'dd' over SSH to transfer files
#   * smart = try sftp, scp, and piped, in that order [default]
#transfer_method = smart

# if False, sftp will not use batch mode to transfer files. This may cause some
# types of file transfer failures impossible to catch however, and should
# only be disabled if your sftp version has problems with batch mode
#sftp_batch_mode = False

# The -tt argument is passed to ssh when pipelining is not enabled because sudo
# requires a tty by default.
#usetty = True

# Number of times to retry an SSH connection to a host, in case of UNREACHABLE.
# For each retry attempt, there is an exponential backoff,
# so after the first attempt there is 1s wait, then 2s, 4s etc. up to 30s (max).
#retries = 3
[persistent_connection]

# Configures the persistent connection timeout value in seconds.  This value is
# how long the persistent connection will remain idle before it is destroyed.
# If the connection doesn't receive a request before the timeout value
# expires, the connection is shutdown. The default value is 30 seconds.
#connect_timeout = 30

# The command timeout value defines the amount of time to wait for a command
# or RPC call before timing out. The value for the command timeout must
# be less than the value of the persistent connection idle timeout (connect_timeout)
# The default value is 30 second.
#command_timeout = 30

[accelerate]
#accelerate_port = 5099
#accelerate_timeout = 30
#accelerate_connect_timeout = 5.0

# The daemon timeout is measured in minutes. This time is measured
# from the last activity to the accelerate daemon.
#accelerate_daemon_timeout = 30

# If set to yes, accelerate_multi_key will allow multiple
# private keys to be uploaded to it, though each user must
# have access to the system via SSH to add a new key. The default
# is "no".
#accelerate_multi_key = yes

7. Section [selinux]

[selinux]
# file systems that require special treatment when dealing with security context
# the default behaviour that copies the existing context or uses the user default
# needs to be changed to use the file system dependent context.
#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p,vfat

# Set this to yes to allow libvirt_lxc connections to work without SELinux.
#libvirt_lxc_noseclabel = yes

8. Sections [colors] et [diff]

[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan
[diff]
# Always print diff when running ( same as always running with -D/--diff )
# always = no

# Set how many context lines to show in diff
# context = 3

9. Plugins de connexion

Connection Plugins

Les plug-ins de connexion permettent à Ansible de se connecter aux hôtes cibles afin d’exécuter des tâches sur ceux-ci. Ansible est livré avec de nombreux plugins de connexion, mais un seul peut être utilisé par hôte à la fois. Les plus utilisés sont les types de connexion Paramiko SSH, ssh natif (appelé simplement ssh) et local.

On peut utiliser ansible-doc -t connection -l pour voir la liste des plugins disponibles. ansible-doc -t <nom du plug-in> permet d’afficher une documentation détaillée et des exemples.

Par défaut, c’est le plugion de connexion ssh sui est utilisé. Il se définit plus finement avec ses paramètres adaptés comme variable de l’hôte lui-même.

10. Automation système Linux

Voici un exemple de fichier de configuration utilisé dans le cadre de l’automation de serveurs Linux :

cat << EOF >> ansible.cfg
[defaults]
inventory = ./inventory.ini
host_key_checking = False
private_key_file = /root/.ssh/id_rsa
callback_whitelist = profile_tasks
forks = 20
#strategy = free
gathering = explicit
become = True
[callback_profile_tasks ]
task_output_limit = 100
EOF

On remarquera les paramètres suivants du fichier ansible.cfg :

  1. Ansible Configuration Settings 

Glossaire Ansible

Glossaire Ansible

Glossaire Ansible.

Action

Une action fait partie d’une tâche qui précise les modules à exécuter et les arguments à transmettre à ce module. Chaque tâche ne peut avoir qu’une seule action, mais elle peut aussi avoir d’autres paramètres.

Ad Hoc

Désigne l’exécution d’Ansible pour exécuter une commande rapide, en utilisant /usr/bin/ansible, plutôt que le langage d’orchestration, qui est /usr/bin/ansible-playbook. Un exemple de commande ad hoc pourrait être le redémarrage de 50 machines dans votre infrastructure. Tout ce que vous pouvez faire ad hoc peut être accompli en écrivant un livre de jeu et les livres de jeu peuvent également cumuler beaucoup d’autres opérations ensemble.

Async

Désigne une tâche qui est configurée pour être exécutée en arrière-plan plutôt que d’attendre l’achèvement. Si vous avez un long processus qui durerait plus longtemps que le délai d’attente SSH, il serait logique de lancer cette tâche en mode asynchrone. Les modes Asynchrones peuvent être interrogés toutes les secondes ou peuvent être configurés pour “tirer et oublier”, auquel cas Ansible ne vérifiera même pas à nouveau la tâche ; il la déclenchera simplement et passera aux étapes suivantes. Les modes asynchrones fonctionnent avec /usr/bin/ansible et /usr/bin/ansible-playbook.

Callback Plugin

Désigne un code écrit par l’utilisateur qui peut intercepter les résultats d’Ansible et en faire quelque chose. Quelques exemples fournis dans le projet GitHub permettent d’effectuer des enregistrements personnalisés, d’envoyer des courriels ou même de jouer des effets sonores.

Check Mode

Désigne l’exécution d’Ansible avec l’option --check, qui n’apporte aucune modification sur les systèmes distants, mais n’affiche que les modifications qui peuvent survenir si la commande s’exécute sans ce drapeau. Ceci est analogue aux modes dits “marche à vide” dans d’autres systèmes, mais l’utilisateur doit être averti que ceci ne tient pas compte des défaillances inattendues des commandes ou des effets en cascade (ce qui est vrai pour des modes similaires dans d’autres systèmes). Utilisez ceci pour vous faire une idée de ce qui pourrait arriver, mais ne le remplacez pas par un bon environnement de mise en scène.

Plugin de connexion

Par défaut, Ansible communique avec les machines distantes via des bibliothèques enfichables. Ansible supporte OpenSSH (SSH (Native)) ou une implémentation Python appelée paramiko. OpenSSH est préféré si vous utilisez une version récente, et permet également certaines fonctionnalités comme Kerberos et les hôtes de saut. Ceci est couvert dans la section Mise en route. Il existe aussi d’autres types de connexion comme le mode accéléré, qui doit être amorcé sur l’un des types de connexion basés sur SSH mais qui est très rapide, et le mode local, qui agit sur le système local. Les utilisateurs peuvent également écrire leurs propres plugins de connexion.

Conditionals

Un conditionnel est une expression qui évalue à vrai ou faux qui décide si une tâche donnée est exécutée sur une machine donnée ou non. Les conditionnels d’Ansible sont alimentés par l’instruction ‘when:’.

Déclaratif

Une approche pour réaliser une tâche qui utilise une description de l’état final plutôt qu’une description de la séquence des étapes nécessaires pour atteindre cet état. Pour un exemple du monde réel, une spécification déclarative d’une tâche serait : “mettez-moi en Californie”. Selon votre emplacement actuel, la séquence des étapes pour vous rendre en Californie peut varier, et si vous êtes déjà en Californie, rien ne doit être fait du tout. Les Ressources d’Ansible sont déclaratives ; elles déterminent les étapes nécessaires pour atteindre l’état final. Il vous permet également de savoir si des mesures doivent être prises ou non pour arriver à l’état final.

Mode Diff

Un drapeau --diff peut être passé à Ansible pour montrer ce qui a changé sur les modules qui le supportent. Vous pouvez le combiner avec --check pour obtenir un bon “test”. Les diffs de fichiers sont normalement dans un format de diff unifié.

Executor

Un composant logiciel de base d’Ansible qui est la puissance derrière /usr/bin/ansible directement - et correspond à l’invocation de chaque tâche dans un playbook. L’exécuteur est quelque chose dont les développeurs d’Ansible peuvent parler, mais ce n’est pas vraiment dans le vocabulaire de l’utilisateur commun.

Facts

Les “facts” sont simplement des choses que l’on découvre au sujet des nœuds distants. Bien qu’ils puissent être utilisés dans des livres de jeu et des modèles tout comme les variables, les “facts” sont des choses qui sont déduites, plutôt que définies. Les “facts” sont automatiquement découverts par Ansible lors de l’exécution d’une lecture de livre de jeu en exécutant le module de configuration interne sur les nœuds distants. Il n’est jamais nécessaire d’appeler le module “setup” explicitement, il s’exécute simplement, mais il peut être désactivé pour gagner du temps s’il n’est pas nécessaire ou vous pouvez dire à ansible de ne collecter qu’un sous-ensemble des faits complets via l’option gather_subset:. Pour la commodité des utilisateurs qui passent d’autres systèmes de gestion de configuration, le module factuel tirera également des faits des outils ohai et facter s’ils sont installés. Ce sont des bibliothèques de “facts” de Chef et Puppet, respectivement. (Ceux-ci peuvent aussi être désactivés via gather_subset:)

Plugin de filtrage

Un plugin de filtrage est quelque chose que la plupart des utilisateurs n’auront jamais besoin de comprendre. Ceux-ci permettent la création de nouveaux filtres Jinja2, qui ne sont plus ou moins utiles qu’aux personnes qui savent ce que sont les filtres Jinja2. Si vous en avez besoin, vous pouvez apprendre à les écrire dans la section documentation de l’API.

Forks

Il est possible de dialoguer en parallèle avec des nœuds distants et de définir le niveau de parallélisme soit en passant --forks à la commande ansible, soit en éditant la valeur par défaut dans un fichier de configuration. La valeur par défaut est une fourche à cinq (5) forks très conservatrice. Si vous avez beaucoup de RAM, vous pouvez facilement régler cette valeur à une valeur comme 50 pour un parallélisme accru.

Gather Facts (booléen)

Les “facts” ont été mentionnés ci-dessus. Parfois, lors de l’exécution d’un livre de jeu multi-jeux, il est souhaitable d’avoir quelques jeux qui ne collectent pas de “facts” car ils n’en n’ont pas besoin. Définir gather_facts: False dans un jeu permet de sauter cette collecte implicite de faits.

Globbing

Le “Globbing” est un moyen de sélectionner un grand nombre d’hôtes en se basant sur des caractères génériques, plutôt que de le faire sur le nom de l’hôte en particulier, ou le nom du groupe dans lequel ils se trouvent. Par exemple, il est possible de sélectionner ww* pour faire correspondre tous les hôtes en commençant par www. Ce concept est tiré directement de Func, l’un des projets précédents de Michael DeHaan (Ansible Founder).

Groupe d’inventaire

Un groupe d’inventaire se compose de plusieurs hôtes assignés à un pool qui peuvent être facilement ciblés ensemble, ainsi que de variables données qu’ils partagent en commun.

Group Vars

Les dossier group_vars/ comprend des fichiers qui vivent dans un répertoire à côté d’un fichier d’inventaire, avec un nom de fichier facultatif nommé du nom de chaque groupe. C’est un endroit pratique pour placer les variables qui sont fournies à un groupe donné, en particulier les structures de données complexes, de sorte que ces variables n’ont pas à être intégrées dans le fichier d’inventaire ou dans le livre de jeu.

Handlers

Les “Handlers” sont comme les tâches régulières dans un playbook Ansible (voir Tâches) mais ne sont exécutés que si la tâche contient une directive notify et également si elle indique qu’elle a changé quelque chose. Par exemple, si un fichier de configuration est modifié, la tâche faisant référence à l’opération de modélisation du fichier de configuration peut avertir un gestionnaire (“handler”) de redémarrage du service. Les gestionnaires peuvent être utilisés à d’autres fins que le redémarrage du service, mais le redémarrage du service est l’utilisation la plus courante.

Hôte (Host)

Un hôte est simplement une machine distante qu’Ansible gère. Des variables individuelles peuvent leur être affectées et elles peuvent également être organisées en groupes. Tous les hôtes ont un nom d’accès (adresse IP ou nom de domaine) et, facultativement, un numéro de port, s’ils ne sont pas accessibles sur le port SSH par défaut.

Host Specifier

Chaque jeu dans Ansible applique une série de tâches (qui définissent le rôle, le but ou les ordres d’un système) à un ensemble de systèmes. hosts: est la directive dans chaque jeu qui est souvent appelée le spécificateur hosts et qui configure ce comportement.

Il peut sélectionner un système, plusieurs systèmes, un ou plusieurs groupes, ou même certains hôtes qui sont dans un groupe et explicitement pas dans un autre.

Host Vars

Tout comme les “Group Vars”, un répertoire à côté du fichier d’inventaire nommé host_vars/ peut contenir un fichier nommé d’après chaque nom d’hôte du fichier d’inventaire, au format YAML. Ceci fournit un endroit pratique pour assigner des variables à l’hôte sans avoir à les intégrer dans le fichier d’inventaire. Le fichier “Host Vars” peut également être utilisé pour définir des structures de données complexes qui ne peuvent pas être représentées dans le fichier d’inventaire.

Idempotence

Une opération est idempotente si le résultat de son exécution unique est exactement le même que le résultat de son exécution répétée sans aucune action intermédiaire.

Includes

L’idée que les fichiers de livre de jeu (qui ne sont rien de plus que des listes de jeux) peuvent inclure d’autres listes de jeux, et les listes de tâches peuvent être externalisées dans des listes de tâches contenues dans d’autres fichiers, et de même avec des “handlers”. Les “Includes” peuvent être paramétrés, ce qui signifie que le fichier chargé en mémoire peut passer des variables. Par exemple, un jeu inclus pour créer un blog WordPress peut prendre un paramètre appelé utilisateur et ce jeu peut être inclus plus d’une fois pour créer un blog pour alice et bob.

Inventaire

Un fichier (par défaut, Ansible utilise un format INI simple) qui décrit les hôtes et les groupes dans Ansible. L’inventaire peut également être fourni via un script d’inventaire (parfois appelé “External Inventory Script”).

Script d’inventaire

Un programme très simple (ou compliqué) qui recherche des hôtes, leur appartenance à un groupe d’hôtes et des informations variables à partir d’une ressource externe - que ce soit une base de données SQL, une solution CMDB, ou quelque chose comme LDAP. Ce concept a été adapté de Puppet (où il est appelé “External Nodes Classifier”) et fonctionne plus ou moins exactement de la même manière.

Jinja2

Jinja2 est le langage de template préféré du module de template d’Ansible. C’est un langage de template Python très simple, généralement lisible et facile à écrire.

JSON

Ansible utilise JSON pour les données de retour des modules distants. Cela permet aux modules d’être écrits dans n’importe quel langage, pas seulement en Python.

Lazy Evaluation

En général, Ansible évalue toutes les variables du contenu du livre de jeu à la dernière seconde, ce qui signifie que si vous définissez une structure de données, cette structure de données elle-même peut définir des valeurs de variables en son sein, et tout “fonctionne” comme vous pouvez vous y attendre. Cela signifie également que les chaînes de caractères variables peuvent inclure d’autres variables à l’intérieur de ces chaînes.

Library

Une collection de modules mis à disposition de /usr/bin/ansible ou d’un playbook Ansible.

Limit Groups

En passant --limit somegroup aux commandes ansible ou ansible-playbook, leur exécution peut être limitée à un sous-ensemble d’hôtes. Par exemple, ceci peut être utilisé pour exécuter un livre de jeu qui cible normalement un ensemble entier de serveurs vers un serveur particulier.

Local Action

Une directive local_action dans un livre de jeu ciblant des machines distantes signifie que l’étape donnée se produira effectivement sur la machine locale, mais que la variable {{ ansible_hostname }} peut être passée pour référencer le nom d’hôte distant auquel elle fait référence. Ceci peut être utilisé pour déclencher, par exemple, une opération rsync ou encore approvisionner des hôtes d’inventaire qui n’existent pas déjà auprès d’un fournisseur en nuage.

Local Connection

En utilisant la connexion: local dans un jeu, ou en passant -c local à la commande /usr/bin/ansible, cela indique que nous gérons l’hôte local et non une machine distante.

Plugin de recherche

Un plugin de recherche est un moyen d’obtenir des données dans Ansible à partir du monde extérieur. Les plugins de recherche sont une extension de Jinja2 et peuvent être appelés à partir des modèles, par exemple, {{research('file','/path/to/file') }}.

Loops

Ansible n’est pas un langage de programmation. Il préfère être plus déclaratif, bien que diverses constructions comme la boucle permettent de répéter une tâche particulière pour plusieurs éléments d’une liste. Certains modules, comme yum et apt, prennent en fait les listes directement, et peuvent installer tous les paquets donnés dans ces listes en une seule transaction, ce qui accélère considérablement le temps total de configuration, de sorte qu’ils peuvent être utilisés sans boucle.

Modules

Les modules sont les unités de travail qu’Ansible expédie aux machines distantes. Les modules sont lancés par /usr/bin/ansible ou /usr/bin/ansible-playbook (où plusieurs tâches utilisent beaucoup de modules différents en même temps). Les modules peuvent être implémentés dans n’importe quel langage, y compris Perl, Bash, ou Ruby - mais peuvent tirer parti d’un code de bibliothèque communautaire utile si il est écrit en Python. Les modules n’ont qu’à retourner JSON. Une fois que les modules sont exécutés sur des machines distantes, ils sont supprimés, de sorte qu’aucun démon de longue durée n’est utilisé. Ansible fait référence à la collection de modules disponibles en tant que bibliothèque.

Multi-Tier

Multi-Tier est le concept selon lequel les systèmes informatiques ne sont pas gérés un système à la fois, mais par des interactions entre plusieurs systèmes et des groupes de systèmes dans des ordres bien définis. Par exemple, un serveur Web peut avoir besoin d’être mis à jour avant un serveur de base de données et des éléments sur le serveur Web peuvent avoir besoin d’être mis à jour après. Ansible modélise l’ensemble des topologies et des workflows informatiques plutôt que d’envisager la configuration dans une perspective “un système à la fois”.

Notify

L’acte d’une tâche enregistrant un événement de changement et informant un gestionnaire (“handler”) de tâche qu’une autre action doit être exécutée à la fin du jeu. Si un “handler” est notifié par plusieurs tâches, il ne sera exécuté qu’une seule fois. Les gestionnaires sont exécutés dans l’ordre dans lequel ils sont listés, et non dans l’ordre dans lequel ils sont notifiés.

Orchestration

De nombreux systèmes logiciels d’automatisation utilisent ce mot pour signifier différentes choses. Ansible l’utilise comme chef d’orchestre pour diriger un orchestre. L’architecture d’un centre de données ou d’un cloud est pleine de nombreux systèmes, jouant de nombreux rôles - serveurs web, serveurs de bases de données, peut-être des équilibreurs de charge, des systèmes de surveillance, systèmes d’intégration continue, etc. Dans l’exécution de tout processus, il est nécessaire de toucher les systèmes avec des commandes particulières, souvent pour simuler des mises-à-jour continues ou pour déployer le logiciel correctement. Certains systèmes peuvent effectuer certaines étapes, puis d’autres d’autres étapes, puis les systèmes précédents déjà traités peuvent avoir besoin d’effectuer d’autres étapes. En cours de route, il peut être nécessaire d’envoyer des courriels ou de communiquer avec des services Web. Une orchestration possible consiste à modéliser ce genre de processus.

Paramiko

Par défaut, Ansible gère les machines via SSH. La bibliothèque qu’Ansible utilise par défaut pour ce faire est une bibliothèque alimentée par Python appelée paramiko. La librairie paramiko est généralement rapide et facile à gérer, bien que les utilisateurs désirant la prise en charge de Kerberos ou de Jump Host puissent passer à un binaire SSH natif tel que OpenSSH en spécifiant le type de connexion dans leurs playbooks, ou en utilisant le drapeau -c ssh.

Livres de jeux

Les Livres de jeux (playbooks) sont la langue par laquelle Ansible orchestre, configure, administre ou déploie les systèmes. On les appelle en partie des playbooks parce que c’est une analogie sportive, et c’est censé être amusant de les utiliser. Ce ne sont pas des cahiers d’exercices :)

Jeux (Plays)

Un livre de jeu est une liste de jeux. Un jeu est au minimum une correspondance entre un ensemble d’hôtes sélectionnés par un spécificateur d’hôte (généralement choisis par des groupes mais parfois par des globes de nom d’hôte) et les tâches qui s’exécutent sur ces hôtes pour définir le rôle que ces systèmes vont jouer. Il peut y avoir un ou plusieurs jeu dans un livre de jeu.

Mode Pull (traction)

Par défaut, Ansible fonctionne en mode “push”, ce qui lui permet de contrôler très finement le moment pendant lequel il parle à chaque système. Le mode Pull est prévu lorsque vous préférez que les nœuds vérifient un livre de jeu toutes les N minutes sur un horaire particulier. Il utilise un programme appelé ansible-pull et peut également être configuré (ou reconfiguré) à l’aide d’un playbook en mode “push”. La plupart des utilisateurs d’Ansible utilisent le mode “push”, mais le mode “pull” est inclus pour la variété et le fait d’avoir des alternatives.

ansible-pull fonctionne en vérifiant les commandes de configuration git avec un crontab puis en gérant la machine localement, en utilisant le plugin de connexion local.

Mode Push

Le mode “Push” est le mode par défaut d’Ansible. En fait, ce n’est pas vraiment un mode du tout - c’est juste la manière dont Ansible fonctionne quand on n’y pense pas. Le mode “push” permet à Ansible d’être finement granulé et de conduire les nœuds à travers des processus d’orchestration complexes sans attendre qu’ils s’enregistrent.

Register Variable

Le résultat de l’exécution d’une tâche dans Ansible peut être stocké dans une variable pour être utilisé dans un modèle ou une instruction conditionnelle. Le mot-clé utilisé pour définir la variable est appelé register, prenant son nom de l’idée de registres dans la programmation en Assembleur. Il existe un nombre infini de noms de variables que vous pouvez utiliser pour l’enregistrement.

Modèle de ressources

Les modules possibles fonctionnent en termes de ressources. Par exemple, le module file sélectionnera un fichier particulier et s’assurera que les attributs de cette ressource correspondent à un modèle particulier. Par exemple, nous pourrions souhaiter changer le propriétaire de /etc/motd à root s’il n’est pas déjà défini à root, ou définir son mode à 0644 s’il n’est pas déjà défini à cette valeur. Les modèles de ressources sont idempotents, ce qui signifie que les commandes de changement ne sont exécutées que si nécessaire, et Ansible ramènera le système à l’état désiré quel que soit l’état réel - plutôt que vous ayez à lui dire comment atteindre cet état.

Rôles

Les rôles sont des unités d’organisation dans Ansible. Attribuer un rôle à un groupe d’hôtes (ou un ensemble de groupes, ou des modèles d’hôtes, etc.) implique qu’ils doivent mettre en œuvre un comportement spécifique. Un rôle peut inclure l’application de certaines valeurs de variables, de certaines tâches et de certains gestionnaires (“handlers”) - ou simplement une ou plusieurs de ces choses. En raison de la structure de fichier associée à un rôle, les rôles deviennent des unités redistribuables qui vous permettent de partager leur comportement entre les playbooks - ou même avec d’autres utilisateurs.

Mise à jour continue (Rolling Updates)

Le fait d’adresser un certain nombre de nœuds d’un groupe N à la fois pour éviter de les mettre à jour tous en même temps et de mettre le système hors ligne. Par exemple, dans une topologie Web de 500 nœuds traitant un très grand volume, il peut être raisonnable de mettre à jour 10 ou 20 machines à la fois, puis de passer aux 10 ou 20 suivantes une fois terminé. serial : le mot-clé dans un Ansible playbooks contrôle la taille du pool de mise à jour mobile. La valeur par défaut est d’adresser la taille du lot d’un seul coup, c’est donc quelque chose que vous devez choisir. La configuration du système d’exploitation (comme s’assurer que les fichiers de configuration sont corrects) n’a généralement pas besoin d’utiliser le modèle de mise à jour continue, mais peut le faire si nécessaire.

Become

Ansible ne nécessite pas de connexion root, et puisqu’il est sans démon, il n’a certainement pas besoin de démons de niveau root (ce qui peut être un problème de sécurité dans les environnements sensibles). Ansible peut se connecter et effectuer de nombreuses opérations enveloppées dans une commande sudo, et peut travailler à la fois avec sudo sans mot de passe et avec sudo avec mot de passe. Certaines opérations qui ne fonctionnent pas normalement avec sudo (comme le transfert de fichiers scp) peuvent être réalisées avec les modules copytemplate et fetch d’Ansible en mode sudo.

SSH (Native)

Native OpenSSH en tant que transport Ansible est spécifié avec -c ssh (ou dans un fichier de configuration, ou avec une directive dans le playbook) et peut être utile si vous voulez vous connecter via “Kerberized SSH” ou en utilisant les hôtes “SSH jump”, etc.

Tags

Ansible permet de baliser les ressources d’un livre de jeu avec des mots-clés arbitraires, puis d’exécuter uniquement les parties du livre de jeu qui correspondent à ces mots-clés. Par exemple, il est possible d’avoir une configuration d’OS complète, et d’avoir certaines étapes appelées ntp, puis d’exécuter uniquement les étapes ntp pour reconfigurer les informations du serveur de temps sur un hôte distant.

Tâche

Les playbooks existent pour exécuter des tâches. Les tâches combinent une action (un module et ses arguments) avec un nom et éventuellement d’autres mots-clés (comme des directives de bouclage). Les gestionnaires (“handlers”) sont également des tâches, mais il s’agit d’un type particulier de tâches qui ne s’exécutent que si elles sont notifiées par leur nom lorsqu’une tâche signale un changement sous-jacent sur un système distant.

Modèles Jinja2

Ansible peut facilement transférer des fichiers vers des systèmes distants mais il est souvent souhaitable de substituer des variables dans d’autres fichiers. Les variables peuvent provenir du fichier d’inventaire, des variables d’hôte, des variables de groupe ou des faits. Les modèles utilisent le moteur de modèles Jinja2 et peuvent aussi inclure des constructions logiques comme des boucles et des instructions if.

Transport

Ansible utilise la terme “Connection Plugins” pour définir les types de transports disponibles. C’est simplement de cette façon qu’Ansible s’adressera aux systèmes gérés. Les transports inclus sont paramiko, ssh (utilisant OpenSSH), et local.

When

Une instruction conditionnelle facultative attachée à une tâche qui est utilisée pour déterminer si la tâche doit être exécutée ou non. Si l’expression suivant le mot-clé when: évalue à faux, la tâche sera ignorée.

Vars (Variables)

Contrairement aux “facts”, les variables sont des noms de valeurs (il peut s’agir de simples valeurs scalaires - entiers, booléens, chaînes de caractères) ou des valeurs complexes (dictionnaires/matières, listes) qui peuvent être utilisées dans des modèles et des livres de jeu. Ce sont des choses déclarées, et non des choses qui sont déduites de l’état ou de la nature actuelle du système distant (ce qui est ce que sont les faits).

YAML

Ansible ne veut pas forcer les gens à écrire du code en langage de programmation pour automatiser l’infrastructure, donc Ansible utilise YAML pour définir les langages de configuration des livres de jeu et aussi les fichiers variables. YAML est agréable parce qu’il a un minimum de syntaxe et qu’il est très propre et facile à lire pour les personnes. C’est un bon format de données pour les fichiers de configuration et les humains, mais aussi pour la lecture automatique. L’utilisation de YAML par Ansible provient de la première utilisation de YAML par Michael DeHaan à l’intérieur de Cobbler vers 2006. YAML est assez populaire dans la communauté des langages dynamiques et le format dispose de bibliothèques disponibles pour la sérialisation dans de nombreux langages de programmation (Python, Perl, Ruby, etc.).