Source : https://github.com/goffinet/guide-ansible-playbooks/tree/master/ansible-linux/11-lamp-drupal-ubuntu
---
- 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:
- name: restart apache
service: name=apache2 state=restarted
Le livre de jeu est accompagné d’un fichier vars.yml
qui contient des variables :
---
# The core version you want to use (e.g. 8.5.x, 8.6.x).
drupal_core_version: "8.6.x"
# The path where Drupal will be downloaded and installed.
drupal_core_path: "/var/www/drupal-{{ drupal_core_version }}-dev"
# The resulting domain will be [domain].test (with .test appended).
domain: "{{ inventory_hostname }}"
# Your Drupal site name.
drupal_site_name: "Drupal Test"
Le cache apt sera mis-à-jour avant l’exécution de toute tâche.
---
- hosts: all
pre_tasks:
- name: Update apt cache if needed.
apt: update_cache=yes cache_valid_time=3600
Un “handler
” est une tâche qui s’exécute sur un appel “notify
” mais une seule fois dans le jeu.
---
- hosts: all
handlers:
- name: restart apache
service: name=apache2 state=restarted
tasks:
- name: Enable Apache rewrite module (required for Drupal).
apache2_module: name=rewrite state=present
notify: restart apache
Remise en forme de l’indentation et placement des guillemets :
- name: Add ondrej repository for later versions of PHP.
apt_repository: repo='ppa:ondrej/php' update_cache=yes
en :
- name: "Add ondrej repository for later versions of PHP."
apt_repository:
repo: 'ppa:ondrej/php'
update_cache: yes
Correction : la boucle with_item
est dépréciée au profit de la boucle loop
.
On peut appeler une variable valorisée en liste dans l’exécution d’un module :
- name: "loop illustration"
hosts: localhost
vars:
fruits:
- apple
- orange
- pineapple
tasks:
- name: "Print my Fruits"
debug:
msg: "{{ item }}"
loop: "{{ fruits }}"
Il serait intéressant de mettre en variable toutes les listes qui sont dans les tâches.
Notons que le module apt
supporte directement les listes sans passer par une boucle.
[DEPRECATION WARNING]: Invoking "apt" only once while using a loop via squash_actions is deprecated. Instead of
using a loop to supply multiple items and specifying `name: {{ item }}`, please use `name: [u'python-apt', u
'python-pycurl']` and remove the loop. This feature will be removed in version 2.11. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.
---
- name: "loop with register"
hosts: localhost
vars:
fruits:
- apple
- orange
- pineapple
tasks:
- name: "loop + register"
shell: "echo {{ item }}"
loop: "{{ fruits }}"
register: echo
- name: "print result"
debug:
msg: "{{ echo }}"
Mieux :
---
- name: "loop with register"
hosts: localhost
vars:
fruits:
- apple
- orange
- pineapple
tasks:
- name: "loop register"
shell: "echo {{ item }}"
loop: "{{ fruits }}"
register: echo
- name: "print result"
debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ echo.results }}"
On trouvera un seul modèle Jinja2 dans templates/drupal.test.conf.j2
assez simple :
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName {{ domain }}.test
ServerAlias www.{{ domain }}.test
DocumentRoot {{ drupal_core_path }}
<Directory "{{ drupal_core_path }}">
Options FollowSymLinks Indexes
AllowOverride All
</Directory>
</VirtualHost>
- name: "Get software for apt repository management."
apt:
name:
- python-apt
- python-pycurl
state: present
- name: "Add ondrej repository for later versions of PHP."
apt_repository:
repo: 'ppa:ondrej/php'
update_cache: yes
- name: "Install Apache, MySQL, PHP, and other dependencies."
apt:
name:
- git
- curl
- unzip
- sendmail
- apache2
- php7.1-common
- php7.1-cli
- php7.1-dev
- php7.1-gd
- php7.1-curl
- php7.1-json
- php7.1-opcache
- php7.1-xml
- php7.1-mbstring
- php7.1-pdo
- php7.1-mysql
- php-apcu
- libpcre3-dev
- libapache2-mod-php7.1
- python-mysqldb
- mysql-server
- ufw
state: present
- name: "Disable the firewall (since this is for local dev only)."
service:
name: ufw
state: stopped
- name: "Start Apache, MySQL, and PHP."
service:
name: "{{ item }}"
state: started
enabled: yes
loop:
- apache2
- mysql
Illustration de :
- name: "Enable Apache rewrite module (required for Drupal)."
apache2_module:
name: rewrite
state: present
notify: "restart apache"
- name: "Add Apache virtualhost for Drupal 8."
template:
src: "templates/drupal.test.conf.j2"
dest: "/etc/apache2/sites-available/{{ domain }}.test.conf"
owner: root
group: root
mode: 0644
notify: "restart apache"
- name: "Symlink Drupal virtualhost to sites-enabled."
file:
src: "/etc/apache2/sites-available/{{ domain }}.test.conf"
dest: "/etc/apache2/sites-enabled/{{ domain }}.test.conf"
state: link
notify: "restart apache"
- name: "Remove default virtualhost file."
file:
path: "/etc/apache2/sites-enabled/000-default.conf"
state: absent
notify: "restart apache"
Exemple d’utilisation du module lineinfile - Manage lines in text files.
- name: "Adjust OpCache memory setting."
lineinfile:
dest: "/etc/php/7.1/apache2/conf.d/10-opcache.ini"
regexp: "^opcache.memory_consumption"
line: "opcache.memory_consumption = 96"
state: present
notify: "restart apache"
Exemple d’usage du module mysql_db - Add or remove MySQL databases from a remote host et du module mysql_user - Adds or removes a user from a MySQL database
- name: Remove the MySQL test database.
mysql_db:
db: test
state: absent
- name: "Create a MySQL database for Drupal."
mysql_db:
db: "{{ domain }}"
state: present
- name: "Create a MySQL user for Drupal."
mysql_user:
name: "{{ domain }}"
password: "1234"
priv: "{{ domain }}.*:ALL"
host: localhost
state: present
Illustration :
On utilise le module command - Executes a command on a remote node qui exécute un script php et le module shell - Execute commands in nodes pour exécuter un programme du shell (ici mv
).
Installation de composer qui est nécessaire à Drus, l’outil cli de Drupal.
- name: "Download Composer installer."
get_url:
url: https://getcomposer.org/installer
dest: /tmp/composer-installer.php
mode: 0755
- name: "Run Composer installer."
command: >
php composer-installer.php
chdir=/tmp
creates=/usr/local/bin/composer
- name: "Move Composer into globally-accessible location."
shell: >
mv /tmp/composer.phar /usr/local/bin/composer
creates=/usr/local/bin/composer
Dans la suite, on installe Drush, notamment avec le module git - Deploy software (or files) from git checkouts.
- name: "Check out drush 8.x branch."
git:
repo: https://github.com/drush-ops/drush.git
version: 8.x
dest: /opt/drush
- name: "Install Drush dependencies with Composer."
shell: >
/usr/local/bin/composer install
chdir=/opt/drush
creates=/opt/drush/vendor/autoload.php
- name: "Create drush bin symlink."
file:
src: /opt/drush/drush
dest: /usr/local/bin/drush
state: link
- name: "Check out Drupal Core to the Apache docroot."
git:
repo: https://git.drupal.org/project/drupal.git
version: "{{ drupal_core_version }}"
dest: "{{ drupal_core_path }}"
- name: "Install Drupal dependencies with Composer."
shell: >
/usr/local/bin/composer install
chdir={{ drupal_core_path }}
creates={{ drupal_core_path }}/vendor/autoload.php
- name: "Install Drupal."
command: >
drush si -y --site-name="{{ drupal_site_name }}"
--account-name=admin
--account-pass=admin
--db-url=mysql://{{ domain }}:1234@localhost/{{ domain }}
--root={{ drupal_core_path }}
creates={{ drupal_core_path }}/sites/default/settings.php
notify: restart apache
# SEE: https://drupal.org/node/2121849#comment-8413637
- name: "Set permissions properly on settings.php."
file:
path: "{{ drupal_core_path }}/sites/default/settings.php"
mode: 0744
- name: "Set permissions properly on files directory."
file:
path: "{{ drupal_core_path }}/sites/default/files"
mode: 0777
state: directory
recurse: yes
with_item
.blocks
de tâches et des “tags” qui correspondent aux étapes du déploiement.Source :
https://github.com/goffinet/guide-ansible-playbooks/tree/master/ansible-linux/14-includes-ansible
https://github.com/goffinet/guide-ansible-playbooks/tree/master/ansible-linux/14-roles-ansible
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
.
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
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
Reprise par “import” de l’exercice 11. Drupal sur plateforme LAMP sur Ubuntu.
├── 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
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
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ôlevars
- 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
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 :
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
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"
Un document qui est certainement à consulter Best Practices