Gestion de la configuration 101 : écriture de manifestes de marionnettes

De Get Docs
Aller à :navigation, rechercher
      1. Introduction

En un mot, la gestion de la configuration du serveur (également appelée automatisation informatique) est une solution pour transformer l'administration de votre infrastructure en une base de code, décrivant tous les processus nécessaires au déploiement d'un serveur dans un ensemble de scripts de provisionnement qui peuvent être versionnés et facilement réutilisés. Il peut grandement améliorer l'intégrité de toute infrastructure de serveur au fil du temps.

Dans un guide précédent, nous avons parlé des principaux avantages de la mise en œuvre d'une stratégie de gestion de la configuration pour votre infrastructure de serveur, du fonctionnement des outils de gestion de la configuration et de ce que ces outils ont généralement en commun.

Cette partie de la série vous guidera à travers le processus d'automatisation du provisionnement de serveur à l'aide de Puppet, un outil de gestion de configuration populaire capable de gérer une infrastructure complexe de manière transparente, en utilisant un serveur maître pour orchestrer la configuration des nœuds. Nous nous concentrerons sur la terminologie, la syntaxe et les fonctionnalités du langage nécessaires à la création d'un exemple simplifié pour automatiser entièrement le déploiement d'un serveur Web Ubuntu 18.04 à l'aide d'Apache.

Voici la liste des étapes que nous devons automatiser pour atteindre notre objectif :

  1. Mettre à jour le cache apt
  2. Installer Apache
  3. Créer un répertoire racine de document personnalisé
  4. Placez un fichier index.html dans la racine du document personnalisé
  5. Appliquer un modèle pour configurer notre hôte virtuel personnalisé
  6. Redémarrez Apache

Nous commencerons par examiner la terminologie utilisée par Puppet, suivi d'un aperçu des principales fonctionnalités du langage pouvant être utilisées pour écrire des manifestes. À la fin de ce guide, nous partagerons l'exemple complet afin que vous puissiez l'essayer par vous-même.

Remarque : ce guide est destiné à vous présenter le langage Puppet et à écrire des manifestes pour automatiser le provisionnement de votre serveur. Pour une vue d'ensemble de Puppet, y compris les étapes nécessaires à l'installation et à la prise en main de cet outil, veuillez vous référer à la documentation officielle de Puppet.


    1. Commencer

Avant de pouvoir passer à une vue plus pratique de Puppet, il est important que nous nous familiarisions avec la terminologie et les concepts importants introduits par cet outil.

      1. Conditions relatives aux marionnettes
  • Puppet Master : le serveur maître qui contrôle la configuration sur les nœuds
  • Puppet Agent Node : un nœud contrôlé par un Puppet Master
  • Manifest : un fichier qui contient un ensemble d'instructions à exécuter
  • Resource : une portion de code qui déclare un élément du système et comment son état doit être modifié. Par exemple, pour installer un package, nous devons définir une ressource package et nous assurer que son état est défini sur "installé".
  • Module : une collection de manifestes et d'autres fichiers associés organisés de manière prédéfinie pour faciliter le partage et la réutilisation de parties d'un approvisionnement
  • Classe : tout comme avec les langages de programmation classiques, les classes sont utilisées dans Puppet pour mieux organiser le provisionnement et faciliter la réutilisation de parties du code
  • Facts : variables globales contenant des informations sur le système, telles que les interfaces réseau et le système d'exploitation
  • Services : utilisé pour déclencher des changements d'état de service, comme le redémarrage ou l'arrêt d'un service

Les approvisionnements de marionnettes sont écrits à l'aide d'un DSL personnalisé (langage spécifique au domaine) basé sur Ruby. ###Ressources Avec Puppet, les tâches ou étapes sont définies en déclarant des ressources. Les ressources peuvent représenter des packages, des fichiers, des services, des utilisateurs et des commandes. Ils peuvent avoir un état, qui déclenchera un changement de système au cas où l'état d'une ressource déclarée serait différent de ce qui se trouve actuellement sur le système. Par exemple, une ressource package définie sur installed dans votre manifeste déclenchera une installation de package sur le système si le package n'a pas été installé précédemment.

