Salt : Pimenter votre DevOps

Aujourd’hui, il est possible de déployer et administrer quelques serveurs manuellement. Toutefois, comment gérer une expansion conséquente de parc pouvant ajouter une dizaine de serveurs ou plus dans les tâches récurrentes d’administration ?

Il est alors nécessaire d’industrialiser la gestion de l’infrastructure et tendre vers une infrastructure orientée “développement”. C’est ce que permettent les outils de gestion de configuration, offrant des capacités de déploiement et de gestion d’un parc informatique.

Relativement nouveau dans le domaine, SaltStack, souvent appelé “Salt”, est aujourd’hui un incontournable et puissant outil de gestion de configuration Open-Source implémenté en Python.

Revenons sur les origines : le DevOps

Le terme DevOps est apparu en 2009 (première apparition sur Twitter en 2010). Le DevOps, plus qu’un terme sans définition précise, est un mouvement visant à rapprocher 2 visions souvent opposées, celle des “Devs” et des “Ops” :

  • Les “Devs” : toutes personnes impliquées dans la fabrication du logiciel avant la mise en production, recherchent le changement et les évolutions ;
  • Les “Ops” : pour opérationnels, responsables de l’exploitation et de la maintenance de la production, recherchent quant à eux stabilité et fiabilité.

Bien qu’ils aient le même but commun de satisfaction des utilisateurs, leur vision est opposée.

Le DevOps vise à une collaboration étroite des “Devs” et “Ops”, afin de tendre à une pratique où chacun participe à l’intégralité du cycle de vie, de la conception jusqu’au support.

Le DevOps est né de la popularité du développement agile, source entre autre d’une croissance importante du nombre de déploiements et du besoin de réactivité permettant de réduire drastiquement le “Time To Market”. Il s’agit d’une véritable culture de fond et non la simple utilisation d’outils comme on peut l’entendre souvent.

Les outils de gestion de configuration comme SaltStack se positionnent dans la chaine du DevOps en offrant des moyens de déploiement et gestion de l’infrastructure. Ces outils permettent d’industrialiser le déploiement et l’exploitation, et ainsi tendre vers une infrastructure orientée “développement”.

SaltStack

Salt a été initialement architecturé et développé par Thomas Hatch, actuel CTO de la société SaltStack. La première version de Salt est publiée en 2011, ce qui en fait un des acteurs les plus récents du domaine. Les solutions concurrentes et jusqu’alors plus connues étaient Puppet, Chef et dans une moindre mesure, Ansible.

