Salt, l'autre chef d'orchestre

GNU/Linux Magazine n° 166 | décembre 2013 | Emile (iMil) Heitor
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
“Devops”, “Cloud”, IaaS et j'en PaaS (oh-oh-oh la bonne blague d'entrée) et des meilleures, vous, lecteurs avides de technologies innovantes, vous n'avez pas échappé à la déferlante de buzzwords dont nous sommes victimes depuis que l'Internet est devenu une marketplace. Et finalement, si beaucoup manipulent ces buzzwords sans avoir aucune idée de ce qu'ils cachent, ils représentent une réalité tout à fait tangible qui met des parts de pizza sur la table de bon nombre d'entre nous. Véritables icônes du mouvement devops, les systèmes d'orchestration ont révolutionné la façon dont on manipule une ferme de machines; l'avènement de la virtualisation et de la transformation des serveurs physiques en “ressources” a fait naître de nouveaux besoins. Puppet et Chef font figure de pionniers dans cette sphère hyperactive mais un concurrent dont on entend beaucoup moins parler n'a pas à rougir devant ses prédécesseurs, il s'agit de Salt[1].

1. Loin de moi, bloat

Salt se distingue de ses homologues car son auteur a eu la bonne idée de ne pas construire un monstre s'appuyant sur des solutions de messagerie à la complexité douteuse mais a plutôt misé sur la librairie ZeroMQ[2], s'assurant ainsi vélocité et simplicité. Ce choix est loin d'être anodin car, sans aucun effort supplémentaire, Salt offre d'emblée des fonctionnalités de gestion de configuration et d'orchestration. Et nous allons voir que mettre le pied à l'étrier du cheval Salt n'a rien de l'insurmontable. Enfin, mais c'est évidemment très personnel, je n'ai aucune affinité avec le langage Ruby et au contraire des solutions sus-citées, Salt est écrit en python, plus doux à mes yeux. Cela peut paraître dérisoire à première vue, mais devant la forte probabilité de tentation d'écriture de modules, le langage de la solution a son importance.

Bien que jeune (Mai 2011), le projet a d'ores et déjà le soutien de noms prestigieux tels que HP, LinkedIn ou encore PayPal, se décline déjà en version commerciale et sait adresser la quasi totalité des Clouds publics et privés.

Enfin, la liste des services Libres que Salt est en mesure de gérer est impressionnante et ne cesse de grossir, tant le développement de modules est simple avec un strict minimum de connaissances en python[4].

2. Musique !

Disponible pour nombre de systèmes de paquets, vous n'aurez aucun mal à installer Salt au sein de votre système d'exploitation. Cet article se concentrera cependant sur deux plateformes, Debian GNU/Linux et NetBSD.

Le site SaltStack propose un dépôt de paquets binaires[3] à jour qu'on déclare comme d'habitude sur un système Debian :

# cat > /etc/apt/sources.list.d/salt.list << EOF
deb http://debian.saltstack.com/debian wheezy-saltstack main
EOF
# wget -q -O- "http://debian.saltstack.com/debian-salt-team-joehealy.
gpg.key" | apt-key add -
# apt-get update

Deux démons sont mis à votre disposition :

