6080 links
  • GuiGui's Show
  • Home
  • Login
  • RSS Feed
  • ATOM Feed
  • Tag cloud
  • Picture wall
  • Daily
Links per page: 20 50 100
 
  • Ansible, Chef, Puppet… Pourquoi ça ne juste marche pas :(
    « Imaginons avoir une infrastructure de notre parc informatique basée sur la virtualisation, ie. chaque service isolé dans son propre conteneur.
    A minima, on va vite se retrouver avec les conteneurs suivants : un Bind9, un Postfix, un Nginx, un Apache, un MySQL, un PostgreSQL…
    Gestion des rôles

    Tout comme dans un vrai code informatique, on voudrait profiter de mécanisme d'héritage, de polymorphisme et d'agrégation pour définir notre infrastructure, afin d'éviter la duplication et de conserver une base robuste à un changement future d'infrastructure.

    On doit pouvoir définir une description globale de tous les Apache (installation et configuration de Apache, ouverture des ports 80 et 443 du pare-feu, installation de RPaf…) et faire apparaître la notion de rôle.

    On veut quand même vouloir en redéfinir certains morceaux machine par machine (liste des virtualhosts servis, déploiement des certificats SSL…), un peu à la manière d'un héritage

    On doit pouvoir agréger les rôles, et signaler qu'un serveur Apache est aussi un serveur de courriel, par composition.
    Gestion des relations

    Tous les services d'une infrastructure sont inter-dépendants : le Nginx sert de reverse-proxy au serveur Apache, qui sert un site web qui utilise une base de données PostgreSQL, le Nginx a donc besoin de l'adresse IP du serveur Apache et des virtualhosts servis, le Apache a besoin de l'IP du serveur MySQL.
    Pire, les dépendances forment des boucles : le serveur Apache a besoin de l'IP du serveur Nginx devant lui pour configurer son module RPaf (qui permet d'avoir l'IP réelle du client dans les logs et non celle du reverse-proxy), le MySQL a besoin de l'IP du serveur Apache pour ouvrir son pare-feu…

    On a donc besoin de pouvoir définir son infrastructure d'une manière déclarative (le quoi) et non impérative (le comment), en évitant la redondance de l'information (l'IP d'une machine définie une et une seule fois), la duplication de code, le risque d'incohérence dans la configuration en cas de modification de l'infrastructure…
    Dans l'exemple précédent, on veut ne définir les IP que dans la description de l'hôte en question, et ne définir qu'à un seul endroit la relation entre un Apache et son proxy Nginx.

    Pour les relations, on aimerait même se minimiser le travail au cas où notre infrastructure soit amenée à évoluer demain, et donc pouvoir les définir de manière complexe, comme par exemple « un Nginx proxite tous les virtualhost des Apaches situés sur la même machine physique », et non se contenter de déclarer le proxy Nginx utilisé dans le descriptif du Apache, ou à l'inverse la liste de tous les Apache proxifiés dans le descriptif du Nginx (mais surtout pas les deux sous peine d'incohérence !)
    Gestion des tâches

    On doit faire le déploiement et la configuration en elle-même. Installation de paquets, édition de fichiers de configuration, création d'un utilisateur, redémarrage d'un service… L'outil doit fournir des primitives basiques et si possible avec une couche d'abstraction suffisante pour être indépendant de l'OS utilisé (on installe un paquet avec apt sous Debian mais avec yum sous Red-Hat).

    Et comme dans un langage informatique, il doit être possible de combiner ces primitives pour obtenir des résultats plus complexes. Par exemple, créer un administrateur, c'est créer un utilisateur, déployer sa clef SSH sur son compte et la déployer aussi sur le compte root.

    [...]

    Puppet : ça ne marche pas

    [...]

    Pour un service donné, l'outil fait clairement bien son job. On peut installer des paquets, déployer des fichiers tout fait ou via un template instancié à la volée avec les valeurs nécessaires. Le DSL est plutôt assez concis et donne des descriptifs faciles à lire une fois passé l'apprentissage des incantations magiques.

    Là où ça commence à pécher, c'est pour la gestion des relations… Puppet est basé sur un mécanisme client/serveur, ou plus exactement master/slave. Une machine, le puppet master, contient l'intégralité des descriptifs, rôles, fichiers et autres templates. Sur chaque machine gérée par l'outil tourne en permanence un démon puppet, le puppet agent, qui vient régulièrement vérifier s'il a des choses à faire, une fois par jour par exemple.
    Du coup, chaque machine est isolée et ne peut travailler qu'avec ce qui lui est directement destiné. Une machine n'a pas la possibilité d'aller voir ce qu'une autre machine a déployé, par exemple un Nginx ne peut pas lister les Apaches sous sa responsabilité, c'est à l'administrateur de lui indiquer explicitement la liste quelque part, ce quelque part devant obligatoirement être dans le périmètre du Nginx et pas uniquement dans celui des Apaches. Bref, c'est à l'administrateur de gérer manuellement les dépendances, sans aucune assistance de la part de l'outil pour lui dire qu'il a oublié de renseigner le nouvel Apache fraîchement arrivé dans la configuration du Nginx !
    [...]

    Pour palier à ce problème, il existe quand même un mécanisme de partage d'information, les ressources exportées.
    On veut par exemple que chaque installation d'un serveur Apache génère automatiquement une vérification Nagios sur le port 80.

    [...]

    Déjà, on vient de devoir créer une nouvelle classe de serveurs, nagios::target::apache. Pourtant, un serveur HTTP est juste un serveur HTTP. Pas un serveur HTTP + vérification du port 80. On a introduit de la redondance d'information…
    Qui dit redondance dit erreur possible. Je définis un nouveau serveur Apache dans mon infra mais je suis un jeune admin tout fraîchement démoulu de l'école ? Je n'ai pas les 10 ans d'historique et d'expérience de mes collègues ? Ben on m'a demandé d'installer un nouveau serveur Apache, donc j'ai juste mis la classe apache à ma nouvelle machine… et elle n'est donc pas monitorée par Nagios !
    Le simple fait d'avoir un port 80 en écoute devrait automatiquement entraîner le monitoring ! Parce que si l'admin sys en chef passe sous un poney demain et qu'il y a toutes les chances du monde qu'il soit le seul à avoir conscience qu'il y a un Nagios mort dans un coin, plus personne ne pensera à le faire !

    [...]

    La redondance appelant généralement à toujours plus de redondance, que se passe-t-il si on souhaite maintenant monitorer le port 80 et le port 443 ? Pas de soucis, il n'y a qu'à rajouter une vérification du port 443 dans la classe nagios::target::apache. Oh mais attendez, si mon serveur Apache n'a que du HTTP ou que du HTTPS ? Ah mais les tests Nagios vont se mettre à échouer sur le port non présent… Pas grave, il n'y a qu'à faire 2 classes, nagios::target::http et nagios::target::https et le tour est joué ! Ah mais oui, j'ai aussi un Munin tient. Ok, ben disons « munin::target::http » et « munin::target::https ».
    On voit bien le risque de fonctionner ainsi : à chaque modification de l'infrastructure, comme les dépendances ont été codées « à la main » dans l'outil, rien ne s'adapte tout seul, et toute modification devient intrusive… Un simple serveur Apache qui devrait n'avoir qu'une seule classe apache va se retrouver avec une foultitude de classes présentes uniquement dans un but technique et non métier.

    [...]

    En plus, on ne parle ici que d'une dépendance simple qui devrait en réalité s'exprimer ainsi : « j'active une vérification Nagios sur le port 80 si la machine possède la classe apache ou nginx et au moins un virtual-host sur le port 80 ».
    Imaginez maintenant devoir régler de cette manière une dépendance plus complexe de type « déploie des virtual-host nginx en reverse proxy pour chaque virtual-host de chaque Apache présent sur la même machine physique que toi, en utilisant comme IP de proxy l'IP du Apache ». Je vous tend déjà le tabouret et la corde…

    [...]

    Chef : ça ne marche pas non plus

    [...]

    Là où tout change c'est que Chef fournit de base une base de données Solr qui va stocker l'intégralité de la description d'une machine. Cette base est bien entendue requétable depuis les descriptions, ce qui permet de faire des choses assez sympathiques pour régler les problèmes précédents. [...] Par rapport à Puppet, on a aussi la notion de databags, qui permettent de séparer proprement la partie données de la partie traitement. Une telle séparation est très difficile à atteindre avec Puppet.

    [...]

    J'ai uniquement éditer les fichiers de données (databag), qui ne sont pas liés à une recette et peuvent donc contenir pas mal d'information, pour y ajouter les clefs SSH à déployer ainsi que les IP autorisées pour les accès SSH.
    Et la recette ssh va utiliser exactement le même jeu de données que la recette users, pour déployer tout ça proprement et remplir les fichiers de clefs et la config SSH. Impact sur la recette users : 0 ! Impact sur les anciennes classes des serveurs : 0 ! Ce qui n'aurait très clairement pas été le cas avec du Puppet (ajout d'une ressource exportée toute moche ou refacto lourde de l'infra pour introduire des classes fantômes) ! On obtient donc des configurations bien plus claires d'un point de vue sémantique avec Chef qu'avec Puppet.
    Tout ça grâce à la primitive search, qui va interroger la base de données Solr pour récupérer l'état de la configuration. Ce truc est extrêmement utile et fait toute la différence avec Puppet, l'intégralité de la configuration est accessible depuis n'importe quelle recette, ce qui évite la duplication d'information et/ou la création de classes fantômes pour gérer les dépendances. La recette Nginx passera par search pour lister les Apache de sa machine hôte (search(:node, "apache:* AND physical:#{node[:physical]}")), ou encore la recette DNS l'utilisera de la même manière pour récupérer l'IP du DNS tournant sur le même réseau pour renseigner resolv.conf.
    Chef, vainqueur par KO…

    [...]

    Le problème avec search, c'est que les données sont publiées dans la base Solr non pas quand vous les publiez sur le serveur Chef mais après l'exécution de l'agent Chef… Et ça change tout…
    Chef utilisant un agent sur chaque machine, on n'a aucun moyen de garantir l'ordre d'exécution du déploiement. Si par malheur le Nginx passe avant les Apaches, la config vue par Nginx est l'ancienne config et ne tient pas compte des potentielles modifications introduites par l'admin ! Pire, Chef a tendance à effacer toutes les configs existantes quand un admin publie ses modifications, le prochain passage sur le Nginx va donc virer tous les virtualhost de proxy et les Apaches vont se retrouver coupés du monde !
    On pourrait résoudre ce problème en désactivant tous les agents et en exécutant le passage de Chef « à la main », via un script qui ordonne correctement les machines à déployer et s'y connecte dans l'ordre via SSH par exemple. Ça ne résoud qu'une partie du problème car dans le cas d'une boucle de dépendances (Nginx proxite les Apaches qui ont besoin de l'IP du Nginx), il faudrait passer plusieurs fois sur chaque machine et avec des recettes différentes pour parvenir à avoir le bon résultat.

    [...]

    Ansible : et ben… ça ne marche pas mieux…

    [...]

    D'abord, je n'ai pas trouvé comment définir des méta-taches, ie. une tache réutilisable qui appelle plusieurs sous-taches. Typiquement, ma tache de création d'un utilisateur (humain) est décomposable en création d'un utilisateur (UNIX) et déploiement de sa clef SSH. La création d'un administrateur est la création d'un utilisateur (humain) et déploiement de sa clef SSH pour root.

    [...]


    Bon, continuons… Les groupes maintenant… J'ai dit un peu plus haut que les variables pouvaient être héritées via les variables de groupe. Mais Ansible a un concept d'héritage somme toute assez intéressant. [...] Naïvement, j'ai voulu faire des choses comme ça. Naïvement hein, vu que c'est juste une description de ce qu'est réellement mon infra…
    Toutes mes machines qui ont du SSH doivent avoir le port SSH d'ouvert. Toutes les machines qui font du Apache doivent avoir le port HTTP et HTTPS d'ouvert, ont pour adresse mail de contact une certaine chose commune et servent leur contenu depuis /srv/www. Et enfin, toutes les machines Apache derrière le même proxy Nginx seront configurées toutes pareilles niveau RPAF.

    [...]

    Voilààààààààà… L'héritage au sens Ansibli-ien du terme, c'est « Zyva, pourquoi j'ai déjà une valeur moi ? Allez zou, fait pas suer, j'écrase ! ». Du coup, j'en suis réduit à calculer moi-même le résultat du merge que j'aurais envie d'avoir eu, et à le coller dans le hosts_var/www.yml et à supprimer tous les groups_var. Très pratique le jour où il y a un paramètre commun à changer…

    [...]

    Dans mon déploiement, le service fail2ban restart nécessaire à la prise en compte des nouvelles règles de blocage de fail2ban est en réalité fait via un service firewall restart, vu que le firewall doit déployer des règles avant celle de fail2ban. J'ai donc besoin de faire [...] Question à 10 balles : où et comment je peux faire pour mutualiser le Restart firewall et ne définir sa tache associée qu'à un seul et unique endroit (principes KISS et DRY) ? [...] Bam… Ça passe très bien à l'exécution de la tache firewall mais ça plante violemment à celle de fail2ban, comme quoi le handler est manquant. Bon ok, j'ai peut-être été un peu vache avec toi Ansible, j'vais quand même te mettre le handler dans un truc un peu plus commun et pas directement dans le role firewall…

    [...]

    Mais en fait, non, j'ai fait une erreur, je n'ai pas tous mes serveurs qui déploient du fail2ban, mais juste ceux avec ssh. Gros naïf va… [...] Wait ? Wat ‽ J'ai bien lu ? C'est une blague à ce niveau-là, non ? Elle est où la caméra ? J'vais quand même pas devoir déclarer dans chaque groupe/rôle/whatever le role common juste pour avoir des handlers qui sont déjà théoriquement communs à toutes les machines, si ?

    Bref… Pour conclure, alors que Ansible me semblait assez prometteur et intéressant sur ses concepts (agentless + ssh + DSL pas trop dégeu), je me retrouve à devoir quasiment réfléchir à la place de l'outil pour lui prémâcher tout le travail, un peu comme si vous aviez une super machine sensée faire le café mais où vous deviez la remplir avec du café déjà tout fait qu'elle n'a plus qu'à réchauffer pour servir…

    [...]

    Ansible n'est qu'un outil pour orchestrer séquentiellement des tâches d'administration et non pas un outil de gestion de configurations comme prétendent l'être chef et puppet. »
    February 25, 2015 at 11:55:58 PM UTC - permalink - https://blog.imirhil.fr/ansible-chef-puppet-pourquoi-ca-ne-juste-marche-pas.html
Links per page: 20 50 100
 
Mentions légales identiques à celles de mon blog | CC BY-SA 3.0

Shaarli - The personal, minimalist, super fast, database-free, bookmarking service by the Shaarli community