Fort d’une communauté très importante et active, Salt a su évoluer et se positionne aujourd’hui comme un des leaders des outils de gestion de configuration. La liste de ses nombreux utilisateurs (SAP, SKY, Orange, LinkedIn, HP, NASA, Adobe, etc.), et de ses récompenses (https://saltstack.com/awards/) le confirme.

Depuis quelques années, Salt fournit deux versions distinctes. La première nommée “Salt O.S.S” est open source sous licence Apache 2.0 et la seconde est une version commerciale nommée “SaltStack Enterprise” ajoutant quelques fonctionnalités dont une interface graphique. Cet article ne traite que de la version Open Source.

Depuis 2014, le cycle des releases est semestriel, et à date d’écriture de cet article, la version la plus récente, utilisée dans les exemples ci-dessous, est la 2017.7.

Les Fondements

Salt est un outil puissant de gestion de la couche infrastructure, offrant les capacités d’exécution à distance, d’orchestration et plus globalement de gestion de configuration. Salt est entièrement écrit en Python, il est modulaire et facilement extensible par des développements spécifiques. Il a pour avantage d’avoir une syntaxe claire et simple et présente de meilleures performances que certaines solutions concurrentes. Pour cela, il s’appuie nativement sur des fichiers de données écrit en YAML (langage paramétrable en JSON…) ainsi que sur le langage de Templating Jinja2 qui permet de rendre dynamique, entre autre, le contenu des différents YAML que l’on pourra écrire sous Salt. Commençons par une présentation de ces deux langages incontournable sous Salt.

Le YAML

YAML est un langage simple de représentation de données structurées. Exemple :

Plus de détail concernant YAML dans Salt ici.

Le Jinja2

Jinja2 est un langage permettant de transformer de simple fichier plat en template. Interprété par Jinja, ces templates permettent la génération de fichier dont le contenu s’adapte en fonction du contexte, de variable, etc.

La syntaxe Jinja est la suivante :

  • Bloc de commande : {% ... %}  ;
    • Définition de variable : {% set variable = 'valeur' %}  ;
    • Conditions, boucles, etc. ;
    • Ces blocs disparaissent suite à interprétation ;
  • Bloc commentaire : {# ... #}  ;
    • Ces blocs disparaissent suite à interprétation ;
  • Bloc de restitution de variable : {{ ... }}  ;
    • Ces blocs sont remplacés dans le fichier final par les valeurs des variables indiquées.

Exemple :

Plus de détail concernant Jinja2 dans Salt ici.

Master/Minions

Salt fonctionne en mode maitre (Master)/esclave (Minion):

  • Le serveur Salt maitre est appelé Master (obligatoirement sous Linux);
  • Tous les serveurs administrés sont des Minions.

Les Minions fonctionnent avec l’ajout d’un agent Salt, facilement intégrable sur les principaux OS (Debian, Ubuntu, Windows Servers, RedHat, CentOS…). Il est cependant possible de se passer d’agent sur les systèmes Linux. Il est de même possible d’avoir plusieurs Master.

Pour faciliter la compréhension des notions essentielles de Salt, la suite de l’article s’appuiera sur des exemples réalisés depuis un environnement de démo offrant 1 master et 2 minions sous Ubuntu, mais nous ne rentrerons pas dans le détail de chaque syntaxe présentée. Très simple à mettre en place, cet environnement est mis à disposition par SaltStack et basé sur Vagrant/VirtualBox : https://docs.saltstack.com/en/getstarted/fundamentals/. En quelques minutes, cet environnement vous permet de manipuler Salt, et découvrir ses capacités et facilités d’usage. Alors n’hésitez pas, tester le par vous-même !

Les échanges Master/Minions

Le principe est simple : le master envoie ses instructions aux minions pour leur indiquer les actions à réaliser. Salt s’appuie sur ZeroMQ pour gérer les échanges de messages entre le master et les minions. Les échanges sont donc simples:

  • Envoi d’instructions par le master (SUB) ;
  • Réponse du Minion (REP) ;
  • Publications (PUB) d’informations à l’initiative du Minion (ex : au démarrage d’un serveur, il informe son master par une publication…) :

Les communications entre le master et les minions sont sécurisées et chiffrées en AES.

Au démarrage les minions se présentent à leur master (La seule configuration à renseigner du côté Minion est l’adresse IP ou nom DNS du master à contacter). Le Minion envoi alors sa clé publique au master.

Avec la commande ci-dessous exécutée sur le master, nous pouvons observer la réception des deux clés, non validées pour le moment, des deux serveurs minion1 et minion2 :

Il faut alors valider les clés depuis le master, avant de pouvoir utiliser les 2 minions :

Les clés sont utilisées initialement lors de la connexion, puis, la communication continue en chiffrement symétrique pour de meilleures performances.

Introduction aux notions de Salt

Voici les notions essentielles de Salt qui seront détaillées par la suite :

  • Module : Librairie de fonctions d’exécution et de requêtage ;
  • State : Fichier décrivant les opérations nécessaires pour mettre un Minion dans un état donné ;
  • Pillar : Dictionnaire des variables ;
  • Grain : Information statique que retournent les minions (OS…).

Les Modules (Execution Module)

Dans l’ensemble des articles ou tutoriels disponibles sur Internet, le terme Module désigne ce qui s’appelle maintenant sur le site SaltStack « Execution Modules ». Dans cet article le terme Module sera de fait utilisé pour parler des « Execution Modules » (Références des modules).

Les Modules, sont des librairies de fonctions d’exécution, représentant l’ensemble des opérations que le master peut faire exécuter à ses minions. Voici quelques exemples :

  • La gestion des fichiers, tests, création, … ;
  • La gestion des paquets, installation, … ;
  • La gestion des services, arrêt/démarrage, … ;
  • La gestion de commande, lancement de commande et script, … ;
  • … La liste complète se trouve ici : Listes des Modules.

De plus, il est nativement possible d’ajouter ses propres Modules par l’intermédiaire de développements Python spécifiques. On distingue deux types de fonctions au sein des Modules, les fonctions d’interrogation et les fonctions d’actions. Une fonction d’un Module peut être lancée sur un ou plusieurs Minions depuis une ligne de commande exécutée sur le master. Cela permet depuis le Master de faire de l’exécution distante sur des Minions.

Commande :

Il existe différents autres moyens de cibler les minions avec les options –G, utilisation des grains, -E, expression régulière ou encore –L, pour une liste de noms de Minions séparés par une virgule.

Exemples

Il existe un Module file possédant de nombreuses fonctions telles que :

  • file_exists , fonction d’interrogation qui vérifie l’existence d’un fichier ;
  • touch , fonction d’action qui créé un fichier vide, entre autre.

L’essentiel des Modules sont disponibles de façon transparente pour les OS Unix et Windows. Toutefois, ils correspondent en réalité à deux modules distincts, avec une documentation distincte, et possède de fait des disparités de paramètres voir de fonctions. Par exemple, l’appel de file.touch  fonctionne aussi bien sur un Minion Unix que Windows, le chemin à passer en paramètre devant évidemment être adapté. Il y a pourtant un module file et un module win_file  exploité de façon transparente.

Exemple d’interrogation de la présence du fichier /tmp/TEST  sur nos 2 Minions avec ‘*’ :

On remarque une réponse dans un format YAML, natif à SALT (mais personnalisable). Ici, à question simple, réponse simple, pour chaque Minion, on a False indiquant que le fichier n’existe pas.

On peut donc effectuer l’action de créer ce fichier :

La création ayant fonctionné sur chaque Minion, le code retour est True .

En recontrôlant l’existence du fichier, on a bien une réponse différente des minions :

Voici un exemple de fonction d’interrogation avec un retour YAML plus fourni : récupération de la liste des paquets installés avec leurs versions :

La réponse est ici volontairement tronquée, mais ce qu’il est intéressant de voir, c’est la capacité de retourner de nombreuses informations structurés.

Les States

Dans Salt, la notion de State est certainement la plus importante. Comme expliqué précédemment, les Modules permettent d’exécuter sur des Minions des actions ou des requêtes, alors que les States définissent dans quel état doit être un Minion. En effet, l’intérêt d’un gestionnaire de configuration, comme Salt, n’est pas de pouvoir simplement faire de l’exécution distante, mais bien d’être capable de mettre un Minion dans un état donné, indépendamment de l’état de départ de celui-ci et du chemin pour y arriver.

Fichier State

On appelle States les fichiers plats possédant une extension sls, décrivant dans une syntaxe YAML un état cible applicable à un ou plusieurs Minions. Dans ces fichiers, sont indiqués une succession d’appels de fonctions avec leurs paramètres permettant de mettre un Minion dans un état cible. Pour ce faire, on exploite les “State Modules”.

Les State Modules

Les State Modules sont, comme pour les (Execution) Modules, des librairies de fonctions. Mais une fonction State est en général idempotente. Elle permet donc quel que soit l’état initial, d’obtenir le même état final. Globalement, nous retrouvons le pendant des librairies Module en librairies State (Listes des State Modules). Les fonctions States, s’appuient sur les fonctions Modules.

Prenons un exemple : Avec les Modules, nous avions une fonction permettant de créer un fichier et de tester son existence. Avec les States, nous avons une fonction indiquant qu’un fichier doit exister. Autrement dit, peu importe si le fichier existe ou non, la fonction State, en exploitant les fonctions Module de création et de test, va faire le nécessaire pour obtenir l’état souhaité : le fichier existe.

Revenons à nos fichiers State

Comme mentionné précédemment, nous faisons appel aux fonctions des “State Module” dans les fichiers State. Ces fichiers ne sont donc qu’une succession d’appels de fonctions jouées de façon séquentielle sur les minions (sauf en cas d’instruction particulière).

Exemple simple de fichier State indiquant que le fichier /tmp/TEST doit exister :

Ou encore (équivalent) :

Nous pouvons alors lancer en ligne de commande notre State sur un Minion, avec la syntaxe vue pour les Module. Ici, la fonction apply du module state permet le lancement d’un état en prenant en paramètre le nom sans extension du fichier.

Première exécution :

Initialement le fichier n’existe pas sur le minion1, il nous indique l’avoir créé, et mentionne le succès de l’opération.

Seconde exécution :

Il indique qu’il n’a fait aucun changement, le Minion étant déjà dans le bon état, et mentionne le succès de l’opération.

L’opération est bien idempotente.

Allons un peu plus loin…

Nous allons installer un paquet et mettre à jour son fichier de configuration, avec le contenu d’un modèle présent sur le master.

Notez :

  • Qu’en plaçant notre fichier State dans dossier à la racine d’un des chemins chargés par Salt (nativement /srv/salt), l’appel de cet état est dossier.vim ;
  • La dépendance entre l’installation du package, et la seconde étape de création du fichier de configuration ;
  • La capacité à l’aide de la syntaxe salt:// , d’utiliser des fichiers du Master pour mettre à jour des fichiers sur les minions (ici le fichier vimrc  est placé à la racine d’un des chemins chargés nativement par Salt).

Lancement :

Si on relance, on constate bien un résultat idempotent, avec cette fois, aucune modification, tout est déjà dans l’état souhaité :

Ordonnancement au sein des States

Sur notre premier exemple, nous avons vu que les fichiers States sont parcourus de façon séquentielle, néanmoins, cet ordre peut être modifié, structuré et conditionné par certaines exécutions avec par exemple :

  • Require : le lancement de l’étape est conditionné par le succès de l’étape indiquée ;
  • Watch : le lancement de l’étape est conditionné par la présence de modifications effectuées dans l’étape indiquée (par exemple un service n’est relancé que si l’étape de configuration de celui-ci a modifié le fichier de configuration) ;
  • Autres.

Ces conditions sont capables de réorganiser dynamiquement l’ordre d’exécution.

Premier bilan, nous disposons à ce stade de tous les éléments pour effectuer des actions, et appliquer des états sur les minions, néanmoins, tout est pour le moment relativement statique. Deux notions permettent d’introduire de la variabilité : les Grains et les Pillars.

Les Grains et Pillars

Les grains

Les Grains sont des informations, essentiellement statiques, fournies par les Minions concernant leur système (informations générales, domaine IP, mémoire…). Il est possible d’ajouter des Grains sur un Minion, afin d’y ajouter une information spécifique.

Les grains sont consultables en ligne de commande avec le Module grains :

Exemple :

L’intérêt ici ne dépasse pas le cadre de l’exemple, mais ces Grains sont exploitables directement dans les States en utilisant le langage de templating Jinja2 présenté en début d’article.

Les Pillars

Les Pillars sont des fichiers de variables écrits en YAML et stockés sur le Master avec l’extension sls . Le contenu des Pillars est transmis aux Minions, avec au besoin, la capacité d’étanchéité des données entre chaque Minion. Au niveau fonctionnement, Salt charge toutes les données des fichiers Pillar présents dans les répertoires paramétrés (par défaut /srv/pillar), et ces dernières sont directement accessibles en interrogeant la structure YAML. Ces données peuvent être exploitées au sein des States en utilisant le langage de templating Jinja2, présenté en début d’article.

Exploitation des Grains et Pillar dans les State

Illustration d’utilisation des Grains et Pillars dans un State :

Le fichier Pillar utilisé :

L’exécution de ce State permet :

  • La récupération depuis les Pillars des données sous l’arborescence YAML test:files ;
  • Le parcours de ce résultat et exécution pour chaque occurrence, de la fonction file.managed ;
    • En indiquant comme nom de fichier la valeur actuelle de la boucle ;
    • En précisant que le contenu du fichier est à interpréter en Jinja ;
    • En indiquant pour le contenu du fichier, une variable, avec appel au Grains, afin d’écrire, en fonction du Minion, un contenu de fichier différent ;
  • En résumé, d’obtenir sur le Minion les 3 fichiers attendus avec le contenu adapté.

Avec ce petit exemple, nous commençons à percevoir comment les Grains et Pillars, en s’appuyant sur du Jinja2, offrent une modularité, une capacité d’adaptation et de variabilisation dans les States, et permettent de commencer à percevoir la puissance de Salt.

Autres utilisation des Grains et Pillars

Comme expliqué précédemment, l’utilisation des Grains n’est pas seulement limitée aux States, il est également possible de les utiliser au sein de simples fichiers plats transmis aux Minions, ou encore, aux sein des Pillars, afin par exemple, de préciser en fonction de l’OS des valeurs de variables différentes.

De même, les Pillars peuvent être exploités au sein de simples fichiers plats, par exemple pour valoriser dynamiquement le contenu d’un fichier de paramètres déposé sur un Minion.

State de haut niveau

Plutôt que de gérer l’état de chaque Minion par commande sur chacun d’eux pour appliquer les States associés, il existe un State de haut niveau, le fichier top.sls. Ce fichier permet de définir quels States appliquer sur quel(s) Minion(s) :

Au même titre qu’avec les autres States, il est possible d’exploiter les Grains et Pillar afin d’adapter les States à jouer sur chaque Minion.

En lançant la commande suivante, on applique alors sur tous les Minions leurs States :

Cependant le State de haut niveau exécute les States simultanément et indépendamment sur chaque Minion, ce qui peut très vite être limitant s’il est nécessaire de maintenir une cohérence globale d’exécution.

Pour cela, nous allons introduire les « Runners » permettant de gérer les problématiques d’orchestration.

Les Runners

Les Runners sont des States s’exécutant sur le Master, faisant appel à des fonctions spécifiques : les “Runner Modules” (Liste des Runner Modules).

Les Events et Reactors

Jusqu’à maintenant, nous avons réussi à adapter nos actions en fonctions de données globalement statiques (Grains et Pillar). Mais il est possible d’aller plus loin avec les Events et Reactors. Les Events sont la capacité des Minions à émettre des évènements vers leur Master. Un certain nombre sont natifs, tels que :

  • Authentification d’un Minon (clé non encore accepté) ;
  • Démarrage/arrêt du Minion ;
  • Etc.

Les Reactors sont des Runners qui s’exécutent lors de la réception d’évènement spécifié. Exemple, dès qu’un Minion essai de s’authentifier, un Runner est déclenché pour accepter sa clé sur le Master.

Orchestration

Les “Orchestrate Runners”, appelés souvent Orchestrateurs permettent l’orchestration des actions sur les minions, en utilisant les Fonctions d’Orchestration mises à disposition. Ils permettent de décrire l’ordre d’exécution des States sur les différents Minions, et de gérer les conditions et pré-requis entre eux.

Exemple, dans un même Orchestrateur, il est possible de :

  • Attendre la demande d’authentification d’un Minion reçu par Event ;
  • Accepter sa clé Salt ;
  • Lancer un 1er State sur celui-ci ;
  • Puis lancer un redémarrage du Minion ;
  • Attendre son démarrage (Attente Event de démarrage de ce Minion) ;
  • Lancer un 2ème State sur celui-ci ;
  • Lancer un State sur 2 autres Minion ;
  • Et ainsi de suite…

Comme pour les States les étapes sont jouées séquentiellement et supportent l’utilisation des conditions entre actions ( require…). De même, l’utilisation du Jinja2 en s’appuyant sur les Pillars, permet la création de Runners dynamiques.

L’orchestration Salt est une fonctionnalité puissante permettant de gérer la majorité des cas de figures d’orchestration de déploiement.

Plus de détails sur l’orchestration ici : Orchestrate Runner.

Résumé du fonctionnement de Salt

Avant de se lancer pleinement dans la rédaction de nombreux States et Pillars, il est important de prendre le temps de définir l’arborescence des States, la structure des données dans les Pillars et la variabilisation souhaitée. Ces choix sont en effet structurant pour la suite, et complexe à modifier. Le site de Salt référence les bonnes pratiques à appliquer.

Pour conclure

Salt offre un large panel de fonctionnalités permettant de couvrir l’essentiel des besoins en termes de déploiement et d’orchestration.

Pour la gestion des cas spécifiques, Salt est pleinement personnalisable, et permet l’intégration de développements spécifiques. De même, Salt est très bien documenté, et s’appuie sur une large et très active communauté permettant l’accès à de nombreux exemples de States/Modules.

De même, la syntaxe basée sur le couple YAML/Jinja2, les nombreux tutoriels et les forums permettent une montée en compétence rapide.

Salt possède de nombreux atouts lui permettant de se faire une place sur le marché très concurrentiel des composants de gestion d’infrastructure et de configuration.

Leave a Reply

Your email address will not be published. Required fields are marked *