Voici à quoi ressemble une ressource package :

package { 'nginx':
    ensure  => 'installed'
}

Vous pouvez exécuter n'importe quelle commande arbitraire en déclarant une ressource exec, comme celle-ci :

exec { 'apt-get update':
    command => '/usr/bin/apt-get update'
}

Notez que la partie apt-get update sur la première ligne n'est pas la déclaration de commande réelle, mais un identifiant pour cette ressource unique. Souvent, nous devons référencer d'autres ressources à partir d'une ressource, et nous utilisons leur identifiant pour cela. Dans ce cas, l'identifiant est apt-get update, mais il peut s'agir de n'importe quelle autre chaîne. ###Dépendance des ressources Lors de l'écriture de manifestes, il est important de garder à l'esprit que Puppet n'évalue pas les ressources dans le même ordre qu'elles sont définies. C'est une source courante de confusion pour ceux qui débutent avec Puppet. Les ressources doivent définir explicitement la dépendance entre elles, sinon il n'y a aucune garantie quant à la ressource qui sera évaluée, et par conséquent exécutée, en premier.

Comme exemple simple, disons que vous voulez exécuter une commande, mais vous devez d'abord vous assurer qu'une dépendance est installée :

package { 'python-software-properties':
    ensure => 'installed'
}

exec { 'add-repository':
    command => '/usr/bin/add-apt-repository ppa:ondrej/php5 -y'
    require => Package['python-software-properties']
}

L'option require reçoit en paramètre une référence à une autre ressource. Dans ce cas, nous faisons référence à la ressource Package identifiée comme python-software-properties. Il est important de noter que si nous utilisons exec, package, et autres pour déclarer des ressources (en minuscules), lorsque nous nous référons à des ressources précédemment définies, nous utilisons Exec, [X182X ], etc. (en majuscules).

Supposons maintenant que vous deviez vous assurer qu'une tâche est exécutée avant une autre. Pour un cas comme celui-ci, nous pouvons utiliser l'option before à la place :

package { 'curl':
    ensure => 'installed'
    before => Exec['install script']
}

exec { 'install script':
    command => '/usr/bin/curl http://example.com/some-script.sh'
      1. Format du manifeste Les manifestes sont essentiellement une collection de déclarations de ressources, utilisant l'extension .pp. Vous trouverez ci-dessous un exemple de playbook simple qui effectue deux tâches : met à jour le cache apt et installe ensuite vim :
exec { 'apt-get update':
    command => '/usr/bin/apt-get update'
}

package { 'vim':
    ensure => 'installed'
    require => Exec['apt-get update']
}

Avant la fin de ce guide, nous verrons un exemple plus réel d'un manifeste, expliqué en détail. La section suivante vous donnera un aperçu des éléments et fonctionnalités les plus importants pouvant être utilisés pour écrire des manifestes Puppet. ##Écrire des manifestes ###Travailler avec des variables Les variables peuvent être définies à n'importe quel endroit d'un manifeste. Les types de variables les plus courants sont les chaînes et les tableaux de chaînes, mais d'autres types sont également pris en charge, tels que les booléens et les hachages.

L'exemple ci-dessous définit une variable de chaîne qui est ensuite utilisée dans une ressource :

$package = "vim"

package { $package:
   ensure => "installed"
}
      1. Utilisation des boucles Les boucles sont généralement utilisées pour répéter une tâche en utilisant différentes valeurs d'entrée. Par exemple, au lieu de créer 10 tâches pour installer 10 packages différents, vous pouvez créer une seule tâche et utiliser une boucle pour répéter la tâche avec tous les différents packages que vous souhaitez installer.

Le moyen le plus simple de répéter une tâche avec différentes valeurs dans Puppet consiste à utiliser des tableaux, comme dans l'exemple ci-dessous :

$packages = ['vim', 'git', 'curl']

package { $packages:
   ensure => "installed"
}

À partir de la version 4, Puppet prend en charge des méthodes supplémentaires pour parcourir les tâches. L'exemple ci-dessous fait la même chose que l'exemple précédent, mais cette fois en utilisant l'itérateur each. Cette option vous donne plus de flexibilité pour parcourir les définitions de ressources :

$packages.each |String $package| {
  package { $package:
    ensure => "installed"
  }
}
      1. Utilisation des conditions Les conditions peuvent être utilisées pour décider dynamiquement si un bloc de code doit être exécuté ou non, en fonction d'une variable ou d'une sortie d'une commande, par exemple.

Puppet prend en charge la plupart des structures conditionnelles que vous pouvez trouver avec les langages de programmation traditionnels, comme les instructions if/else et case. De plus, certaines ressources comme exec prennent en charge les attributs qui fonctionnent comme une condition, mais n'acceptent qu'une sortie de commande comme condition.

Supposons que vous souhaitiez exécuter une commande basée sur un fact. Dans ce cas, comme vous souhaitez tester la valeur d'une variable, vous devez utiliser l'une des structures conditionnelles prises en charge, comme if/else :

if $osfamily != 'Debian' {
 warning('This manifest is not supported on this OS.')
}
else {
 notify { 'Good to go!': }
}

Une autre situation courante est lorsque vous souhaitez conditionner l'exécution d'une commande en fonction de la sortie d'une autre commande. Dans de tels cas, vous pouvez utiliser onlyif ou unless, comme dans l'exemple ci-dessous. Cette commande ne sera exécutée que lorsque la sortie de /bin/which php est réussie, c'est-à-dire que la commande se termine avec l'état 0 :

exec { "Test":
 command => "/bin/echo PHP is installed here > /tmp/test.txt",
 onlyif => "/bin/which php"
}

De même, unless exécutera la commande à tout moment, sauf lorsque la commande sous unless se termine avec succès :

exec { "Test":
 command => "/bin/echo PHP is NOT installed here > /tmp/test.txt",
 unless => "/bin/which php"
}
      1. Travailler avec des modèles Les modèles sont généralement utilisés pour configurer des fichiers de configuration, permettant l'utilisation de variables et d'autres fonctionnalités destinées à rendre ces fichiers plus polyvalents et réutilisables. Puppet prend en charge deux formats différents pour les modèles : Embedded Puppet (EPP) et Embedded Ruby (ERB). Le format EPP, cependant, ne fonctionne qu'avec les versions récentes de Puppet (à partir de la version 4.0).

Vous trouverez ci-dessous un exemple de modèle ERB pour configurer un hôte virtuel Apache, en utilisant une variable pour configurer la racine du document pour cet hôte :

<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot <%= @doc_root %>

    <Directory <%= @doc_root %>>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Afin d'appliquer le modèle, nous devons créer une ressource file qui restitue le contenu du modèle avec la méthode template. Voici comment appliquer ce modèle pour remplacer l'hôte virtuel Apache par défaut :

file { "/etc/apache2/sites-available/000-default.conf":
    ensure => "present",
    content => template("apache/vhost.erb") 
}    

Puppet fait quelques hypothèses lorsqu'il s'agit de fichiers locaux, afin de renforcer l'organisation et la modularité. Dans ce cas, Puppet rechercherait un fichier de modèle vhost.erb dans un dossier apache/templates, dans votre répertoire de modules.

      1. Définition et déclenchement des services Les ressources de service sont utilisées pour s'assurer que les services sont initialisés et activés. Ils sont également utilisés pour déclencher des redémarrages de service.

Prenons en considération notre exemple d'utilisation de modèle précédent, où nous avons configuré un hôte virtuel Apache. Si vous voulez vous assurer qu'Apache est redémarré après un changement d'hôte virtuel, vous devez d'abord créer une ressource service pour le service Apache. Voici comment une telle ressource est définie dans Puppet :

service { 'apache2':
    ensure => running,
    enable => true
}

Désormais, lors de la définition de la ressource, vous devez inclure une option notify afin de déclencher un redémarrage :

file { "/etc/apache2/sites-available/000-default.conf":
    ensure => "present",
    content => template("vhost.erb"),
    notify => Service['apache2'] 
} 
    1. Exemple de manifeste Examinons maintenant un manifeste qui automatisera l'installation d'un serveur Web Apache dans un système Ubuntu 14.04, comme indiqué dans l'introduction de ce guide.

L'exemple complet, y compris le fichier modèle pour la configuration d'Apache et un fichier HTML devant être servi par le serveur Web, peut être trouvé sur Github. Le dossier contient également un Vagrantfile qui vous permet de tester le manifeste dans une configuration simplifiée, à l'aide d'une machine virtuelle gérée par Vagrant.

Vous trouverez ci-dessous le manifeste complet :

défaut.pp

$doc_root = "/var/www/example"

exec { 'apt-get update':
 command => '/usr/bin/apt-get update'
}

package { 'apache2':
 ensure  => "installed",
 require => Exec['apt-get update']
}

file { $doc_root:
 ensure => "directory",
 owner => "www-data",
 group => "www-data",
 mode => 644
}

file { "$doc_root/index.html":
   ensure => "present",
   source => "puppet:///modules/main/index.html",
   require => File[$doc_root]
}

file { "/etc/apache2/sites-available/000-default.conf":
   ensure => "present",
   content => template("main/vhost.erb"),
   notify => Service['apache2'],
   require => Package['apache2']
}

service { 'apache2':
   ensure => running,
   enable => true
}
      1. Manifest expliqué
        1. line 1 Le manifeste commence par une définition de variable, $doc_root. Cette variable est ensuite utilisée dans une déclaration de ressource.
        1. lines 3-5 Cette ressource exec exécute une commande apt-get update.
        1. lines 7-10 Cette ressource package installe le package apache2, définissant que la ressource apt-get update est une exigence, ce qui signifie qu'elle ne sera exécutée qu'après la ressource requise est évaluée.
        1. lignes 12-17 Nous utilisons ici une ressource fichier pour créer un nouveau répertoire qui servira de racine de document. La ressource file peut être utilisée pour créer des répertoires et des fichiers, et elle est également utilisée pour appliquer des modèles et copier des fichiers locaux sur le serveur distant. Cette tâche peut être exécutée à tout moment du provisionnement, nous n'avons donc pas eu besoin de définir de require ici.
        1. lignes 19-23 Nous utilisons ici une autre ressource fichier, cette fois pour copier notre fichier local index.html à la racine du document à l'intérieur du serveur. Nous utilisons le paramètre source pour indiquer à Puppet où trouver le fichier d'origine. Cette nomenclature est basée sur la manière dont Puppet gère les fichiers locaux ; Si vous jetez un œil au référentiel d'exemples Github, vous verrez comment la structure de répertoires doit être créée afin de permettre à Puppet de trouver cette ressource. Le répertoire racine du document doit être créé avant l'exécution de cette ressource, c'est pourquoi nous incluons une option require faisant référence à la ressource précédente.
        1. lignes 25-30 Une nouvelle ressource fichier est utilisée pour appliquer le modèle Apache et notifier le service d'un redémarrage. Pour cet exemple, notre approvisionnement est organisé dans un module appelé main, et c'est pourquoi la source du modèle est main/vhost.erb. Nous utilisons une instruction require pour nous assurer que la ressource de modèle n'est exécutée qu'après l'installation du package apache2, sinon la structure de répertoires utilisée par Apache peut ne pas encore être présente.
        1. lignes 32-35 Enfin, la ressource service déclare le service apache2, que nous notifions pour un redémarrage à partir de la ressource qui applique le modèle d'hôte virtuel.
    1. Conclusion Puppet est un puissant outil de gestion de configuration qui utilise un DSL personnalisé expressif pour gérer les ressources du serveur et automatiser les tâches. Son langage offre des ressources avancées qui peuvent donner une flexibilité supplémentaire à vos configurations de provisionnement ; il est important de se rappeler que les ressources ne sont pas évaluées dans le même ordre qu'elles sont définies, et pour cette raison, vous devez être prudent lors de la définition des dépendances entre les ressources afin d'établir la bonne chaîne d'exécution.

Dans le prochain guide de cette série, nous examinerons Chef, un autre puissant outil de gestion de configuration qui exploite le langage de programmation Ruby pour automatiser l'administration et le provisionnement de l'infrastructure.