- salt-master, le serveur central où se connecteront tous les esclaves, minions dans la terminologie Salt (j'adore.)

- salt-minion, donc, des dizaines, centaines, milliers de serveurs potentiellement interrogés et contrôlés par le master.

Une fois n'est pas coutume, notre machine Debian sera un minion, aussi, nous installons le nécessaire :

# apt-get install salt-minion

Nous reviendrons à la configuration du minion un peu plus loin.

Vous l'aurez compris, notre master sera un système NetBSD qui dispose dans pkgsrc/sysutils/salt d'un paquet dont je m'assure personnellement qu'il soit à jour. Ainsi, il suffira d'invoquer pkgin de la sorte :

# pkgin in salt

Afin de disposer du nécessaire à la poursuite des opérations.

Comme d'habitude sous NetBSD, nous activons les services souhaités de la façon suivante :

# cat >> /etc/rc.conf << EOF
salt_master=YES
salt_minion=YES
EOF

Comme vous pouvez le constater, notre machine maître sera également minion car nous voulons pouvoir la piloter comme le reste de notre parc.

Afin d'indiquer au minion qui est son maître, nous éditons le fichier /usr/pkg/etc/salt/minion et plaçons les valeurs suivantes :

# hostname ou fqdn du master
master: coruscant
# identifiant du minion, par simplicité, le hostname de la machine
id: coruscant

Dans l'immédiat, aucune autre configuration n'est nécessaire, nous démarrons salt-master et salt-minion sans plus de manières :

# /etc/rc.d/salt_master start
# /etc/rc.d/salt_minion start

Nous configurerons notre minion Debian de la même façon, seul le chemin vers le fichier de configuration diffère et il faudra évidemment donner à ce dernier un id different :

# hostname ou fqdn du master
master: coruscant
# identifiant du minion
id: tatooine

On démarre le minion sur cette machine de façon classique :

# /etc/init.d/salt-minion start

Afin de contrôler notre premier minion, nous allons l'autoriser à se connecter au maître. Pour ce faire, la commande salt-key, lancée sur le master, va autoriser l'échange de clés de type AES entre notre minion et son maître. On liste en premier lieu les minions en attente d'acceptation :

$ sudo salt-key -L
Accepted Keys:
ragnos
tatooine
watto
Unaccepted Keys:
coruscant
Rejected Keys:

Et on accepte simplement l'échange de clés :

$ sudo salt-key -a coruscant

Ou pour accepter toutes les clés en attente :

$ sudo salt-key -A

À cet instant, la commande salt-key -L devrait renvoyer la liste complète des minions autorisés :

$ sudo salt-key -L
Accepted Keys:
coruscant
ragnos
tatooine
watto
Unaccepted Keys:
Rejected Keys:

Dès lors, il nous est immédiatement possible d'envoyer des commandes à nos minions. Vérifions en premier lieu ce que ces derniers nous répondent :

$ sudo salt ‘*’ test.ping
coruscant:
True
tatooine
True
ragnos:
True
watto:
True

On pourra évidemment tester un seul minion en précisant son id :

$ sudo salt ‘tatooine’ test.ping
tatooine
True

Et maintenant que nous savons que nos minions répondent présent, nous pouvons de ce pas leur faire exécuter des commandes :

$ sudo salt ‘*’ cmd.run "uname -a"
tatooine:
Linux tatooine 3.2.0-4-686-pae #1 SMP Debian 3.2.41-2 i686 GNU/Linux
coruscant:
NetBSD coruscant 6.0 NetBSD 6.0 (XEN3_DOM0) amd64
watto:
NetBSD watto.home.imil.net 6.1_RC4 NetBSD 6.1_RC4 (GENERIC) i386
ragnos:
NetBSD ragnos 6.0 NetBSD 6.0 (RAGNOS) #2: Wed Oct 17 11:33:31 CEST
2012 root@ragnos:/usr/src/sys/arch/i386/compile/RAGNOS i386

Il ne s'agit là que des prémices de ce que permet l'orchestration via Salt, car les modules sont très nombreux. La liste complète des actions sont visibles grâce à la commande :

$ sudo salt ‘*’ sys.doc

Testons par exemple la commande pkg.install sur notre minion Debian :

$ sudo salt ‘tatooine’ pkg.install vim
tatooine:
----------
vim:
----------
new:
2:7.3.547-7
old:

Bien évidemment ici, c'est apt qui est appelé derrière la scène, mais l'action d'installation d'un paquet via Salt aurait pris la même forme vers un minion FreeBSD ou RedHat.

À cela il faut ajouter une fonctionnalité donnant accès à plus de granularité dans l'interrogation des minions: les grains, justement. Cette interface permet en particulier d'interroger les minions fonction d'une caractéristique particulière. Par exemple, si l'on voulait pinger uniquement les machines dont le système d'exploitation est NetBSD, nous ferions :

$ sudo salt -G ‘os:NetBSD’ test.ping
watto:
True
coruscant:
True
ragnos:
True

La liste des grains disponibles s'obtient via la commande :

$ sudo salt ‘*’ grains.ls

Et leurs valeurs par l'invocation suivante :

$ salt ‘*’ grains.items

Les grains sont un catalogue de données statiques, glanées au démarrage du minion et ces dernières s'étendent de façon très simple dans le fichier de configuration /usr/pkg/etc/salt/minion sous NetBSD et /etc/salt/minion sur un environnement GNU/Linux. Par exemple :

grains:
roles:
- www
- mysql
datacenter: equinix
baie: A17
chassis: 3

Nous reparlerons des grains un peu plus loin dans cet article.

3. Mes précieuuuuux fichiers...

Les suites logicielles telles que Salt adressent de façon élégante une problématique plus vaste que l'exécution de taches distantes: la gestion de la configuration.

Dressons le tableau, vous disposez d'un nombre conséquent de machines (possiblement virtuelles) pour lesquelles vous souhaiteriez :

- qu'elles soient toutes à jour,

- qu'elles soient manipulées comme un tout cohérent et non pas de façon artisanale,

- que la configuration de vos outils fétiches (vim, tmux, apt, bash, zsh, OpenSSH…) soit identique,

- que l'ajout, la modification ou la suppression d'un fichier soit répercutée globalement.

Ce que vous appelez de vos vœux est ce qu'on appelle la gestion de la configuration (ou gestion du changement en langage corpo-flanby). Cette fonctionnalité est inclue de base dans Salt et s'organise à travers un répertoire où l'on trouvera les fichiers dictant les diverses actions à mener. Pour diffuser ces informations vers les minions, le maître dispose d'un serveur de fichiers articulé autour de zeromq afin d'assurer une rapidité exemplaire du transport. Elle s'active très simplement dans le fichier /usr/pkg/etc/salt/master (ou /etc/salt/master sur un master GNU/Linux) :

file_roots:
base:
- /home/imil/salt

Nous définissons ici que le serveur de fichiers trouvera sa racine dans le repertoire /home/imil/salt. Les connaisseurs n'auront pas manqué de remarquer que la syntaxe présentée, comme celle de la déclaration des grains, est au format YAML[5], interpreté par PyYAML qu'utilise par défaut Salt.

Dans ce répertoire racine du serveur de fichiers, on organisera les fichiers states, des recettes dans le vocable Puppet / Chef qui définiront les actions à mener sur nos minions mais aussi possiblement les fichiers à transférer. Nous verrons plus loin que d'autres méthodes sont également disponibles pour additionner à la gestion de la configuration une dimension de versionning.

Commençons par un exemple très simple: nous souhaitons déposer sur tous nos minions le fichier foo. Écrivons pour cela un state simpliste :

$ cat test.sls
/tmp/foo:
file:
- managed
- source: salt://foo

Sans plus de fioritures, et comme on peut s'y attendre, ce state copiera le fichier foo présent dans home/imil/salt vers le répertoire /tmp de nos minions. Afin de prendre en compte ce fichier sls il est nécessaire d'écrire un fichier de haut niveau appelé top.sls. Dans ce fichier, on associe des `environnements' à des minions, puis on déclare les states associés. Nous avons défini plus haut dans la configuration du maître l'environnement base, nous nous cantonnerons à cet environnement dans le cadre de cet article.

$ cat top.sls
base:
‘*’:
- test

En clair, dans l'environnement base dont la racine se trouve dans /home/imil/salt, pour l'ensemble des minions (* est un glob ici), nous chargerons le state test.sls. Créons donc notre fichier foo de la façon la plus simple qui soit :

$ pwd
/home/imil/salt
$ echo bar > foo

Et demandons à Salt ce qu'il compte faire, on l'interroge grâce à la commande :

$ sudo salt ‘watto’ state.show_highstate
watto:
/tmp/foo:
----------
__env__:
base
__sls__:
test
file:
- managed
----------
- source:
salt://foo

On remarquera que nous avons précisé l'id minion, watto, sur lequel nous souhaitons publier puis exécuter le state et, comme prévu, Salt nous informe de l'opération à mener. Exécution :

$ sudo salt ‘watto’ state.highstate
----------
State: - file
Name: /tmp/foo
Function: managed
Result: True
Comment: File /tmp/foo updated
Changes: diff: New file

Et de vérifier sur le minion :

(imil@watto):[~]
$ cat /tmp/foo
bar

Au delà des fonctions de type file, un nombre impressionnant de modules est disponible, leur étude exhaustive remplirait la totalité des pages de ce magazine, aussi, nous ne nous concentrerons que sur certaines d'entre elles. En particulier, l'une des utilisations les plus courantes de ce type de suite logicielle concerne l'installation et la mise à jour des paquets, cette opération est prise en charge par les fonctions de type pkg. Il s'agit bien évidemment du pendant des opérations réalisées plus haut en mode commande, mais cette fois au sein d'une recette; là aussi, ces fonctions sont abstraites et appelleront en sous-main les divers outils de packaging de l'OS.

Exemple très simple: nous souhaitons nous assurer que le paquet vim est bien installé sur tous nos minions, nous renseignons un fichier state avec les lignes suivantes :

$ cat test.sls
vim:
pkg:
- installed

Comme on peut l'imaginer, l'application de ce state sur les minions aura pour effet d'installer le paquet nommé vim sur ces derniers.

Plus fort, nous souhaitons mettre à jour un fichier sur les systèmes distants en nous assurant que le logiciel qui l'utilisera est bien présent :

$ cat test.sls
/etc/vimrc:
file:
- managed
- source://vim/vimrc
- require:
- pkg: vim

Ainsi, le fichier présent sur le serveur de fichiers Salt, /home/imil/salt/vim/vimrc sera copié sur les minions sous /etc/vimrc mais non sans avoir installé le paquet vim si ce dernier était absent sur la machine destination.

4. Les colonnes de sel

Nous venons à peine de gratter la surface de capacités ce de fantastique outil, car pour industrialiser plus en amont les déploiements, il est pratiquement toujours impératif de rendre plus génériques les actions. Pour ce faire, Salt met à disposition de l'administrateur des concepts de templating à mi-chemin entre la déclaration et la programmation.

Dans cette optique, Salt introduit la notion de pillar. Un fichier pillar définit des données relatives à un ou des minions. Par exemple, on pourrait y stocker une liste de fichiers, d'utilisateurs, de donnés à post-traiter…

Les pillars sont actifs par défaut dans Salt mais pour les besoins de l'expérimentation, nous allons redéfinir leur emplacement sur le système de fichiers dans la configuration du master :

pillar_roots:
base:
- /home/imil/salt/pillar

Pour tirer profit des méthodes de templating mises à notre disposition, nous allons dérouler le scénario suivant : nous disposons d'une poignée de dotfiles, fichiers de configuration de nos outils fétiches, que nous souhaiterions déployer sur l'ensemble de nos minions. Simple mais fastidieuse, la spécification des actions à mener pour l'intégralité des fichiers semble intuitivement une mauvaise idée. De plus, nous allons pousser l'élégance jusqu'à l'utilisation d'un dépot git comme source d'information. Oui, Salt sait faire ça.

De façon à permettre à Salt de s'interfacer sur un serveur git, les paramètres suivants devront être activés dans la configuration du master :

fileserver_backend:
- roots
- git
gitfs_remotes:
- git://github.com/iMilnb/dotfiles.git

Deux remarques :

- ”Order matters”, l'ordre d'apparition des backends a son importance, dans l'exemple ci-dessus, si un fichier est trouvé dans la racine du serveur de fichiers local, Salt n'ira pas chercher dans son cache de fichiers git.

- Il est possible de lister plusieurs dépôts git et, comme précédemment, l'ordre est pris en compte.

Notez que pour l'utilisation de cette fonctionnalité, l'ajout du module python git-python est obligatoire. D'autre part, Salt n'interrogera pas systématiquement votre dépôt git lors d'un déploiement, mais utilisera plutôt un cache local accessible également à travers le scheme salt://, comme l'est un classique fichier local au système de fichiers.

Concentrons nous maintenant sur l'écriture de notre fichier pillar; les pillars ont le même format et la même extension que les states, nous créons donc en premier lieu un fichier top.sls dans le répertoire pillar et un fichier destiné à fournir les données que nous utiliserons lors du déploiement de nos fichiers dotfiles :

$ cat pillar/top.sls
base:
‘*’:
- dotfiles
$ cat pillar/dotfiles.sls
dotfiles:

.tmux.conf:
{% if grains[‘kernel’] == ‘Linux’ %}
.tmux.conf-linux
{% else %}
.tmux.conf
.profile:
.profile
.bashrc:
.bashrc
{% endif %}
.vimrc:
.vimrc
.vim/colors/molokai-trans.vim:
.vim_colors_molokai-trans.vim
.vim/colors/molokai.vim:
.vim_colors_molokai.vim

Bon oui, là comme ça, ça pique un peu. Mais lisons plus attentivement, nous déclarons ici une simple structure de données (dotfiles) qui contient un nom de fichier local, celui qui sera déployé sur nos minions et le nom du fichier distant (celui présent sur le dépôt git). Comme vous pouvez le constater, nous utilisons la notion de grains, en effet, la version de tmux, sur mes serveurs Debian est antérieure à celle proposée par pkgsrc sur mes machines NetBSD et ne supporte pas certaines options, aussi, je dispose de deux fichiers distincts. De la même façon, je ne souhaite pas écraser les fichiers .profile ni les .bashrc des machines Linux.

Dans ce fichier pillar, on touche une notion extrêmement puissante de la syntaxe des fichiers sls, il est en effet possible, grâce au système de template Jinja[6] de programmer nos templates en utilisant les facilités fournies par Salt. Ici, nous utilisons les informations des grains pour déterminer les fichiers déclarés en fonction du système d'exploitation du minion.

Munis de ces informations, nous allons pouvoir écrire un fichier state générique indiquant quelles opérations appliquer lors d'une demande de synchronisation :

{% for rcfile, remote in pillar[‘dotfiles’].
iteritems() %}
/home/imil/{{ rcfile }}:
file.managed:
- source: salt://{{ remote }}

- user: imil
{% if grains[‘os’] == ‘NetBSD’ %}
- group: wheel
{% else %}
- group: imil
{% endif %}
- mode: 644
{% endfor %}
/home/imil/.vim:
file.directory:
- user: imil
- recurse:
- user

Là encore, le template utilise Jinja pour itérer dans la structure de données dotfiles disponible via pillar. À l'aide des grains on choisit le groupe propriétaire du fichier créé.

On s'assure que tous les fichiers du répertoire .vim appartiennent bien à l'utilisateur visé à l'aide de la fonction file.directory et son paramètre recurse.

Finalement, on applique le state aux minions par la commande :

$ sudo salt ‘*’ state.highstate

Il sera toutefois judicieux, avant tout envoi, de valider le bon fonctionnement et la cohérence des states par la commande :

sudo salt ‘*’ state.show_highstate

dont voici un extrait :

ragnos:
----------
/home/imil/.bashrc:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- source:
salt://.bashrc
----------
- user:
imil
----------
- group:
wheel
----------

- mode:
644
- managed
/home/imil/.profile:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- source:
salt://.profile
----------
- user:
imil
----------
- group:
wheel
----------
- mode:
644
- managed
/home/imil/.tmux.conf:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- source:
salt://.tmux.conf
----------
- user:
imil
----------
- group:
wheel
----------
- mode:
644
- managed
/home/imil/.vim:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- user:
imil
----------
- recurse:
- user
- directory
/home/imil/.vim/colors/molokai-trans.vim:
----------
__env__:
base
__sls__:
imilhome

file:
----------
- source:
salt://.vim_colors_molokaitrans.
vim
----------
- user:
imil
----------
- group:
wheel
----------
- mode:
644
- managed
/home/imil/.vim/colors/molokai.vim:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- source:
salt://.vim_colors_molokai.vim
----------
- user:
imil
----------
- group:
wheel
----------
- mode:
644
- managed
/home/imil/.vimrc:
----------
__env__:
base
__sls__:
imilhome
file:
----------
- source:
salt://.vimrc
----------
- user:
imil
----------
- group:
wheel
----------
- mode:
644
- managed
/tmp/foo:
----------
__env__:
base
__sls__:

test
file:
- managed
----------
- source:
salt://foo

Comble du raffinement, lorsque le fichier à diffuser est déjà présent sur le minion visé, Salt affichera le différentiel entre les deux versions au format diff. Like a sir.

5. One spice to rule them all

Au sein de mon entreprise dont l'activité principale consiste à manipuler un nombre conséquent de machines virtuelles de façon industrielle toute la journée (un cloud, donc), de nombreuses solutions d'orchestration ont été évalué. Inégales tant sur leur courbe d'apprentissage que sur le niveau de complexité infrastructurelle, Salt nous est apparu comme une suite cohérente, abordable rapidement et s'intégrant parfaitement dans un éco-système régi en grande partie par Fabric[7], lui aussi écrit en Python. Bien que jeune, la qualité des développement et la structuration de sa communauté, le projet Salt est probablement sur le point de distribuer une nouvelle donne dans le monde bouillonnant de la gestion du changement (+10 points bullshit-bingo).

6. Références

[1] http://saltstack.com/

[2] http://www.zeromq.org/

[3] http://docs.saltstack.com/topics/installation/debian.html

[4] http://docs.saltstack.com/ref/modules/

[5] http://www.yaml.org/

[6] http://jinja.pocoo.org/

[7] http://docs.fabfile.org/en/1.6/