Publié par
Il y a 4 années · 8 minutes · DevOps

Puppet recipes 02 : Hiera

Nous avons vu dans le premier article comment gérer les fichiers avec Puppet. La ressource « file » associée aux ressources « service » et « package » constituent la base de nos classes Puppet.

Nous aurons également besoin de fournir des données à nos classes que ça soit des paramètres par défaut ou spécifiques à notre environnement, application ou serveur.

Et c’est exactement ce à quoi va nous servir Hiera : fournir des données à nos classes via un mécanisme hiérarchisé et intégré à Puppet.

Avant Hiera il était nécessaire d’inclure les valeurs par défaut directement dans nos classes (en général dans une classe appelées params.pp), mélangeant ainsi allègrement le code et les données (et ça c’est mal) ou d’utiliser un ENC (« External Node Classifier », on ne panique pas nous verrons cela dans un autre épisode).

Dans la suite nous parlerons de Hiera tel qu’inclus dans Puppet v3.3 à savoir la version 1.3.

Une base hiérarchisée

Hiera suit les même principes de base que Puppet :

  • On stocke les informations dans des fichiers textes simples
  • Les données (ou les classes dans le cas de Puppet) sont hiérarchisées

Le système Hiera est constitué :

  • D’un fichier de configuration précisant principalement la hiérarchie des fichiers de données
  • D’un ensemble de fichiers textes en général au format YAML stockant les données
  • De trois fonctions permettant d’interroger Hiera depuis nos manifests Puppet

Considérons l’exemple simple suivant :

Tout d’abord notre fichier de configuration Hiera (situé en général dans /etc/puppet/hiera.yaml):

---
:backends:
  - yaml
:yaml:
  :datadir: /etc/puppet/hieradata
:hierarchy:
  - common

Le premier bloc (« :backends ») définit les backends utilisés pour Hiera. Pour le moment nous n’utilisons que le backend YAML.

Le deuxième bloc, quant à lui, indique l’endroit où sont stockés les fichiers Hiera au format YAML.

Enfin le dernier bloc indique notre hiérarchie de fichiers où seront stockées nos données. Pour le moment nous avons une hiérarchie extrêmement simple avec un seul fichier nommé « common ». Cela signifie que lors de l’appel à une des fonctions Hiera dans notre code Puppet toutes les données seront récupérées d’un fichier nommé /etc/puppet/hieradata/common.yaml.

Et voici le contenu de notre fichier common.yaml :

---
ntp_servers:
  - pool.ntp.org
  - fr.pool.ntp.org

Dans ce fichier nous définissons un tableau au format YAML nommé « ntp_servers » et comprenant deux éléments (il existe de nombreux types supportés par YAML, voir le cookbook « YAML for Ruby »).

Nous allons enfin récupérer les valeurs de ce tableau dans notre code Puppet en utilisant la fonction « hiera » et les afficher:

class ntp(){
  $servers = hiera('ntp_servers')
  notify{$servers:}
}

La fonction hiera va parcourir les fichiers Hiera en fonction de la hiérarchie définit dans le fichier de configuration et renvoyer le premier résultat trouvé. Il existe deux autres fonctions de recherche: hiera_array et hiera_hash.

Et le résultat:

Info: Retrieving plugin
Info: Caching catalog for puppet
Info: Applying configuration version '1391585652'
Notice: pool.ntp.org
Notice: /Stage[main]/Ntp/Notify[pool.ntp.org]/message: defined 'message' as 'pool.ntp.org'
Notice: fr.pool.ntp.org
Notice: /Stage[main]/Ntp/Notify[fr.pool.ntp.org]/message: defined 'message' as 'fr.pool.ntp.org'
Notice: Finished catalog run in 0.02 seconds

Interprétation de variables et fonctions

Il est possible d’utiliser dans la configuration ainsi que dans les fichiers de données des variables et des fonctions.

