Inclusions, imports et rôles

Source

Source :

Objectifs

  • Transformer les deux scripts d’application Web avec des includes sur debian/ubuntu (Drupal) et en rôle sur Centos (Node.js).

1. Inclusions, imports et rôles

2. Inclusions

Il est possible d’ “inclure” dans un livre de jeu des fichiers qui comprennent une liste de jeux ou de tâches avec un module include*. Notons que le module include est déprécié depuis la version Ansible 2.4 au profit de include_tasks et import_tasks

Le module include_tasks est appelé sur le bon niveau hiérarchique. Un liste de tâches se trouvera sous la directive tasks.

Par exemple :

- hosts: all
  tasks:
    - debug:
        msg: task1
    - name: Include task list in play
      include_tasks: stuff.yaml
    - debug:
        msg: task10
- hosts: all
  tasks:
    - debug:
        msg: task1
    - name: Include task list in play only if the condition is true
      include_tasks: other_stuff.yaml
      when: hostvar is defined

Ce livre de jeu est constitué de deux jeux. Le premier jeu comprend trois tâches. La seconde est un “include” d’un fichier stuff.yaml constitué d’une liste de tâches. Le second jeu comprend deux tâches dont la dernière prend la liste de tâche d’un autre fichier other_stuff.yaml.

3. Imports

On connait aussi le module import_tasks dont le fonctionnement est similaire à include_tasks.

- hosts: all
  tasks:
    - debug:
        msg: task1
    - name: Import task list in play
      import_tasks: stuff.yaml
    - debug:
        msg: task10

ou encore :

tasks:
- import_tasks: wordpress.yml
  vars:
    wp_user: timmy
- import_tasks: wordpress.yml
  vars:
    wp_user: alice
- import_tasks: wordpress.yml
  vars:
    wp_user: bob

Quelle est la différence entre import_* et include_* ?

Toutes les instructions import_* sont pré-traitées au moment de l’analyse des livres de jeu. Toutes les instructions include_* sont traitées au fur et à mesure lors de l’exécution du livre de jeu.

Autrement dit, l’importation est statique, l’inclusion est dynamique.

Sur base de l’expérience, on devrait utiliser import lorsque l’on traite avec des “unités” logiques. Par exemple, une longue liste de tâches dans des fichiers de sous-tâches :

main.yml:

- import_tasks: prepare_filesystem.yml
- import_tasks: install_prerequisites.yml
- import_tasks: install_application.yml

Mais on utiliserait de préférence include pour traiter différents flux de travail et prendre des décisions en fonction de “gathered facts” de manière dynamique :

install_prerequisites.yml:

- include_tasks: prerequisites_{{ ansible_os_family | lower }}.yml

import_playbook

Le module import_playbook intervient plus au niveau du livre de jeu :

---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml

ou encore :

- hosts: localhost
  tasks:
    - debug:
        msg: play1

- name: Include a play after another play
  import_playbook: otherplays.yaml

4. Exercice “import”

Reprise par “import” de l’exercice 11. Drupal sur plateforme LAMP sur Ubuntu.

Structure proposée

├── provisioning
│   ├── handlers
│   │   └── handlers.yml
│   ├── playbook.yml
│   ├── tasks
│   │   ├── apache.yml
│   │   ├── common.yml
│   │   ├── composer.yml
│   │   ├── drupal.yml
│   │   ├── drush.yml
│   │   ├── mysql.yml
│   │   └── php.yml
│   ├── templates
│   │   └── drupal.test.conf.j2
│   └── vars.yml
├── README.md
└── Vagrantfile

Fichiers

Fichier playbook.yml

# file ./playbook.yml
---
- hosts: all
  become: yes

  vars_files:
    - vars.yml

  pre_tasks:
    - name: Update apt cache if needed.
      apt: update_cache=yes cache_valid_time=3600

  handlers:
    - import_tasks: handlers/handlers.yml

  tasks:
    - import_tasks: tasks/common.yml
    - import_tasks: tasks/apache.yml
    - import_tasks: tasks/php.yml
    - import_tasks: tasks/mysql.yml
    - import_tasks: tasks/composer.yml
    - import_tasks: tasks/drush.yml
    - import_tasks: tasks/drupal.yml

5. Roles Ansible-Galaxy

Ansible Galaxy fait référence au site Web de Galaxy à partir duquel les utilisateurs peuvent partager des rôles. Il fait aussi référence à un outil en ligne de commande pour l’installation, la création et la gestion de rôles à partir de dépôts git.

