Description

puppetLe besoin se résume ainsi:

Gérer de manière efficace et centralisée la configuration de plusieurs machines tout en ayant la souplesse de s'adapter aux spécificités de chacune d'entre elles.

Quand on gère un nombre important de serveurs, il est intéressant (voire indispensable) de disposer d'un outil permettant de déployer et de mettre à jour facilement leur configuration. L'outil cfEngine existe depuis pas mal de temps et répond parfaitement à ce besoin, mais il est un peu trop complexe, voire même hermétique/imbitable/lourd/etc. Je lui préfère donc Puppet qui a déjà l'avantage de s'appuyer sur le langage Ruby, et donc de proposer des fichiers de configuration clairs et lisibles car on y parle objet. Bref sans connaitre Ruby, la configuration est lisible. C'est un des gros avantage de Puppet, il s'appuie sur des outils plus génériques et standards que cfEngine.

Le schéma de fonctionnement de Puppet est simple, il faut un serveur central (PuppetMaster) et des serveurs à configurer qui exécutent en tâche de fond le client Puppet. Par défaut, les échanges entre le serveur et les clients sont sécurisés via une demande de certificats effectuée lors de la première connexion. Une fois le certificat du client signé par le serveur, les échanges peuvent commencer.

Sur le serveur PuppetMaster, il faudra définir la ou les configurations que l'on souhaite faire sur nos machines. Ensuite on attribue à un nœud, une ou plusieurs définition de configuration. Ainsi quand le client Puppet du nœud en question interrogera le serveur PuppetMaster, ce dernier lui proposera uniquement les éléments qu'il doit appliquer. Ces éléments de configuration sont définis en réalité dans des classes. L'identification d'un nœud se fait par son nom d'hôte.

Le type de configuration à déployer va des cas les plus simples, comme la vérification des permissions sur un fichier, la diffusion d'un même fichier sur l'ensemble du parc etc, aux cas les plus complexes où les fichiers doivent être adaptés en fonction de la machine, voire même exécuter des scripts. Pour la distribution de fichier, puppet propose un serveur de fichiers simple dont le contenu est mis à disposition des clients puppet. Pour savoir si un fichier à été mis à jour coté serveur et doit donc être deployé sur le nœud, celui-ci compare les sommes md5. Si elles ne sont pas identiques, le fichier est téléchargé. C'est simple mais efficace.

Mais puppet ne s'arrête pas là, via son serveur de fichiers il est aussi possible de proposer non pas un contenu unique pour un fichier, mais un contenu adapté pour chaque machine, via un système de "template" basé sur ERB. Un exemple:

127.0.0.1   localhost localhost.localdomain <%= hostname %>

Le mot-clef hostname sera remplacé sur le client par son nom d'hôte. Une liste impressionnante de mots-clef est disponible et est collectée par un petit outil (toujours en Ruby) qui s'appelle "Facter". On y trouve vraiment tout un tas de valeur spécifique à la machine, sa configuration réseau, les adresses mac, le nombre de processeur, le type d'architecture, le système d'exploitation, et même le numéro de série de la machine...

Installation

terminalComme expliqué plus haut, Puppet est un logiciel client/serveur. Il faut donc installer 1 serveur Puppet qui servira à déployer les configurations vers les clients Puppet.

Commençons par le serveur:

[root@serveur] # yum install puppet-server

Le service constituant le puppet-server s'appelle "puppetmaster".

Passons ensuite sur chaque noeud afin d'installer le client Puppet:

[root@noeud1] # yum install puppet

Configuration

La plus grosse partie de la configuration se fait sur le serveur Puppet. Pour cela nous suivons la documentation officielle concernant les "Best Practices" afin de préparer notre serveur Puppet à utiliser des modules pour chaque élément de configuration. L'utilisation de module permet une organisation simple et granulaire, nous disposerons de différents modules qui pourront être combinés ou pas sur nos machines.

Configuration coté serveur

Nous commençons par la création du répertoire qui abritera nos modules:

[root@serveur] # mkdir /etc/puppet/modules

Nous éditons ensuite le fichier général du serveur puppet, à savoir /etc/puppet/manifests/site.pp:

import "modules"
import "nodes"

filebucket { main: server => "puppet.in.domaine.net" }

# global defaults
File { backup => main }
Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin" }

Package {
 provider => $operatingsystem ? {
  debian => aptitude,
  centos => yum,
  fedora => yum,
 }
}

Ce fichier est facilement compréhensible, il indique d'importer le contenu des manifests (fichiers de configuration de puppet) "modules" et "nodes" qui définissent respectivement nos modules de configuration et nos machines à configurer.

Il indique ensuite l'adresse de notre serveur de fichier (la machine exécutant puppet-server) qui est dans notre exemple "puppet.in.domaine.net". Il faut bien entendu que nos clients Puppet sache résoudre cette adresse. C'est un cas d'erreur fréquent qui empêchera vos clients de récupérer les fichiers de configuration pousser depuis le serveur Puppet.

Viennent ensuite des paramètres globaux.

Enfin il définit en fonction du système d'exploitation sur le client Puppet quel gestionnaire de paquet utiliser. Il est donc facile d'exploiter un parc hétérogène avec Puppet, dans notre exemple nous avons des clients sous Debian et sous CentOS/Fedora.

Il ne reste plus qu'à lancer nos services sur nos machines. Sur le serveur, nous lançons le service puppetmaster et nous l'activons par défaut au démarrage avec les commandes suivantes:

[root@serveur] # service puppetmaster start
Démarrage de puppetmaster:        [ OK ]
[root@serveur] # chkconfig puppetmaster on

Configuration puppet coté client

La configuration du client puppet consiste uniquement à renseigner l'adresse du serveur puppet. Ceci ce fait dans le fichier /etc/sysconfig/puppet:

[root@noeud1] # cat /etc/sysconfig/puppet

Le service puppet peut maintenant être démarré et activé par défaut au démarrage:

[root@noeud1] # service puppet start
Démarrage de puppet:                  [ OK ]
[root@noeud1] # chkconfig puppet on

Signature des certificats

Lors du premier contact du client Puppet avec le serveur PuppetMaster, celui-ci n'est pas reconnu et fait donc une demande de signature de certificat. Il faut donc sur le serveur Puppet signer ces demandes afin d'authentifier les clients puppet.

Pour lister les certificats en attente:

[root@serveur] # puppetca --list
noeud1.in.domaine.net

Pour signer un certificat:

[root@serveur] # puppetca --sign noeud1.in.domaine.net

Une fois le certificat d'un client signé, celui-ci peut enfin commencer la communication et rapatrier les éléments de configuration qui lui sont associés.

Pour l'instant, les clients et le serveur Puppet peuvent communiquer, les traces de ces échanges sont par défaut dans /var/log/messages. Bien sûr aucune configuration ne sera déployée ce qui limite l'intérêt de la chose mais ce billet commençait à être bien long. Nous verrons donc dans un prochain billet la création d'un module de configuration à déployer.

Comme promis: Notre premier module