Avant de se servir de cette fonctionnalité trois précautions sont à prendre en compte :

  1. On ne peut utiliser que des variables contenant des strings ou des fonctions renvoyant des strings, pas de booléen, de hash ou « undef »
  2. Si l’on utilise un fichier au format YAML, toutes les chaines contenant une variable ou une fonction doivent être entre double quotes
  3. Il n’est pas possible de créer une nouvelle donnée avec une variable ou le résultat d’une fonction. Ainsi les variables ne peuvent être utilisées que dans la partie « valeur » des fichiers YAML (key: « value »)

Utilisation des variables

Voici la méthode pour utiliser les variables dans les fichiers YAML:

hosts_entry: "myVM.%{::fqdn}"

Des doubles quotes, un signe pourcentage et des accolades et c’est tout ! Il est conseillé de spécifier le chemin complet des variables pour plus de sécurité.

Il faut savoir que Hiera reçoit l’ensemble des variables Puppet lorsqu’il est invoqué, aussi bien les facts (comme dans l’exemple ci-dessus) que les variables locales aux manifests.

Utilisation des fonctions

Il est également possible d’utiliser des fonctions à la place des variables. Actuellement deux fonctions sont disponibles:

  • hiera: comme son nom l’indique cette fonction réalise une recherche Hiera. Cela peut être très pratique dans les fichiers de données afin de factoriser certaines données (un chemin de fichier ou une adresse de base de données par exemple):
    wordpress::database_server: "%{hiera('instances::mysql::public_hostname')}" 
  • scope: moins utile. Interprète la variable passé en paramètre. Les deux lignes suivantes sont identiques:
    smtpserver: "mail.%{::domain}"
    smtpserver: "mail.%{scope('::domain')}" 

Amélioration de notre exemple

Nous allons modifier notre premier exemple afin d’avoir une liste de serveurs NTP dépendante de notre environnement. Première étape on modifie la hiérarchie dans notre fichier de configuration afin d’ajouter un fichier de données par environnement:

:backends:
  - yaml
:yaml:
  :datadir: /etc/puppet/hieradata
:hierarchy:
  - "%{environment}/common"
  - common

Ici nous avons indiqué à Hiera d’utiliser en priorité le fichier common situé dans le dossier de l’environnement. Cela permet d’avoir un fichier de données par environnement et si celui-ci n’est pas présent de se rabattre sur le fichier common.yaml commun à tous les environnements.

Nous allons donc rajouter un fichier /etc/puppet/hieradata/production/common.yaml:

---
ntp_servers:
  - ntp.prod.fr
  - ntpB.prod.fr

Et un nouveau test en précisant que notre machine appartient à l’environnement production:

root@puppet:~# puppet agent -t --environment production
Info: Retrieving plugin
Info: Caching catalog for puppet
Info: Applying configuration version '1391605463'
Notice: ntp.prod.fr
Notice: /Stage[main]/Ntp/Notify[ntp.prod.fr]/message: defined 'message' as 'ntp.prod.fr'
Notice: ntpB.prod.fr
Notice: /Stage[main]/Ntp/Notify[ntpB.prod.fr]/message: defined 'message' as 'ntpB.prod.fr'
Notice: Finished catalog run in 0.02 seconds

Et le même test en précisant que notre machine appartient à l’environnement de développement:

root@puppet:~# puppet agent -t --environment development
Info: Retrieving plugin
Info: Caching catalog for puppet
Info: Applying configuration version '1391605839'
Notice: pool.ntp.org
Notice: /Stage[main]/Ntp/Notify[pool.ntp.org]/message: defined 'message' as 'pool.ntp.org'
Notice: fr.pool.ntp.org
Notice: /Stage[main]/Ntp/Notify[fr.pool.ntp.org]/message: defined 'message' as 'fr.pool.ntp.org'
Notice: Finished catalog run in 0.02 seconds

Il est possible de préciser des arborescences bien plus fines pour la configuration Hiera (un fichier par machine par exemple) si le besoin s’en fait sentir.

De multiples backend

Comme nous l’avons vu Hiera stocke ses données au format YAML. Mais ce n’est pas le seul format disponible, on peut également utiliser nativement JSON mais surtout il existe un backend permettant d’encrypter ses données (au hasard, les mots de passe): hiera-eyaml.

