Introduction à Puppet
Mise à jour :
Puppet
est un outil de gestion de configuration qui fonctionne
principalement en mode client/serveur. En effet, il est également possible
d’appliquer le code directement sur une machine cliente sans faire appel au
serveur. Pour rappel un outil de gestion de configuration a pour but :
- de déployer des configurations sur les serveurs ;
- d’automatiser le déploiement et la configuration de logiciels ;
- de s’assurer que les configurations voulues sont celles qui sont appliquées ;
Le serveur est appelé le master
et les clients des nodes
. Sur les nodes sont
installées un agent qui se charge d’obtenir les configurations déclarées sur le
master. Le master regroupe les classes en ce qu’on appelle un catalogue et qui
sera appliqué sur le node par l’agent. Dans le cas où la configuration décrite
n’est pas celle présente sur le node, l’agent appliquera les changements
nécessaires pour la respecter.
Le langage de Puppet
Les manifests
Le code Puppet utilise des fichiers .pp
qui sont écrits dans un langage
déclaratif basé sur du Ruby. Ces fichiers sont appelés des manifests
. Dans ces
manifests on utilise des ressources de base qui peuvent être de type compte
user, group, file, package, service, exec et cron. Ces ressources présentent un
haut niveau d’abstraction. Par exemple, il n’est pas nécessaire d’indiquer le
système de package à utiliser sur la distribution cible du node. Pour identifier
le système hôte, l’agent puppet utilise Facter
qui décrit le système sous la
forme d’un fichier JSON.
Les classes
Une classe est une collection de ressources liées qui, une fois la classe définie, peuvent être déclarées comme une seule unité. Par exemple, une classe peut contenir toutes les ressources (telles que des fichiers, des paramètres, des modules et des scripts) nécessaires pour configurer le serveur Web Apache sur un hôte. Les classes peuvent également déclarer d’autres classes.
Les modules
Un module est un ensemble de classes, de types de ressources, de fichiers, de fonctions et de modèles, regroupés pour atteindre un objectif particulier. Par exemple, un module peut configurer une instance de serveur Web Apache. Des modules écrits par la communauté Puppet sont disponibles sur la PuppetForge ↗.
Apprendre Puppet avec Vagrant
Comme pour Ansible
, Salt
et Chef
, Vagrant
met à disposition un
provisionner
prenant en charge les manifests Puppet
.
Voici un exemple de fichier Vagrantfile l’utilisant :
# -*- mode: ruby -*-# vi: set ft=ruby :$script=<<EOFwget http://apt.puppet.com/puppet7-release-jammy.debsudo dpkg -i puppet7-release-jammy.debsudo apt update && sudo apt install puppet-agentEOFVagrant.configure("2") do |config| config.vm.box = "generic/ubuntu2204" config.vm.network :forwarded_port, guest: 80, host: 8080 config.vm.provision "shell", inline: $script config.vm.provision :puppet do |puppet| puppet.module_path = "puppet/modules" puppet.manifests_path = "puppet/manifests" puppet.manifest_file = "init.pp" endend
Vous remarquez que j’installe juste le puppet-agent
et j’indique le chemin du
manifest
à utiliser. D’ailleurs écrivons le :
Ecriture d’un simple manifest
Nous allons tout simplement demander l’installation du package apache. Il suffit
donc de créer le fichier init.pp
dans le répertoire puppet/manifests
:
package { 'apache2': ensure => installed,}
notify { 'apache installed': message => 'apache a été installé',}
Nous utilisons deux simples ressources une de type package ↗ et une autre de type notify ↗. Vous pouvez retrouver tous les arguments de ces ressources en suivant leur lien respectif. Une fois dessus vous serez capable d’utiliser n’importe quelle autre type de ressource Puppet.
Utilisation de modules de la puppetForge
Maintenant que l’on sait écrire de simples manifests voyons comment récupérer et utiliser des modules Puppet sur la puppetForge ↗.
Pour télécharger les modules, il faut au préalable installer le puppet-agent :
sudo apt install puppet-agent
Ensuite on crée le répertoire et on y télécharge le module désiré :
mkdir -p puppet/modulespuppet module install puppetlabs-apache --version 8.2.1 --modulepath=puppet/modules
## Quels modules avons nous ?
puppet module list --modulepath=.//home/vagrant/Projets/personal/vagrant-puppet/puppet/modules├── puppet-nginx (v4.0.0)├── puppetlabs-apache (v8.2.1)├── puppetlabs-concat (v7.3.0)└── puppetlabs-stdlib (v8.4.0)
Notre module est installé localement. Je vais l’utiliser dans mon manifest.
Remplacer le contenu du fichier init.pp
avec ces lignes (on remplace package
par le module) :
class { 'apache': default_vhost => false,}
notify { 'apache installed': message => 'apache a été installé',}
On relance notre box Vagrant. Comme pour Chef il faut utiliser la commande reload pour recharger les modifications faites localement :
vagrant reload --provision
Maintenant que nous savons utiliser un module de la forge voyons comment en écrire.
Ecriture d’un simple module
Dans un premier temps installons le PDK pour créer la structure de notre module:
wget --content-disposition 'https://pm.puppet.com/cgi-bin/pdk_download.cgi?dist=ubuntu&rel=20.04&arch=amd64&ver=latest'sudo dpkg -i pdk_2.5.0.0-1focal_amd64.deb
Maintenant dans le répertoire puppet/modules créons le squelette de notre module test:
mkdir -p puppet/modulespuppet/modulespdk new module apache2
Nous avons la structure, créons notre première classe.
cd apache2pdk new class apache2---------------Files added--------------/home/vagrant/Projets/personal/vagrant-puppet/puppet/modules/apache2/spec/classes/apache2_spec.rb/home/vagrant/Projets/personal/vagrant-puppet/puppet/modules/apache2/manifests/init.pp
Pour cela éditer le fichier puppet/modules/apache2/manifests/init.pp
. Prenons
comme exemple l’installation du serveur web apache2 :
# @summary A short summary of the purpose of this class## A description of what this class does## @example# include apache2class apache2 { # Installation de apache package { 'apache2': ensure => 'present', } -> # On démarre le service apache service { 'apache2': ensure => 'running', }}
Pour valider votre code, nous allons utiliser la commande validate de pdk 👍 :
pdk validatepdk (INFO): Using Ruby 2.7.6pdk (INFO): Using Puppet 7.16.0pdk (INFO): Running all available validators...pdk (INFO): Validator 'puppet-epp' skipped for '/home/vagrant/Projets/personal/vagrant-puppet/puppet/modules/apache2'. No files matching '["**/*.epp"]' found to validate.pdk (INFO): Validator 'task-metadata-lint' skipped for '/home/vagrant/Projets/personal/vagrant-puppet/puppet/modules/apache2'. No files matching '["tasks/*.json"]' found to validate.┌ [✔] Running metadata validators ...├── [✔] Checking metadata syntax (metadata.json tasks/*.json).└── [✔] Checking module metadata style (metadata.json).┌ [✔] Running puppet validators ...├── [✔] Checking Puppet manifest syntax (**/*.pp).└── [✔] Checking Puppet manifest style (**/*.pp).┌ [✔] Running ruby validators ...└── [✔] Checking Ruby code style (**/**.rb).┌ [✔] Running tasks validators ...├── [✔] Checking task names (tasks/**/*).└── [✔] Checking task metadata style (tasks/*.json).┌ [✔] Running yaml validators ...└── [✔] Checking YAML syntax (**/*.yaml **/*.yml).
Pour vous aider à écrire votre code Puppet correctement, vous pouvez installer l’extension vscode officielle ↗. Elle est plutôt complète on y retrouve : linting, indentation, intellisense, debug …
Éditer maintenant le fichier puppet/manifests/init.pp et remplacez les lignes par :
class { 'apache2':}
Utilisation des facts
Nous allons simplement ajouter un test vérifiant si le systeme d’exploitation est bien celui attendu. Pour retrouver la structure des facts puppet il suffit de lancer la commande facter. Pour limiter le retour à certains paramètres il suffit d’ajouter le nom derrière :
facteraio_agent_version => 7.20.0augeas => { version => "1.13.0"}
...
os => { architecture => "amd64", distro => { codename => "jammy", description :> "Ubuntu 22.04.1 LTS", id => "Ubuntu", release => { full => "22.04", major => "22.04" } }, family => "Debian", hardware => "x86_64", name => "Ubuntu", release => { full => "22.04", major => "22.04" },
...
facter os.familyDebian
Ajoutons donc ce test dans notre manifest:
# @summary A short summary of the purpose of this class## A description of what this class does## @example# include apache2class apache2 { if $facts['os']['family'] != 'Debian' { notice("This os ${facts['os']['name']} is not supported") } else { # Installation de apache package { 'apache2': ensure => 'present', } -> # On démarre le service apache service { 'apache2': ensure => 'running', } }}
Changez la distribution de base de votre Vagrantfile par une fedora et regarder le résultat :
# -*- mode: ruby -*-# vi: set ft=ruby :$script=<<EOFwget http://yum.puppet.com/puppet7-release-fedora-36.noarch.rpmsudo yum install puppet7-release-fedora-36.noarch.rpm -ysudo yum update -y && sudo yum install puppet-agent -yEOFVagrant.configure("2") do |config| config.vm.box = "generic/fedora36" config.vm.network :forwarded_port, guest: 80, host: 8080 config.vm.provision "shell", inline: $script config.vm.provision :puppet do |puppet| puppet.module_path = "puppet/modules" puppet.manifests_path = "puppet/manifests" puppet.manifest_file = "init.pp" endend
On détruit et on re-provisionne la machine :
vagrant destroy -fvagrant up
...
==> default: Notice: Scope(Class[Apache2]): This os Fedora is not supported==> default: Notice: Compiled catalog for fedora36.localdomain in environment production in 0.04 seconds==> default: Notice: Applied catalog in 0.06 seconds
Ca fonctionne.
Utilisation de variables et paramètres
Nous remplacer le fichier index.html par notre propre contenu. Pour cela nous allons déclarer un paramètre content dans notre class apache2. Ce paramètre sera ensuite utilisé dans la ressource file :
# @summary A short summary of the purpose of this class## A description of what this class does## @example# include apache2class apache2 ( String $content ) { if $facts['os']['family'] != 'Debian' { notice("This os ${facts['os']['name']} is not supported") } else { # Installation de apache package { 'apache2': ensure => 'present', } -> # On démarre le service apache service { 'apache2': ensure => 'running', } file { '/var/www/html/index.html': ensure => 'file', content => $content, path => '/var/www/html/index.html', } }}
Pour déclarer les variables par défaut, nous allons l’ajouter dans le fichier
common.yaml
présent dans le répertoire data
de notre module :
---apache2::content: Ma première variable
Une autre manière aurait eté de l’ajouter dans la déclaration de la classe :
class apache2 ( String $content = 'Mon texte par défaut') {
On relance, on récupère l’adresse IP de notre VM et un petit curl pour vérifier que le contenu est bien celui attendu :
vagrant destroy -fvagrant up...
vagrant ssh-configHost default HostName 192.168.121.16 User vagrant Port 22 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile /home/vagrant/Projets/personal/vagrant-puppet/.vagrant/machines/default/libvirt/private_key IdentitiesOnly yes LogLevel FATAL
curl http://192.168.121.16Ma première variable
Maintenant pour surcharger le paramètre dans l’instance de notre classe, il
suffit d’ajouter la variable content
dans l’appel de la classe :
class { 'apache2': content=> 'Ma variable est défini dans la classe',}
On relance et on repasse le curl :
curl http://192.168.121.16Ma variable est défini dans la classe
On relance la validation de notre module :
pdk validate
...└── [✔] Checking YAML syntax (**/*.yaml **/*.yml).pdk (WARNING): puppet-lint: missing documentation for class parameter apache2::content (manifests/init.pp:7:24)
Aie. Il faut documenter la variable. Modifions le code de notre manifest :
# @summary A short summary of the purpose of this class## A description of what this class does## @param content# Valid option: une chaine de caractèreclass apache2 ( String $content ) { if $facts['os']['family'] != 'Debian' { notice("This os ${facts['os']['name']} is not supported") }
C’est réglé.
Plus d’infos sur les paramètres ↗
Utilisation de template
Nous aimerions plutôt que le contenu de notre fichier utilise un template Puppet
(EPP) dans lequel on va injecter notre variable content
. Il faut dans un
premier temps créer le fichier index.epp
dans le répertoire templates
de
notre module :
Hello mon premier Template hébergé sur <%= $facts[hostname] %><br><%= $content %>
<% if $facts[hostname] =~ 'ubuntu2204' { -%>Bonne machine<% } -%>
La syntaxe utilisée pour les templates (pas simple) :
- <%= VAR %> affiche le contenu d’une variable
- <% EXPRESSION %> une instruction interprété
- <%# COMMENT %> un commentaire
- <%- ou -%> supprime le saut de ligne avant ou après
On regarde le résultat :
curl http://192.168.121.16Hello mon premier Template hébergé sur ubuntu2204<br>Ma variable est défini dans la classe
Bonne machine
Plus d’infos
- Site Officiel : puppet.com ↗