Comment gérer des environnements à plusieurs étages avec Ansible

introduction

Ansible est un puissant système de gestion de la configuration utilisé pour configurer et gérer les infrastructures et les applications dans des environnements variés. Bien qu’Ansible offre une syntaxe facile à lire, des flux de travail flexibles et des outils puissants, il peut être difficile de gérer un grand nombre d’hôtes lorsque ceux-ci varient en fonction de l’environnement de déploiement et des fonctionnalités.

Dans ce guide, nous aborderons certaines stratégies d’utilisation d’Ansible pour travailler avec des environnements de déploiement à plusieurs étages. En règle générale, les exigences relatives aux différentes étapes entraînent différents nombres et configurations de composants. Par exemple, la mémoire requise par un serveur de développement peut être différente de celle utilisée pour la mise en attente et la production, et il est important de pouvoir contrôler explicitement la manière dont les variables représentant ces exigences sont hiérarchisées. Dans cet article, nous allons examiner certaines manières de résumer ces différences et certaines constructions fournies par Ansible pour encourager la réutilisation de la configuration.

Stratégies incomplètes pour la gestion d’environnements à plusieurs étages avec Ansible

Bien que vous puissiez gérer les environnements au sein d’Ansible de différentes manières, Ansible n’offre pas de solution personnalisée. Au lieu de cela, il fournit de nombreuses constructions pouvant être utilisées pour gérer des environnements et permettant à l’utilisateur de choisir.

L’approche que nous allons démontrer dans ce guide s’appuie sur Ansible * groupe de variables * et * inventaires multiples *. Cependant, plusieurs autres stratégies méritent d’être envisagées. Nous explorerons certaines de ces idées ci-dessous et nous expliquerons pourquoi elles pourraient poser des problèmes lors de leur mise en œuvre dans des environnements complexes.

Si vous souhaitez vous familiariser avec la stratégie recommandée par Ansible, passez directement à la section https://www.digitalocean.com/community/tutorials/how-to-manage-multistage-environnements-avec-ansible#ansible-recommended-strategy -utilisant-groupes-et-inventaires-multiples [en utilisant des groupes Ansible et des inventaires multiples].

Se fier uniquement à des variables de groupe

À première vue, il peut sembler que les variables de groupe fournissent toute la séparation entre les environnements requis par Ansible. Vous pouvez désigner certains serveurs comme appartenant à votre environnement de développement, tandis que d’autres peuvent être affectés aux zones de transfert et de production. Ansible facilite la création de groupes et leur affecte des variables.

L’intersection de groupe pose toutefois de graves problèmes pour ce système. Les groupes sont souvent utilisés pour classer plus d’une dimension. Par exemple:

  • environnements de déploiement (local, dev, scène, prod, etc.)

  • fonctionnalité hôte (serveurs Web, serveurs de base de données, etc.)

  • Région de centre de données (NYC, SFO, etc.)

Dans ces cas, les hôtes seront généralement dans un groupe par catégorie. Par exemple, un hôte peut être un serveur Web (fonctionnel) sur scène (environnement de déploiement) dans NYC (région de centre de données).

Si la même variable est définie par plusieurs groupes pour un hôte, Ansible n’a aucun moyen de spécifier explicitement la priorité. Vous préférerez peut-être que les variables associées aux environnements de déploiement remplacent d’autres valeurs, mais Ansible ne permet pas de définir cela.

Au lieu de cela, * Ansible utilise la dernière valeur chargée *. Dans la mesure où Ansible évalue les groupes par ordre alphabétique, la variable associée au nom du groupe figurant en dernier dans l’ordre des dictionnaires sera gagnante. Ce comportement est prévisible, mais la gestion explicite de l’alphabétisation des noms de groupe n’est pas idéale du point de vue administratif.

Utiliser les enfants du groupe pour établir une hiérarchie

Ansible vous permet d’affecter des groupes à d’autres groupes en utilisant la syntaxe + [: enfants] + dans l’inventaire. Cela vous donne la possibilité de nommer certains groupes membres d’autres groupes. Les groupes d’enfants ont la possibilité de remplacer les variables définies par les groupes de parents.

En règle générale, cela est utilisé pour la classification naturelle. Par exemple, nous pourrions avoir un groupe appelé + environnements qui comprend les groupes` + dev`, + stage,` + prod`. Cela signifie que nous pourrions définir des variables dans le groupe environment + et les remplacer dans le groupe + dev +. Vous pouvez également avoir un groupe parent appelé + functions + qui contient les groupes + web,` + database` et + load balancer.