En utilisant ce backend nous pouvons maintenant stocker nos données privées de manière sécurisée:

---
ntp_servers:
  - ntp.prod.fr
  - ntpB.prod.fr

db_password: >
    ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQAw
    DQYJKoZIhvcNAQEBBQAEggEAoJi/jv4AKg0z8T5Z8d9Hdl1CYwBPnABy9XCS
    Nl6Q0peFGGE74kHprM1kSrjDIPte60IMM9KOdHwPBQEchifhe6mvFO9212Dv
    +TXANt+GeWZuzj7X8+6cWXPq6N8lK3iFHdSSydiB9u2xvC4Hg3+EHT5gy2ud
    o8BefK1mGSeaRN/noOGPJabiFISEu3rlcqeBIbnRqyaslF2V9g0Orrbt5WzG
    /+RSoQJuXUaRRcmrIiknfLKG3dRvThIW8TYXAqzsp8QkU+DX+g50xP8xmWPt
    aIP4eDOAi+ef33txexYoUxT3myw4fFQ/AOwRWkdFXTThDURtchEMECNjjpyW
    WnSdbTA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCCboO8kSEjCq6h6DkW
    5UbcgBADMs8x2xwJT+0e4a8sL5OM]

L’installation et la configuration de ce backend sont clairement décrit dans le README du projet Github correspondant.

Il existe bien d’autres backend pour Hiera avec entre autre un backend MySQL, un backend REST ou encore un backend LDAP. Et s’il vous en faut un autre, il est possible de créer ses propres backends.

L’autoloading dans Puppet 3.x

Depuis la version 3 de Puppet chaque paramètre de classe (et uniquement de classe, ce n’est pas valable pour un define par exemple) est automatiquement cherché dans Hiera.

Par exemple pour la classe:

class ntp::client($server = "pool.ntp.org"){

...
}

Le paramètre « server » sera cherché dans Hiera au format « ntp::client::server » (nom de la classe puis nom du paramètre). Comme on peut le voir ici les paramètres d’une classe peuvent être passé de différentes façons et Puppet choisira la valeur retenue de la façon suivante:

  1. Si la classe est appelée en fournissant un paramètre directement (par exemple class{‘ntp::client’: server => « fr.pool.ntp.org »}) alors ce paramètre est la valeur retenue
  2. S’il existe dans Hiera un champ correspondant au paramètre (et donc nommé ntp::client::server) alors cette valeur est retenue si la condition 1 n’a pas été remplie
  3. Si les conditions 1 et 2 n’ont pas été remplies alors la valeur retenue est la valeur par défaut

Cela permet ainsi d’éviter d’avoir de multiples lignes inutiles dans un fichier params.pp telles que:

$servers = hiera('servers')

$port = hiera('port')

...

Et maintenant ?

Nous avons vu dans le premier épisode comment utiliser la ressource « file » et dans celui-ci comment séparer proprement le code Puppet des données.

Dans le prochain épisode nous verrons quelle structure donner à nos modules afin qu’ils soient lisibles, configurables et réutilisables.

Si vous avez lu jusqu’ici et que le sujet vous intéresse, n’hésitez pas, Xebia recrute !

Matthieu Nantern
Matthieu est un consultant Java senior spécialisé en DevOps, Apache Cassandra et la performance des applications. Depuis plus de 8 ans il remplit des missions agiles de développement, d'architecture et de conseils pour diverses sociétés. Récemment il est devenu formateur sur Apache Cassandra. Il travaille actuellement sur un projet de développement d'une plateforme IoT pour un grand groupe français.

Une réflexion au sujet de « Puppet recipes 02 : Hiera »

  1. Publié par wou, Il y a 3 mois

    Dans hiera, on met un tableau « classes » pour que Puppet instancie ces classes.

    « puppet config print manifest » donne le répertoire contenant les manifests qui vont être exécutés et dans l’un d’eux, on met ces instructions :
    node default { hiera_include(‘classes’) }

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *