Gestion de la configuration 101 : Rédaction de recettes de chef

De Get Docs
Aller à :navigation, rechercher

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 tout au long du processus d'automatisation du provisionnement de serveur à l'aide de Chef, un puissant outil de gestion de la configuration qui exploite le langage de programmation Ruby pour automatiser l'administration et le provisionnement de l'infrastructure. 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 un aperçu de la terminologie utilisée par Chef, suivi d'un aperçu des principales fonctionnalités du langage pouvant être utilisées pour écrire des recettes. À 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 Chef et à écrire des recettes pour automatiser le provisionnement de votre serveur. Pour une vue d'ensemble de Chef, y compris les étapes nécessaires à l'installation et à la mise en route de cet outil, veuillez vous reporter à la Documentation officielle de Chef.


    1. Démarrage Avant de pouvoir passer à une vue plus pratique de Chef, il est important que nous nous familiarisions avec la terminologie et les concepts importants introduits par cet outil. ###Conditions du chef
  • Chef Server : un serveur central qui stocke les informations et gère le provisionnement des nœuds
  • Chef Node : un serveur individuel géré par un serveur chef
  • Chef Workstation : une machine contrôleur où les approvisionnements sont créés et téléchargés sur le serveur Chef
  • Recipe : un fichier qui contient un ensemble d'instructions (ressources) à exécuter. Une recette doit être contenue dans un Cookbook
  • Resource : une portion de code qui déclare un élément du système et quelle action doit être exécutée. Par exemple, pour installer un package, nous déclarons une ressource package avec l'action install
  • Cookbook : une collection de recettes et d'autres fichiers connexes organisés de manière prédéfinie pour faciliter le partage et la réutilisation de parties d'un approvisionnement
  • Attributs : détails sur un nœud spécifique. Les attributs peuvent être automatiques (voir la définition suivante) et peuvent également être définis à l'intérieur des recettes
  • Attributs automatiques : variables globales contenant des informations sur le système, telles que les interfaces réseau et le système d'exploitation (appelées facts dans d'autres outils). Ces attributs automatiques sont collectés par un outil appelé Ohai
  • Services : utilisé pour déclencher des changements d'état de service, comme le redémarrage ou l'arrêt d'un service
      1. Recipe Format Les recettes Chef sont écrites en Ruby. Une recette est essentiellement une collection de définitions de ressources qui créeront un ensemble d'instructions étape par étape à exécuter par les nœuds. Ces définitions de ressources peuvent être mélangées avec du code Ruby pour plus de flexibilité et de modularité.

Vous trouverez ci-dessous un exemple simple de recette qui exécutera apt-get update et installera vim par la suite :

execute "apt-get update" do
 command "apt-get update"
end

apt_package "vim" do
 action :install
end
    1. Écrire des recettes ###Travailler avec des variables Les variables locales peuvent être définies à l'intérieur des recettes comme des variables locales Ruby normales. L'exemple ci-dessous montre comment créer une variable locale qui sera ensuite utilisée dans une définition de ressource :
package  = "vim"

apt_package package do
 action :install
end

Ces variables ont cependant une portée limitée, n'étant valides qu'à l'intérieur du fichier où elles ont été définies. Si vous souhaitez créer une variable et la rendre disponible dans le monde entier, afin de pouvoir l'utiliser à partir de n'importe lequel de vos livres de cuisine ou recettes, vous devez définir un attribut personnalisé.

        1. Utilisation des attributs Les attributs représentent des détails sur un nœud. Chef a des attributs automatiques, qui sont les attributs collectés par un outil appelé Ohai et contenant des informations sur le système (telles que la plate-forme, le nom d'hôte et l'adresse IP par défaut), mais il vous permet également de définir vos propres attributs personnalisés.

Les attributs ont différents niveaux de priorité, définis par le type d'attribut que vous créez. Les attributs default sont le choix le plus courant, car ils peuvent toujours être remplacés par d'autres types d'attributs si nécessaire.

L'exemple suivant montre à quoi ressemblerait l'exemple précédent avec un attribut de nœud default au lieu d'une variable locale :

node.default['main']['package'] = "vim"