Les rôles permettent de charger automatiquement certains fichiers vars_files, tasks et handlers en fonction d’une structure de fichier connue. Le regroupement de contenu par rôles permet également de les partager facilement avec d’autres utilisateurs.

En bref, une organisation en rôle n’est jamais qu’une manière d’abstraire son livre de jeu. Toutes les règles de conception d’un livre de jeu sont respectées sur base d’une structure de fichiers et de dossiers connue.

Exemple de structure d’un projet utilisant des rôles.

site.yml
webservers.yml
fooservers.yml
roles/
   common/
     tasks/
     handlers/
     files/
     templates/
     vars/
     defaults/
     meta/
   webservers/
     tasks/
     defaults/
     meta/

Les rôles s’attendent à ce que les fichiers se trouvent dans certains répertoires dont le nom est connu. Les rôles doivent inclure au moins un de ces répertoires, mais il est parfaitement fonctionnel d’exclure ceux qui ne sont pas nécessaires. Lorsqu’il est utilisé, chaque répertoire doit contenir un fichier main.yml, qui contient le contenu adéquat :

  • tasks - contient la liste principale des tâches à exécuter par le rôle.
  • handlers - contient les handlers pouvant être utilisés par ce rôle ou même en dehors de ce rôle.
  • defaults - variables par défaut du rôle
  • vars - autres variables pour le rôle.
  • files - contient des fichiers pouvant être déployés via ce rôle.
  • templates - contient des modèles pouvant être déployés via ce rôle.
  • meta - définit des métadonnées pour ce rôle.

D’autres fichiers YAML peuvent être inclus dans certains répertoires. Par exemple, il est courant d’inclure des tâches spécifiques à la plate-forme à partir du fichier tasks/main.yml :

# roles/example/tasks/main.yml
- name: added in 2.4, previously you used 'include'
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'

# roles/example/tasks/redhat.yml
- yum:
    name: "httpd"
    state: present

# roles/example/tasks/debian.yml
- apt:
    name: "apache2"
    state: present

On appelle des rôles à partir d’un livre de jeu par exemple webservers.yml :

---
# file: webservers.yml
- hosts: webservers
  roles:
    - common
    - webtier

Le livre de jeu principal de l’infrastrucure site.yml serait le suivant :

---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml

include_role

Le module include_role permet de charger un rôle comme une tâche dynamique.

- name: Run tasks/other.yaml instead of 'main'
  include_role:
    name: myrole
    tasks_from: other

Création d’un rôle

Création d’un rôle :

cd roles
ansible-galaxy init newrole
- newrole was created successfully
tree newrole/
newrole/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

6. Mise en roles

Reprise par role de l’exercice 10. Déployer un serveur Node.JS sur Centos pour l’installation de node.js.

./
├── README.md
├── Vagrantfile
├── app
│   ├── app.js
│   └── package.json
├── playbook.yml
└── roles
    └── nodejs
        ├── meta
        │   └── main.yml
        └── tasks
            └── main.yml

Fichier playbook.yml :

---
- hosts: all

  vars:
    node_apps_location: /usr/local/opt/node

  pre_tasks:
    - name: Install Remi repo.
      yum:
        name: "https://rpms.remirepo.net/enterprise/remi-release-7.rpm"
        state: present

    - name: Import Remi GPG key.
      rpm_key:
        key: "https://rpms.remirepo.net/RPM-GPG-KEY-remi"
        state: present

    - name: Install EPEL repo.
      yum: name=epel-release state=present

    - name: Ensure firewalld is stopped (since this is a test server).
      service: name=firewalld state=stopped

  roles:
    - nodejs

  tasks:
    - name: Ensure Node.js app folder exists.
      file: "path={{ node_apps_location }} state=directory"

    - name: Copy example Node.js app to server.
      copy: "src=app dest={{ node_apps_location }}"

    - name: Install app dependencies defined in package.json.
      npm: "path={{ node_apps_location }}/app"

    - name: Check list of running Node.js apps.
      command: forever list
      register: forever_list
      changed_when: false

    - name: Start example Node.js app.
      command: "forever start {{ node_apps_location }}/app/app.js"
      when: "forever_list.stdout.find(node_apps_location + '/app/app.js') == -1"

7. Bonnes pratiques de conception des livres de jeu

Un document qui est certainement à consulter Best Practices

8. Documentation de Roles