Optimiser les temps d'exécution d'Ansible
Quand on développe du code Ansible, rien de plus agaçant que d'attendre la fin de l'exécution. Autre problème comment limiter autant que possible l'indisponibilité d'une application en optimisant l'exécution de vos playbooks Ansible ? Voyons comment résoudre ces problèmes. Ce sera aussi le moyen de vérifier que les anciennes recommandations sont toujours d'actualité.
Mise en place du lab
Je vais utiliser terraform
pour provisionner 6 machines virtuelles
tournant sur ubuntu 22.04
chez AWS avec juste des différences en termes de
capacités (cpu et mémoire). Sur ces machines, je vais utiliser des rôles fournis
par geerlingguy, en l'occurrence celui permettant d'installer mysql
et
apache
.
Pour ceux qui veulent reproduire les tests je vous fournis le code :
Le fichier amin.tf
:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = var.aws_zone
}
variable "aws_project" {
type = string
default = "test-ansible"
description : "The aws project to launch test into."
}
variable "aws_zone" {
type = string
default = "eu-west-3"
description : "The aws region to deploy instances."
}
variable "SSH_KEY" {
type = string
}
data "aws_ami" "latest-ubuntu" {
most_recent = true
filter {
name = "root-device-type"
values = ["ebs"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy**amd64-server*"]
}
}
resource "aws_key_pair" "ssh-key" {
key_name = "ssh-key"
public_key = file("~/.ssh/id_rsa.pub")
}
resource "aws_instance" "test1" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t2.small"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test1"
}
}
resource "aws_instance" "test2" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t3.micro"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test2"
}
}
resource "aws_instance" "test3" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t2.micro"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test3"
}
}
resource "aws_instance" "test4" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t2.small"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test4"
}
}
resource "aws_instance" "test5" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t3.micro"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test5"
}
}
resource "aws_instance" "test6" {
ami = data.aws_ami.latest-ubuntu.id
instance_type = "t2.micro"
associate_public_ip_address = true
key_name = aws_key_pair.ssh-key.key_name
tags = {
Name = "test6"
}
}
resource "aws_default_vpc" "default" {
tags = {
Name = "Default VPC"
}
}
resource "aws_default_security_group" "default" {
vpc_id = "${aws_default_vpc.default.id}"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Le fichier requirements.yml pour installer les rôles :
roles:
- name: geerlingguy.mysql
- name: geerlingguy.apache
On installe les rôles avec la commande ansible-galaxy
:
ansible-galaxy install -r requirements.yml
Le playbook qui servira de test :
- name: Installation de mysql avec le role de geerlinguy
hosts: all
gather_facts: true
become: true
roles:
- geerlingguy.apache
- geerlingguy.mysql
Pour la configuration d'ansible, j'ai généré celle par défaut et que j'ai obtenue avec la commande suivante :
ansible-config init --disabled -t all > ansible.cfg
Le fichier de configuration ansible.cfg
obtenue contient tous les paramètres
en commentaires. Pour chaque run, je ne donnerai que ceux que j'ai activés. Donc
voici celui de la configuration du premier run, intégrant que les modifications
nécessaires au fonctionnement du lab et donc sans optimisations :
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
Prise de mesures initiales
Pour mesurer le temps pris par chacune des taches, je vais activer les
callbacks
suivants :
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
Pour rappel les callbacks
permettent de configurer la sortie des commandes
ansible-playbook
.
Pour ce premier run, je prends les valeurs par défaut de la configuration ansible. Ce run va nous servir d'étalon pour valider les différentes modifications de paramètres. Pour info, je l'ai lancé 5 fois et j'ai pris le run avec le temps moyen.
Pour chacun des runs suivants, je vais détruire et reconstruire les machines
pour que nous nous retrouvions à chaque fois dans les conditions initiales. Pour
cela, je vais utiliser l'option -replace
de terraform
qui remplace
désormais la commande taint
:
terraform apply -replace=aws_instance.test1 -replace=aws_instance.test2 -replace=aws_instance.test3 -replace=aws_instance.test4 -replace=aws_instance.test5 -replace=aws_instance.test6 --auto-approve
Le premier lancement utilisera une version 3.9.15 de python. Sur les machines
cibles, qui tournent sur Ubuntu 22.04
, nous aurons droit à une 3.10.8 !
On installe python 3.9.15 avec la dernière version d'ansible avec pyenv sur le contrôleur ansible.
pyenv install 3.9.15
pyenv virtualenv 3.9.15 ansible-python3.9.15
pyenv shell ansible-python3.9.15
pip install ansible boto3
On lance le premier run :
ansible-playbook -u vagrant test-performance.yml
Playbook run took 0 days, 0 hours, 2 minutes, 56 seconds
lundi 27 mars 2023 12:40:49 +0200 (0:00:05.627) 0:02:56.819 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 55.39s
geerlingguy.apache : Update apt cache. : 23.82s
geerlingguy.apache : Ensure Apache is installed on Debian. : 21.20s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.03s
Gathering Facts : 5.65s
geerlingguy.mysql : restart mysql : 5.63s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.52s
geerlingguy.apache : Get installed version of Apache. : 4.28s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.78s
geerlingguy.apache : Add apache vhosts configuration. : 3.32s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 3.00s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.75s
geerlingguy.apache : Enable Apache mods. : 2.70s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.55s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.27s
geerlingguy.apache : restart apache : 2.08s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.82s
geerlingguy.mysql : Remove MySQL test database. : 1.75s
geerlingguy.mysql : Disallow root login remotely : 1.71s
geerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.54s
lundi 27 mars 2023 12:40:49 +0200 (0:00:05.628) 0:02:56.820 ************
===============================================================================
geerlingguy.mysql : 107.06s
geerlingguy.apache : 64.09s
gather_facts : 5.65s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 176.80s
Optimisation des temps d'exécution du playbook
Utiliser des connexions persistantes
L'établissement d'une connexion SSH est un processus relativement lent qui s'exécute en arrière-plan. Le temps d'exécution global augmente considérablement lorsque vous avez plus de tâches dans un playbook et plus de nœuds.
Par défaut, Ansible utilise OpenSSH
pour les connexions SSH car il supporte la
persistance de connexion via le paramètre ControlPersist
. Si votre contrôleur
utilise une ancienne version d'OpenSSH Ansible utilisera ‘paramiko’.
Vous pouvez activer les paramètres ControlMaster
et ControlPersist
pour
améliorer les performances.
ControlMaster
permet à plusieurs sessions SSH avec un hôte distant d'utiliser une seule connexion réseau.ControlPersist
indique combien de temps le SSH maintient une connexion inactive ouverte en arrière-plan.
Notre nouveau fichier de configuration ansible.cfg
:
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
[ssh_connection]
# -C permit compression
ssh_args=-o ControlMaster=auto -o ControlPersist=600s
Le résultat du run :
Playbook run took 0 days, 0 hours, 3 minutes, 2 seconds
lundi 27 mars 2023 12:49:38 +0200 (0:00:05.764) 0:03:02.314 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 59.27s
geerlingguy.apache : Update apt cache. : 25.66s
geerlingguy.apache : Ensure Apache is installed on Debian. : 24.58s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.23s
Gathering Facts : 6.17s
geerlingguy.mysql : restart mysql : 5.76s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.45s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.61s
geerlingguy.apache : Add apache vhosts configuration. : 3.12s
geerlingguy.apache : Enable Apache mods. : 2.79s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.78s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.73s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.72s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.08s
geerlingguy.apache : restart apache : 2.01s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.76s
geerlingguy.apache : Get installed version of Apache. : 1.58s
geerlingguy.apache : Configure Apache. : 1.52s
geerlingguy.mysql : Remove MySQL test database. : 1.50s
geerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.48s
lundi 27 mars 2023 12:49:38 +0200 (0:00:05.766) 0:03:02.316 ************
===============================================================================
geerlingguy.mysql : 109.77s
geerlingguy.apache : 66.35s
gather_facts : 6.17s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 182.30s
Pas de gain. Mon pipeline est peut-être trop court ?
Activer le pipelining
Lorsque Ansible utilise des connexions SSH, plusieurs opérations se produisent
en arrière-plan pour copier les fichiers, les scripts et les autres commandes
d'exécution. Vous pouvez réduire le nombre de connexions SSH en activant le
paramètre pipelining
(il est désactivé par défaut) dans ansible.cfg :
Mais attention lors de l'utilisation des opérations "sudo:", vous devez d'abord
désactiver "requiretty" dans /etc/sudoers
sur tous les hôtes gérés (cf doc
Ansible.)
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
[ssh_connection]
pipelining = True
Le resultat du run :
Playbook run took 0 days, 0 hours, 2 minutes, 42 seconds
lundi 27 mars 2023 13:00:49 +0200 (0:00:05.583) 0:02:42.610 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 58.68s
geerlingguy.apache : Update apt cache. : 23.88s
geerlingguy.apache : Ensure Apache is installed on Debian. : 23.13s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 8.26s
Gathering Facts : 5.89s
geerlingguy.mysql : restart mysql : 5.58s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 3.80s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.54s
geerlingguy.apache : Add apache vhosts configuration. : 2.36s
geerlingguy.apache : Configure Apache. : 1.95s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.92s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 1.91s
geerlingguy.apache : Get installed version of Apache. : 1.48s
geerlingguy.apache : Enable Apache mods. : 1.38s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.35s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.22s
geerlingguy.apache : restart apache : 1.18s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.15s
geerlingguy.mysql : Remove MySQL test database. : 0.80s
geerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 0.71s
lundi 27 mars 2023 13:00:49 +0200 (0:00:05.583) 0:02:42.610 ************
===============================================================================
geerlingguy.mysql : 97.75s
geerlingguy.apache : 58.95s
gather_facts : 5.89s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 162.59s
Cette fois, nous avons des gains significatifs :)
Configurer le parallélisme
Dans la configuration par défaut d'ansible, le paramètre forks est défini à 5. On peut jouer avec sa valeur surtout si on utilise de gros inventaires.
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
forks = 20
Le résultat du run :
Playbook run took 0 days, 0 hours, 1 minutes, 44 seconds
lundi 27 mars 2023 13:43:02 +0200 (0:00:03.470) 0:01:44.616 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 33.39s
geerlingguy.apache : Update apt cache. : 13.84s
geerlingguy.apache : Ensure Apache is installed on Debian. : 13.32s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 4.62s
geerlingguy.mysql : restart mysql : 3.47s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 3.33s
Gathering Facts : 3.30s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 2.06s
geerlingguy.apache : Add apache vhosts configuration. : 1.65s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.47s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.44s
geerlingguy.apache : Enable Apache mods. : 1.43s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 1.36s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.28s
geerlingguy.apache : Get installed version of Apache. : 1.10s
geerlingguy.apache : restart apache : 1.04s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.03s
geerlingguy.mysql : Create datadir if it does not exist : 0.82s
geerlingguy.mysql : Remove MySQL test database. : 0.81s
geerlingguy.mysql : Check if MySQL is already installed. : 0.80s
lundi 27 mars 2023 13:43:02 +0200 (0:00:03.471) 0:01:44.616 ************
===============================================================================
geerlingguy.mysql : 64.48s
geerlingguy.apache : 36.81s
gather_facts : 3.30s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 104.59s
Gros gain via ce paramètre !
Il y a quelques années, j'ai utilisé Ansible pour peupler une CMDB. Le principe
nmap scrutait le port 22 sur tous les réseaux, en ressortait une liste de 8000
serveurs. Ensuite via Ansible, je testais la connexion, puis un gather_facts
pour les machines qui répondaient. Pour ce besoin, j'ai poussé ce paramètre à
500, mais j'ai dû ajouter des ressources CPU et Mémoire pour que cela passe.
Donc faites bien attention !
Utiliser une version de python récente
Lors de la sortie de python 3.10, il a été annoncé que cette version apporterait
des gains de performances. Voyons son influence en l'utilisant sur le contrôleur
(la machine d'où est lancé ansible). En partant d'une ubuntu 22.04
sur les
cibles la version 3.10.8 de python est déjà installé.
pyenv install 3.10.8
pyenv virtualenv 3.10.8 ansible-python3.10.8
pyenv shell ansible-python3.10.8
pip install ansible boto3
On lance le run :
Playbook run took 0 days, 0 hours, 2 minutes, 57 seconds
lundi 27 mars 2023 13:29:30 +0200 (0:00:07.267) 0:02:57.820 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 58.41s
geerlingguy.apache : Update apt cache. : 24.32s
geerlingguy.apache : Ensure Apache is installed on Debian. : 23.35s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 8.80s
geerlingguy.mysql : restart mysql : 7.27s
Gathering Facts : 5.90s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.49s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.74s
geerlingguy.apache : Add apache vhosts configuration. : 2.93s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.58s
geerlingguy.apache : Enable Apache mods. : 2.51s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.48s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.44s
geerlingguy.apache : restart apache : 2.11s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.00s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.73s
geerlingguy.apache : Get installed version of Apache. : 1.67s
geerlingguy.mysql : Remove MySQL test database. : 1.47s
geerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.38s
geerlingguy.apache : Configure Apache. : 1.33s
lundi 27 mars 2023 13:29:30 +0200 (0:00:07.267) 0:02:57.819 ************
===============================================================================
geerlingguy.mysql : 108.77s
geerlingguy.apache : 63.12s
gather_facts : 5.90s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 177.79s
Peu ou pas de gains !
Désactiver ou améliorer la collecte des facts ansible
La collecte des facts peut être consommatrice, on peut la désactiver en mettant
gather_facts
à false
. Mais dans notre exemple le playbook plante
lamentablement. Eh oui, il teste la distribution pour configurer les machines en
fonction de celles-ci. On va donc ajouter une pre-task permettant de collecter
juste les facts distribution. Le playbook test-performance.yml
devient :
- name: Installation de mysql avec le role de geerlinguy
hosts: all
gather_facts: false
become: true
pre_tasks:
- name: Get minimal facts
ansible.builtin.setup:
gather_subset:
- '!all'
- distribution
roles:
- geerlingguy.mysql
Plus d'infos sur les gather_subset
ici
Playbook run took 0 days, 0 hours, 2 minutes, 50 seconds
lundi 27 mars 2023 13:53:42 +0200 (0:00:06.061) 0:02:50.820 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 54.84s
geerlingguy.apache : Update apt cache. : 23.32s
geerlingguy.apache : Ensure Apache is installed on Debian. : 21.76s
geerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.01s
geerlingguy.mysql : restart mysql : 6.06s
Get minimal facts : 4.77s
geerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.62s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.68s
geerlingguy.apache : Add apache vhosts configuration. : 3.09s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.72s
geerlingguy.apache : Enable Apache mods. : 2.59s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.55s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.54s
geerlingguy.apache : restart apache : 2.02s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.00s
geerlingguy.apache : Configure Apache. : 1.72s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.72s
geerlingguy.mysql : Get MySQL version. : 1.52s
geerlingguy.mysql : Create datadir if it does not exist : 1.51s
geerlingguy.apache : Add vhost symlink in sites-enabled. : 1.48s
lundi 27 mars 2023 13:53:42 +0200 (0:00:06.061) 0:02:50.819 ************
===============================================================================
geerlingguy.mysql : 105.29s
geerlingguy.apache : 60.73s
ansible.builtin.setup : 4.77s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 170.79s
Le gain est peu significatif, mais il peut le devenir si l'inventaire cible est conséquent !
De plus, dans le cas ou les gather_facts
sont lancés à plusieurs reprises, on
peut mettre en œuvre leur mise en cache. Cela se passe au niveau du fichier de
config ansible.cfg
.
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
Dans notre exemple cela n'apportera rien, car les facts
ne sont collectées
qu'une seule fois (cloud = nouvelles ressources) !
Mais dans le cas où vous lancez plusieurs playbooks sur un nombre conséquent de machines ce paramétrage trouve tout son sens !
Regrouper si possible les taches
On voit que les taches les plus longues sont celles demandant l'installation des
packages. En regardant le rôle de gueerlinguy, on peut voir que dans
l'installation des packages sur une debian family
, il utilise plusieurs taches
apt
qui pourraient être regroupées en une seule :
Dans le fichier roles/geerlingguy.mysql/tasks/setup-Debian.yml
on peut lire :
- name: Update apt cache if MySQL is not yet installed.
ansible.builtin.apt: update_cache=yes
changed_when: False
when: not mysql_installed.stat.exists
- name: Ensure MySQL Python libraries are installed.
ansible.builtin.apt:
name: "{{ mysql_python_package_debian }}"
state: present
- name: Ensure MySQL packages are installed.
ansible.builtin.apt:
name: "{{ mysql_packages }}"
state: present
policy_rc_d: 101
register: deb_mysql_install_packages
Deviennent :
- name: Ensure MySQL packages are installed.
ansible.builtin.apt:
update_cache: true
name: "{{ mysql_packages + mysql_python_package_debian }}"
state: present
policy_rc_d: 101
register: deb_mysql_install_packages
when: not mysql_installed.stat.exists
Pour que cela fonctionne, j'ai aussi modifié la variable
mysql_python_package_debian
en une liste dans le fichier
roles/geerlingguy.mysql/defaults/main.yml
:
Playbook run took 0 days, 0 hours, 2 minutes, 46 seconds
lundi 27 mars 2023 14:05:35 +0200 (0:00:05.144) 0:02:46.575 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 62.22s
geerlingguy.apache : Update apt cache. : 24.69s
geerlingguy.apache : Ensure Apache is installed on Debian. : 22.79s
Gathering Facts : 5.71s
geerlingguy.mysql : restart mysql : 5.14s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.66s
geerlingguy.apache : Add apache vhosts configuration. : 3.02s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.62s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.52s
geerlingguy.apache : Enable Apache mods. : 2.52s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.44s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 2.03s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.99s
geerlingguy.apache : restart apache : 1.94s
geerlingguy.mysql : Remove MySQL test database. : 1.45s
geerlingguy.apache : Get installed version of Apache. : 1.42s
geerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.40s
geerlingguy.apache : Configure Apache. : 1.36s
geerlingguy.mysql : Get MySQL version. : 1.29s
geerlingguy.mysql : Get list of hosts for the root user. : 1.27s
lundi 27 mars 2023 14:05:35 +0200 (0:00:05.144) 0:02:46.575 ************
===============================================================================
geerlingguy.mysql : 98.03s
geerlingguy.apache : 62.82s
gather_facts : 5.71s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 166.55s
Même constat que précédemment, le gain peut devenir significatif dès lors où
vous faites attention à regrouper des taches identiques. Et là se pose la
question de l'utilisation et du codage des rôles. Il existe plusieurs stratégies
pour améliorer les temps d'exécution en utilisant des rôles. La première que
j'utilise est de ne faire que de la configuration dans les rôles, l'installation
des packages étant regroupée dans le playbook en pre_tasks
ou de construire
des goldens images par middlewares.
Pour le plaisir toutes les optimisations ensemble
Attention, je ne le fais que pour jouer, mais avant de généraliser ce genre de pratique, il faut bien étudier le fonctionnement de vos playbooks pour voir s'ils sont compliants avec ce genre de pratique.
Le fichier ansible.cfg
:
[defaults]
roles_path = ./roles
inventory=aws_ec2.yml
remote_user=ubuntu
host_key_checking = False
callbacks_enabled = timer, profile_tasks, profile_roles
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
forks = 20
[ssh_connection]
# -C permit compression
ssh_args=-o ControlMaster=auto -o ControlPersist=600s
pipelining = True
Playbook run took 0 days, 0 hours, 1 minutes, 35 seconds
lundi 27 mars 2023 14:16:38 +0200 (0:00:03.075) 0:01:35.738 ************
===============================================================================
geerlingguy.mysql : Ensure MySQL packages are installed. : 35.49s
geerlingguy.apache : Update apt cache. : 15.13s
geerlingguy.apache : Ensure Apache is installed on Debian. : 13.41s
Get minimal facts : 7.56s
geerlingguy.mysql : restart mysql : 3.08s
geerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 1.57s
geerlingguy.apache : Add apache vhosts configuration. : 1.31s
geerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.06s
geerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.00s
geerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 0.93s
geerlingguy.apache : restart apache : 0.91s
geerlingguy.mysql : Copy .my.cnf file with root password credentials. : 0.89s
geerlingguy.apache : Get installed version of Apache. : 0.84s
geerlingguy.mysql : Ensure MySQL is stopped after initial install. : 0.83s
geerlingguy.apache : Enable Apache mods. : 0.77s
geerlingguy.mysql : Disallow root login remotely : 0.57s
geerlingguy.mysql : Remove MySQL test database. : 0.50s
geerlingguy.apache : Configure Apache. : 0.50s
geerlingguy.mysql : Create datadir if it does not exist : 0.42s
geerlingguy.mysql : Check if MySQL is already installed. : 0.42s
lundi 27 mars 2023 14:16:38 +0200 (0:00:03.075) 0:01:35.737 ************
===============================================================================
geerlingguy.mysql : 52.70s
geerlingguy.apache : 35.45s
ansible.builtin.setup : 7.56s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total : 95.72s
Gros gain :)
Autres pistes
Utiliser les taches asynchrones et les blocks
Parmi les autres gains possibles, c'est d'utiliser les taches
asynchrones et les
blocks. Le principe des taches asynchrones reprend
celui de la strategy free
sauf que l'on choisit quelle(s) tache(s) peu(ven)t
tourner en arrière-plan et définir une tâche de resynchronisation.
Utiliser la meilleure des stratégies
Ansible par défaut utilise la stratégie linear
pour ordonnancer les taches. En
fait, il attend que chaque tâche soit totalement terminée pour passer à la
suivante. Il existe une autre stratégie, appelée free
qui elle enchaine les
taches sur les machines sans attendre la fin de l'exécution des taches. Cela
fonctionne très bien si vous n'avez pas de dépendances entre les différentes
machines, par contre dans le cas contraire cela peut être désastreux. Donc à
utiliser avec précaution. A ne surtout pas utiliser avec run_once
!!!!!
[defaults]
inventory = .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
callbacks_enabled = timer, profile_tasks, profile_roles
roles_path = ./roles
Pourquoi pas de changement dans le fichier ansible.cfg ? Car je préfère le voir directement dans le playbook !
- name: Installation de mysql avec le role de geerlinguy
hosts: all
gather_facts: true
become: true
strategy: free
roles:
- geerlingguy.apache
- geerlingguy.mysql
Je ne mets pas de résultat pour ce paramètre, parce qu'il n'est pas assez généralisable.
Les erreurs communes allongeant le temps d'exécution des playbooks
Le recours aux modules shell
et command
au lieu des modules existant
Très souvent quand je reprends du code existant, même écrit par moi, je trouve
des pistes d'améliorations. La plus courante étant le recours aux modules
shell
et command
au lieu des modules. Souvent par méconnaissance et parfois
parce qu'au moment de l'écriture du code ansible ce module n'existait pas. Une
autre piste est la méconnaissance des nouvelles options. Combien de fois, on
utilise plusieurs tâches pour contrer l'absence du paramètre d'un module et moi
le premier. Le plus gros des risques en utilisant les modules shell
et command
est de rendre votre playbook non idempotant.
Utiliser des boucles sur les modules gérant les listes
Une autre des plus grosses erreurs est de mettre des boucles : loop
,
with_items
, ... alors que le module prend en charge une liste de paramètres.
Le plus courant le module package ou apt ou yum .... :
- name: Installation de plusieurs packages
ansible.builtin.apt:
name: "{{item}}"
state: present
with_items:
- htop
- net-tools
qui devrait être écrit :
- name: Installation de plusieurs packages
ansible.builtin.apt:
name:
- htop
- net-tools
state: present
Ne pas utiliser les blocks
.
Regrouper des taches sur une seule condition avec les blocks !!!!
Ne pas utiliser le module synchronize
Pour copier plusieurs fichiers ou répertoires, on peut utiliser le module
synchronize
qui est un wrapper de la commande rsync:
- name: Synchronization using rsync protocol (pull)
ansible.posix.synchronize:
mode: pull
src: rsync://somehost.com/path/
dest: /some/absolute/path/
Plus loin
Si vous avez d'autres tips, n'hésitez pas en m'en faire part.