apt_package node['main']['package'] do
 action :install
end

Il y a deux détails à observer dans cet exemple :

La pratique recommandée lors de la définition des variables de nœud consiste à les organiser sous forme de hachages en utilisant le livre de recettes en cours d'utilisation comme clé. Dans ce cas, nous avons utilisé main, car nous avons un livre de cuisine du même nom. Cela évite toute confusion si vous travaillez avec plusieurs livres de recettes qui pourraient avoir des attributs nommés similaires. Notez que nous avons utilisé node.default lors de la définition de l'attribut, mais lors de l'accès ultérieur à sa valeur, nous avons utilisé node directement. L'utilisation node.default définit que nous créons un attribut de type default. Cet attribut peut voir sa valeur écrasée par un autre type avec une priorité plus élevée, comme les attributs normal ou override.

La priorité des attributs peut être un peu déroutante au début, mais vous vous y habituerez après un peu de pratique. Pour illustrer le comportement, considérons l'exemple suivant :

node.normal['main']['package']  = "vim"

node.override['main']['package'] = "git"

node.default['main']['package'] = "curl"

apt_package node['main']['package'] do
 action :install
end

Savez-vous quel package sera installé dans ce cas ? Si vous avez deviné git, vous avez bien deviné. Quel que soit l'ordre dans lequel les attributs ont été définis, la priorité la plus élevée du type override rendra le node['main']['package'] be evaluated to git`. ###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.

Chef prend en charge toutes les structures de boucle Ruby pour créer des boucles à l'intérieur des recettes. Pour une utilisation simple, each est un choix courant :

['vim', 'git', 'curl'].each do |package|
 apt_package package do
   action :install
 end
end

Au lieu d'utiliser un tableau en ligne, vous pouvez également créer une variable ou un attribut pour définir les paramètres que vous souhaitez utiliser dans la boucle. Cela gardera les choses plus organisées et plus faciles à lire. Ci-dessous, le même exemple utilisant maintenant une variable locale pour définir les packages à installer :

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

packages.each do |package|
 apt_package package do
   action :install
 end
end
      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.

Chef prend en charge toutes les conditions Ruby pour créer des instructions conditionnelles dans les recettes. De plus, tous les types de ressources prennent en charge deux propriétés spéciales qui évalueront une expression avant de décider si la tâche doit être exécutée ou non : if_only et not_if.

L'exemple ci-dessous vérifiera l'existence de php avant d'essayer d'installer l'extension php-pear. Il utilisera la commande which pour vérifier s'il y a un exécutable php actuellement installé sur ce système. Si la commande which php renvoie faux, cette tâche ne sera pas exécutée :

apt_package "php-pear" do
 action :install
 only_if "which php"
end

Si nous voulons faire le contraire, en exécutant une commande à tout moment sauf lorsqu'une condition est évaluée comme vraie, nous utilisons not_if à la place. Cet exemple installera php5 sauf si le système est CentOS :

apt_package "php5" do
 action :install
 not_if { node['platform'] == 'centos' }
end

Pour effectuer des évaluations plus complexes, ou si vous souhaitez exécuter plusieurs tâches dans une condition spécifique, vous pouvez utiliser l'une des conditions Ruby standard. L'exemple suivant n'exécutera apt-get update que lorsque le système est Debian ou Ubuntu :

if node['platform'] == 'debian' || node['platform'] == 'ubuntu'
 execute "apt-get update" do
   command "apt-get update"
 end
end

L'attribut node['platform'] est un attribut automatique de Chef. Le dernier exemple n'était que pour démontrer une construction conditionnelle plus complexe, cependant il pourrait être remplacé par un simple test utilisant l'attribut automatiquenode['platform_family'] , qui renverrait « debian » pour les systèmes Debian et Ubuntu.


Chef utilise des modèles Embedded Ruby (ERB), qui est le même format utilisé par Puppet. Ils prennent en charge les conditions, les boucles et d'autres fonctionnalités Ruby.

Vous trouverez ci-dessous un exemple de modèle ERB pour configurer un hôte virtuel Apache, en utilisant une variable pour définir 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>