Cette utilisation ne résout pas le problème de l’intersection des groupes, car les groupes d’enfants ne font que remplacer leurs parents. Les groupes enfants peuvent remplacer les variables dans le parent, mais l’organisation ci-dessus n’a pas établi de relation entre les catégories de groupes, comme + environnements + et + fonctions +. La priorité des variables entre les deux catégories n’est toujours pas définie.

Il est possible d’exploiter ce système en définissant une appartenance à un groupe non naturel. Par exemple, si vous souhaitez établir la priorité suivante, de la priorité la plus élevée à la plus faible:

  • environnement de développement

  • Région

  • une fonction

Vous pouvez affecter une appartenance à un groupe qui ressemble à ceci:

Exemple d’inventaire

. . .
[function:children]
web
database
loadbalancer


[region:children]
nyc
sfo


[environments:children]
dev
stage
prod

Nous avons établi ici une hiérarchie qui permet aux variables régionales de remplacer les variables fonctionnelles puisque le groupe + région + est un enfant du groupe + fonction +. De même, les variables définies dans les groupes + environnements + peuvent remplacer n’importe lequel des autres. Cela signifie que si nous affectons une valeur différente à la même variable dans les groupes + dev +, + nyc + et + web +, un hôte appartenant à chacun d’eux utilisera la variable à partir de '+ dev + `.

Cela permet d’atteindre le résultat souhaité et est également prévisible. Cependant, cela n’est pas intuitif et cela brouille la distinction entre les vrais enfants et les enfants nécessaires pour établir la hiérarchie. Ansible est conçu pour que sa configuration soit claire et facile à suivre, même pour les nouveaux utilisateurs. Ce type de contournement compromet cet objectif.

Utilisation de constructions Ansible autorisant un ordre de chargement explicite

Dans Ansible, quelques constructions autorisent un ordre de chargement de variable explicite, à savoir + vars_files + et + include_vars +. Ceux-ci peuvent être utilisés dans Ansible joue pour charger explicitement des variables supplémentaires dans l’ordre défini dans le fichier. La directive + vars_files + est valide dans le contexte d’une lecture, alors que le module + include_vars + peut être utilisé dans des tâches.

L’idée générale est de définir uniquement les variables d’identification de base dans + group_vars +, puis de les exploiter pour charger les fichiers de variables corrects avec le reste des variables souhaitées.

Par exemple, quelques-uns des fichiers + group_vars + peuvent ressembler à ceci:

group_vars / dev

---
env: dev

group_vars / stage

---
env: stage

group_vars / web

---
function: web

groupe_vars / base de données

---
function: database

Nous aurions alors un fichier vars distinct qui définit les variables importantes pour chaque groupe. Celles-ci sont généralement conservées dans un répertoire + vars + séparé pour plus de clarté. Contrairement aux fichiers + group_vars +, lorsqu’ils traitent + include_vars +, les fichiers doivent inclure une extension de fichier + .yml +.

Imaginons qu’il faille attribuer à la variable + server_memory_size + une valeur différente dans chaque fichier + vars +. Vos serveurs de développement seront probablement plus petits que vos serveurs de production. De plus, vos serveurs Web et vos serveurs de base de données peuvent avoir des besoins en mémoire différents:

vars / dev.yml

---
server_memory_size: 512mb

vars / prod.yml

---
server_memory_size: 4gb

vars / web.yml

---
server_memory_size: 1gb

vars / database.yml

---
server_memory_size: 2gb

Nous pourrions ensuite créer un playbook qui charge explicitement le fichier + vars + correct en fonction des valeurs attribuées à l’hôte à partir des fichiers + group_vars +. L’ordre des fichiers chargés déterminera la priorité, la dernière valeur étant gagnante.

Avec + vars_files +, un exemple de jeu ressemblerait à ceci:

example_play.yml

---
- name: variable precedence test
 hosts: all
 vars_files:
   - "vars/{{ env }}.yml"
   - "vars/{{ function }}.yml"
 tasks:
   - debug: var=server_memory_size

Puisque les groupes fonctionnels sont chargés en dernier, la valeur + server_memory_size + serait extraite des fichiers + var / web.yml + et + var / database.yml +:

ansible-playbook -i inventory example_play.yml
Output. . .
TASK [debug] *******************************************************************
ok: [host1] => {
   "server_memory_size":
}
ok: [host2] => {
   "server_memory_size":
}
ok: [host3] => {
   "server_memory_size":
}
ok: [host4] => {
   "server_memory_size":
}
. . .

Si nous modifions l’ordre de chargement des fichiers, nous pouvons attribuer une priorité plus élevée aux variables d’environnement de déploiement:

