Encore un troll ?
Après la bataille « vi ou emacs » (tout le monde sait que « vi RuLeZ », pardonne-moi St IGNUcius), fromage ou dessert (les deux !), voici le débat « CFEngine vs Ansible ». Alors, lequel ?
CFEngine, le vénérable
CFEngine (https://cfengine.com/) a été créé en 1993. Il est écrit en C. Il permet de déployer des configurations sur tout un parc informatique hétérogène, ainsi que d’exécuter des commandes sur des serveurs.
Côté architecture, il se compose d’un module « serveur » et d’un module « client ». Le client est déployé sur toutes les machines. Le « serveur » est installé sur un ou plusieurs serveurs de déploiement (sur lesquels on installera également un client si on veut déployer de la configuration sur eux-mêmes).
La communication réseau entre le client et le serveur est optionnellement chiffrée via une configuration « openssl » implémentée directement dans le logiciel.
CFEngine utilise un langage propriétaire pour écrire les fichiers de configuration. Ce langage se base sur le principe de la vérification d’assertions, les « promesses ».
CFEngine maintient la configuration en cohérence avec le référentiel, en synchronisant régulièrement le « client » sur le « serveur » et en vérifiant régulièrement la configuration de la machine avec sa configuration théorique (toutes les 5 minutes par défaut).
Ansible, le trublion
Ansible (https://www.ansible.com/) a été créé en 2012. Il permet de déployer des configurations et d’exécuter des commandes sur un parc plus ou moins hétérogène. Il est écrit en Python.
Côté architecture, Ansible fonctionne en mode « client – serveur ». Mais là où la frontière se floute, c’est que la configuration d’Ansible est entièrement dupliquée sur chaque client quand il est déployé : il peut devenir serveur à son tour par la suite.
Ansible se base sur des scripts écrits en YAML. La programmation classique retrouve ses règles (tests, boucles…).
Ansible est prévu pour déployer des configurations de manière unitaire, i.e. une bonne fois pour toutes (l’action peut être répétée évidemment).
Mon mode de test
J’ai installé CFEngine et Ansible sur 2 serveurs Debian. J’ai écrit 4 modules :
- fichier /etc/motd
- configuration serveur SSH
- déploiement automatique de packages
- configuration serveur et client NTP
Et j’ai synchronisé les 2 machines en mode « client – serveur ».
Mes commentaires
Bootstrap
Ansible ne nécessite pas de logiciel client ou serveur, il se base sur SSH pour communiquer entre le donneur d’ordres et l’exécuteur. A contrario, CFEngine nécessite un logiciel client. Le « bootstrap » (première installation, premier démarrage) nécessite donc davantage d’efforts pour CFEngine que pour Ansible (installation d’un logiciel vs. rien).
Ça, c’est sur le papier. Dans la réalité, Ansible nécessite un utilisateur standard sur la machine cliente (pour la connexion en SSH), et cet utilisateur doit avoir le droit d’exécuter n’importe quelle commande via « sudo ». On retrouve donc finalement un bootstrap sur Ansible, mais délocalisé sur la configuration système (compte utilisateur) et « sudo ».
Lecture de l'état du client
CFEngine et Ansible récupèrent tous deux une liste de « choses » depuis la machine où ils sont exécutés en tant que client. Ces « choses » prennent un sens différent chez Ansible et CFEngine, mais représentent chez les deux protagonistes toujours la même chose : la configuration système actuelle de la machine (processes en cours d’exécution, nom de la machine, adresses IP, routage réseau…).
Par contre, là où le langage machine est très performant (CFEngine est écrit en C), le Python (Ansible) montre des signes de faiblesse et réalise cette opération en plus de 10 secondes, contre seulement 2 pour CFEngine.
Le langage du logiciel
Ansible est écrit en Python. Ce langage interprété né 4 ans après Perl ne connaît réellement de mode que depuis la fin des années 2000, là où Perl s’est imposé depuis des dizaines d’années dans l’administration systèmes. Je ne vais pas rentrer dans un nouveau troll « Python ou Perl », mais simplement constater que les sysadmins utilisent davantage Perl, là où les développeurs préfèrent souvent Python. Et Ansible est un logiciel pour réaliser un travail de sysadmin.
Le langage des modules
Ansible et CFEngine sont censés gérer un parc de serveurs hétérogènes. Mais dans les faits, on s’aperçoit vite qu’Ansible est très orienté RedHat, là où CFEngine gère tous les systèmes cibles de manière équitable (c’est bien en 2015 que RedHat a racheté Ansible, c’est ça ?).
On retrouve le même « effet de mode » du Python dans le langage utilisé par Ansible pour écrire ses module, le YAML. Mais ce langage a l’avantage d’être très clair et très lisible, contrairement à la syntaxe propriétaire de CFEngine.
Les modules de CFEngine demandent un gros d’effort d’apprentissage des notions de programmation (« bundles », « promises », « facts »)… Il faut oublier la programmation itérative, récursive ou objet, avec CFEngine : son mode de fonctionnement se rapproche davantage de la programmation par contraintes (argh… rappelle-moi ma note au partiel de Prolog…).
La manière d'écrire un module
Le YAML combiné à la puissance d’Ansible permet d’écrire tout ce qu’on veut, dans la configuration d’un module donné. L’avantage du YAML et du Python sous-jacent, c’est qu’avec un minimum de bagage en programmation procédurale, on peut écrire un module. L’inconvénient de cette puissance et de la liberté de codage, c’est qu’on peut écrire n’importe quoi : autant quelque chose de parfait, que quelque chose qui marchotte vaguement on ne sait pas bien pourquoi ni comment.
CFEngine nécessite davantage de rigueur dans l’écriture d’un module. On y passera donc davantage de temps qu’avec Ansible (je compte environ du 1 pour 2 voire du 1 pour 3). Mais une fois que le module est écrit, alors il est démontré et parfaitement fonctionnel.
La convergence vers un état fini
Ansible déploie une configuration. Point. On peut le voir comme un « super-script » Shell, Python, Perl… qui exécute des commandes les unes après les autres.
CFEngine cherche à stabiliser un état final. Il va modifier le système petit à petit pour arriver dans ce nouvel état.
J’ai lu qu’Ansible est fait pour déployer une configuration. Une bonne fois pour toutes. Il n’est pas fait pour être exécuté périodiquement et fréquemment. Là où CFEngine s’exécute toutes les 5 minutes de manière fluide, quand Ansible déploie toute sa configuration (et je ne l’ai testé qu’avec 4 modules), la charge de la machine (« load average ») prend 2 points ! La documentation d’Ansible explique qu’on ne déploie en général qu’une partie de la configuration, rarement l’ensemble des modules tous ensemble. Elle rajoute qu’une fois déployée, la configuration sur la machine cible ne bouge pas, car c’est ce que votre « gestion de la configuration » garantit (allez, ressortez vos cours ITIL, chapitre « gestion des infrastructures »).
Sur le papier, vous gérez votre configuration de manière centralisée. Avec un process de gestion de configuration, de validation de la configuration, de planification de mise en production de ladite configuration. Vous êtes peut-être même certifiés ITIL. Mais ça, c’est sur le papier.
Dans la vraie vie, on cherche à se rapprocher du papier. Mais la gestion des incidents combinée au stress de leur résolution rapide, saupoudrée d’un zeste de gestion de l’humain (vous savez, l’être humain, cette machine informatique qui ne fonctionne en général que de 9h à 18h hors WE et jours fériés…), fait en sorte que la théorie est vraie… en théorie. Sur le terrain, je suis convaincu qu’on a besoin régulièrement de s’assurer que l’état d’un serveur est toujours tel qu’il est censé être. Et ça, c’est la philosophie de CFEngine.
Ma conclusion en 2 mots
Vous voulez faire quoi, avec Ansible ou avec CFEngine ?
- Générer une image Docker, une bonne fois pour toutes, et de manière répétée grâce à Jenkins ? Choisissez Ansible.
- Vous assurer que la configuration système et réseau de votre parc informatique est en permanence dans l’état que vous avez défini ? Sautez dans le grand bain avec CFEngine.