Pour appliquer le modèle, nous devons créer une ressource template. Voici comment appliquer ce modèle pour remplacer l'hôte virtuel Apache par défaut :

template "/etc/apache2/sites-available/000-default.conf" do
 source "vhost.erb"
 variables({ :doc_root => node['main']['doc_root'] })
 action :create
end 

Chef fait quelques hypothèses lorsqu'il s'agit de fichiers locaux, afin de renforcer l'organisation et la modularité. Dans ce cas, Chef rechercherait un fichier de modèle vhost.erb dans un dossier templates qui devrait se trouver dans le même livre de recettes où se trouve cette recette.

Contrairement aux autres outils de gestion de configuration que nous avons vus jusqu'à présent, Chef a une portée plus stricte pour les variables. Cela signifie que vous devrez fournir explicitement toutes les variables que vous prévoyez d'utiliser dans un modèle, lors de la définition de la ressource template. Dans cet exemple, nous avons utilisé la méthode variables pour transmettre l'attribut doc_root dont nous avons besoin au modèle d'hôte virtuel. ###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.

Dans Chef, les ressources de service doivent être déclarées avant d'essayer de les notifier, sinon vous obtiendrez une erreur.

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 Chef :

service "apache2" do
  action [ :enable, :start ]
end

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

template "/etc/apache2/sites-available/000-default.conf" do
 source "vhost.erb"
 variables({ :doc_root => node['main']['doc_root'] })
 action :create
 notifies :restart, resources(:service => "apache2")
end
    1. Exemple de recette Voyons maintenant une recette 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 la recette dans une configuration simplifiée, à l'aide d'une machine virtuelle gérée par Vagrant.

Vous trouverez ci-dessous la recette complète :

node.default['main']['doc_root'] = "/vagrant/web"

execute "apt-get update" do
 command "apt-get update"
end

apt_package "apache2" do
 action :install
end

service "apache2" do
 action [ :enable, :start ]
end

directory node['main']['doc_root'] do
 owner 'www-data'
 group 'www-data'
 mode '0644'
 action :create
end

cookbook_file "#{node['main']['doc_root']}/index.html" do
 source 'index.html'
 owner 'www-data'
 group 'www-data'
 action :create
end

template "/etc/apache2/sites-available/000-default.conf" do
 source "vhost.erb"
 variables({ :doc_root => node['main']['doc_root'] })
 action :create
 notifies :restart, resources(:service => "apache2")
end

      1. Recette expliquée
        1. ligne 1 La recette commence par une définition attribute, node['main']['doc_root']. Nous aurions pu utiliser une simple variable locale ici, mais dans la plupart des scénarios d'utilisation, les recettes doivent définir des variables globales qui seront utilisées à partir des recettes incluses ou d'autres fichiers. Pour ces situations, il est nécessaire de créer un attribut au lieu d'une variable locale, car cette dernière a une portée limitée.
        1. lines 3-5 Cette ressource execute exécute un apt-get update.
        1. lines 7-10 Cette ressource apt_package installe le package apache2.
        1. lines 12-15 Cette ressource service active et démarre le service apache2. Plus tard, nous devrons notifier cette ressource pour un redémarrage du service. Il est important que la définition de service vienne avant toute ressource qui tente de notifier un service, sinon vous obtiendrez une erreur.
        1. lines 17-22 Cette ressource directory utilise la valeur définie par l'attribut personnalisé node['main']['doc_root'] pour créer un répertoire qui servira de notre document root.
        1. lignes 24-29 Une ressource cookbook_file est utilisée pour copier un fichier local sur un serveur distant. Cette ressource copiera notre fichier index.html et le placera à l'intérieur de la racine du document que nous avons créée dans une tâche précédente.
        1. lines 31-36 Enfin, cette ressource template applique notre modèle d'hôte virtuel Apache et notifie le service apache2 pour un redémarrage.
    1. Conclusion Chef est un puissant outil de gestion de la configuration qui exploite le langage Ruby pour automatiser le provisionnement et le déploiement des serveurs. Il vous donne la liberté d'utiliser les fonctionnalités de langage standard pour une flexibilité maximale, tout en offrant également des DSL personnalisés pour certaines ressources.