example_play.yml

---
- name: variable precedence test
 hosts: all
 vars_files:
   - "vars/{{ function }}.yml"
   - "vars/{{ env }}.yml"
 tasks:
   - debug: var=server_memory_size

L’exécution du livre de lecture montre à nouveau les valeurs appliquées à partir des fichiers de l’environnement de déploiement:

ansible-playbook -i inventory example_play.yml
Output. . .
TASK [debug] *******************************************************************
ok: [host1] => {
   "server_memory_size":
}
ok: [host2] => {
   "server_memory_size":
}
ok: [host3] => {
   "server_memory_size":
}
ok: [host4] => {
   "server_memory_size":
}
. . .

Le playbook équivalent utilisant + include_vars +, qui fonctionne comme une tâche, ressemblerait à ceci:

---
- name: variable precedence test
 hosts: localhost
 tasks:
   - include_vars:
       file: "{{ item }}"
     with_items:
       - "vars/{{ function }}.yml"
       - "vars/{{ env }}.yml"
   - debug: var=server_memory_size

C’est un domaine dans lequel Ansible permet un ordre explicite, ce qui peut être très utile. Cependant, comme dans les exemples précédents, il existe des inconvénients importants.

Tout d’abord, utiliser + vars_files + et + include_vars + nécessite de placer des variables étroitement liées à des groupes dans un emplacement différent. L’emplacement + group_vars + devient un stub pour les variables réelles situées dans le répertoire + vars +. Cela ajoute encore une fois de la complexité et diminue la clarté. L’utilisateur doit faire correspondre les fichiers de variables corrects à l’hôte, ce que Ansible fait automatiquement lorsqu’il utilise + group_vars +.

Plus important encore, le recours à ces techniques les rend obligatoires. Chaque playbook nécessite une section qui charge explicitement les fichiers variables corrects dans le bon ordre. Les livres de lecture sans cela ne pourront pas utiliser les variables associées. De plus, exécuter la commande + ansible + pour des tâches ad-hoc sera presque totalement impossible pour tout ce qui repose sur des variables.

Stratégie Ansible recommandée: utilisation de groupes et d’inventaires multiples

Jusqu’à présent, nous avons examiné certaines stratégies de gestion des environnements à plusieurs étages et discuté des raisons pour lesquelles ils pourraient ne pas constituer une solution complète. Cependant, le projet Ansible offre quelques suggestions sur la meilleure façon d’abréger votre infrastructure d’un environnement à l’autre.

L’approche recommandée consiste à travailler avec des environnements à plusieurs étages en séparant complètement chaque environnement d’exploitation. Au lieu de conserver tous vos hôtes dans un seul fichier d’inventaire, un inventaire est maintenu pour chacun de vos environnements. Des répertoires séparés + group_vars + sont également conservés.

La structure de répertoire de base ressemblera à ceci:

.
├── ansible.cfg
├── environments/         # Parent directory for our environment-specific directories
│   │
│   ├── dev/              # Contains all files specific to the dev environment
│   │   ├── group_vars/   # dev specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the dev environment
│   │
│   ├── prod/             # Contains all files specific to the prod environment
│   │   ├── group_vars/   # prod specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the prod environment
│   │
│   └── stage/            # Contains all files specific to the stage environment
│       ├── group_vars/   # stage specific group_vars files
│       │   ├── all
│       │   ├── db
│       │   └── web
│       └── hosts         # Contains only the hosts in the stage environment
│
├── playbook.yml
│
└── . . .

Comme vous pouvez le constater, chaque environnement est distinct et compartimenté. Les répertoires d’environnement contiennent un fichier d’inventaire (nommé arbitrairement + hôtes +) et un répertoire + group_vars +.

Il y a des duplications évidentes dans l’arborescence. Il existe des fichiers + web + et + db + pour chaque environnement individuel. Dans ce cas, la duplication est souhaitable. Les modifications de variables peuvent être appliquées à tous les environnements en modifiant d’abord les variables d’un environnement et en les déplaçant après les tests, comme vous le feriez avec des modifications de code ou de configuration. Les variables + group_vars + suivent les valeurs par défaut actuelles pour chaque environnement.

Une des limitations est l’impossibilité de sélectionner tous les hôtes par fonction dans tous les environnements. Heureusement, cela tombe dans la même catégorie que le problème de duplication variable ci-dessus. S’il est parfois utile de sélectionner tous vos serveurs Web pour une tâche, vous souhaitez presque toujours appliquer les modifications à votre environnement, un à la fois. Cela permet d’éviter que des erreurs n’affectent votre environnement de production.

Définition de variables inter-environnements

Une chose qui n’est pas possible dans la configuration recommandée est le partage de variable entre les environnements. Nous pouvons mettre en œuvre le partage de variables entre environnements de plusieurs manières. L’un des plus simples consiste à exploiter la capacité d’Ansible à utiliser des répertoires à la place de fichiers. Nous pouvons remplacer le fichier + all + dans chaque répertoire + group_vars + par un répertoire + all +.

À l’intérieur du répertoire, nous pouvons à nouveau définir toutes les variables spécifiques à l’environnement d’un fichier. Nous pouvons ensuite créer un lien symbolique vers un emplacement de fichier contenant des variables inter-environnements. Ces deux éléments seront appliqués à tous les hôtes de l’environnement.

Commencez par créer un fichier de variables inter-environnements quelque part dans la hiérarchie. Dans cet exemple, nous le placerons dans le répertoire + environnements +. Placez toutes les variables inter-environnements dans ce fichier:

cd environments
touch 000_cross_env_vars

Ensuite, déplacez-vous dans l’un des répertoires + group_vars +, renommez le fichier + all + et créez le répertoire + all +. Déplacez le fichier renommé dans le nouveau répertoire:

cd dev/group_vars
mv all env_specific
mkdir all
mv env_specific all/

Ensuite, vous pouvez créer un lien symbolique vers le fichier de variable inter-environnement:

cd all/
ln -s ../../../000_cross_env_vars .

Lorsque vous avez terminé les étapes ci-dessus pour chacun de vos environnements, la structure de votre répertoire ressemblera à ceci:

.
├── ansible.cfg
├── environments/
│   │
│   ├──
│   │
│   ├── dev/
│   │   ├── group_vars/
│   │   │   ├──
│       │   │   ├──
│   │   │   │   └──
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   ├── prod/
│   │   ├── group_vars/
│   │   │   ├──
│   │   │   │   ├──
│   │   │   │   └──
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   └── stage/
│       ├── group_vars/
│       │   ├──
│       │   │   ├──
│       │   │   └──
│       │   ├── db
│       │   └── web
│       └── hosts
│
├── playbook.yml
│
└── . . .

Les variables définies dans le fichier + 000_cross_env_vars + seront disponibles pour chacun des environnements de faible priorité.

Définition d’un inventaire d’environnement par défaut

Il est possible de définir un fichier d’inventaire par défaut dans le fichier + ansible.cfg +. C’est une bonne idée pour plusieurs raisons.

Tout d’abord, cela vous permet de laisser les indicateurs d’inventaire explicites dans + ansible et` + ansible-playbook`. Donc au lieu de taper:

ansible -i environments/dev -m ping

Vous pouvez accéder à l’inventaire par défaut en tapant:

ansible -m ping

Deuxièmement, la définition d’un inventaire par défaut permet d’éviter que des modifications indésirables n’affectent accidentellement les environnements de transfert ou de production. Par défaut, votre environnement de développement affecte les modifications des infrastructures les moins importantes. La promotion des modifications dans de nouveaux environnements est alors une action explicite qui nécessite l’indicateur + -i +.

Pour définir un inventaire par défaut, ouvrez votre fichier + ansible.cfg +. Cela peut se trouver dans le répertoire racine de votre projet ou dans + / etc / ansible / ansible.cfg + en fonction de votre configuration.

nano ansible.cfg

Comme mentionné ci-dessus, il est recommandé de définir votre environnement de développement comme inventaire par défaut. Remarquez comment nous pouvons sélectionner l’ensemble du répertoire de l’environnement à la place du fichier hosts qu’il contient:

[defaults]
inventory = ./environments/dev

Vous devriez maintenant pouvoir utiliser votre inventaire par défaut sans l’option + -i +. Les inventaires autres que ceux par défaut nécessiteront toujours l’utilisation de + -i +, ce qui les aidera à les protéger contre les modifications accidentelles.

Conclusion

Dans cet article, nous avons exploré la flexibilité fournie par Ansible pour la gestion de vos hôtes dans plusieurs environnements. Cela permet aux utilisateurs d’adopter de nombreuses stratégies différentes pour gérer la priorité variable lorsqu’un hôte appartient à plusieurs groupes, mais l’ambiguïté et le manque de direction officielle peuvent être difficiles. Comme pour toute technologie, le meilleur choix pour votre organisation dépendra de vos cas d’utilisation et de la complexité de vos besoins. La meilleure façon de trouver une stratégie qui répond à vos besoins est d’expérimenter. Partagez votre cas d’utilisation et approche dans les commentaires ci-dessous.

Related