Optimiser les temps d'exécution d'Ansible
Mise à jour :
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_ed25519.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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_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.15pyenv virtualenv 3.9.15 ansible-python3.9.15pyenv shell ansible-python3.9.15pip 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 secondslundi 27 mars 2023 12:40:49 +0200 (0:00:05.627) 0:02:56.819 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 55.39sgeerlingguy.apache : Update apt cache. : 23.82sgeerlingguy.apache : Ensure Apache is installed on Debian. : 21.20sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.03sGathering Facts : 5.65sgeerlingguy.mysql : restart mysql : 5.63sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.52sgeerlingguy.apache : Get installed version of Apache. : 4.28sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.78sgeerlingguy.apache : Add apache vhosts configuration. : 3.32sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 3.00sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.75sgeerlingguy.apache : Enable Apache mods. : 2.70sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.55sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.27sgeerlingguy.apache : restart apache : 2.08sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.82sgeerlingguy.mysql : Remove MySQL test database. : 1.75sgeerlingguy.mysql : Disallow root login remotely : 1.71sgeerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.54slundi 27 mars 2023 12:40:49 +0200 (0:00:05.628) 0:02:56.820 ************===============================================================================geerlingguy.mysql : 107.06sgeerlingguy.apache : 64.09sgather_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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_enabled = timer, profile_tasks, profile_roles[ssh_connection]# -C permit compressionssh_args=-o ControlMaster=auto -o ControlPersist=600s
Le résultat du run :
Playbook run took 0 days, 0 hours, 3 minutes, 2 secondslundi 27 mars 2023 12:49:38 +0200 (0:00:05.764) 0:03:02.314 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 59.27sgeerlingguy.apache : Update apt cache. : 25.66sgeerlingguy.apache : Ensure Apache is installed on Debian. : 24.58sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.23sGathering Facts : 6.17sgeerlingguy.mysql : restart mysql : 5.76sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.45sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.61sgeerlingguy.apache : Add apache vhosts configuration. : 3.12sgeerlingguy.apache : Enable Apache mods. : 2.79sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.78sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.73sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.72sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.08sgeerlingguy.apache : restart apache : 2.01sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.76sgeerlingguy.apache : Get installed version of Apache. : 1.58sgeerlingguy.apache : Configure Apache. : 1.52sgeerlingguy.mysql : Remove MySQL test database. : 1.50sgeerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.48slundi 27 mars 2023 12:49:38 +0200 (0:00:05.766) 0:03:02.316 ************===============================================================================geerlingguy.mysql : 109.77sgeerlingguy.apache : 66.35sgather_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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_enabled = timer, profile_tasks, profile_roles[ssh_connection]pipelining = True
Le resultat du run :
Playbook run took 0 days, 0 hours, 2 minutes, 42 secondslundi 27 mars 2023 13:00:49 +0200 (0:00:05.583) 0:02:42.610 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 58.68sgeerlingguy.apache : Update apt cache. : 23.88sgeerlingguy.apache : Ensure Apache is installed on Debian. : 23.13sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 8.26sGathering Facts : 5.89sgeerlingguy.mysql : restart mysql : 5.58sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 3.80sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.54sgeerlingguy.apache : Add apache vhosts configuration. : 2.36sgeerlingguy.apache : Configure Apache. : 1.95sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.92sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 1.91sgeerlingguy.apache : Get installed version of Apache. : 1.48sgeerlingguy.apache : Enable Apache mods. : 1.38sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.35sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.22sgeerlingguy.apache : restart apache : 1.18sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.15sgeerlingguy.mysql : Remove MySQL test database. : 0.80sgeerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 0.71slundi 27 mars 2023 13:00:49 +0200 (0:00:05.583) 0:02:42.610 ************===============================================================================geerlingguy.mysql : 97.75sgeerlingguy.apache : 58.95sgather_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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_enabled = timer, profile_tasks, profile_rolesforks = 20
Le résultat du run :
Playbook run took 0 days, 0 hours, 1 minutes, 44 secondslundi 27 mars 2023 13:43:02 +0200 (0:00:03.470) 0:01:44.616 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 33.39sgeerlingguy.apache : Update apt cache. : 13.84sgeerlingguy.apache : Ensure Apache is installed on Debian. : 13.32sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 4.62sgeerlingguy.mysql : restart mysql : 3.47sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 3.33sGathering Facts : 3.30sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 2.06sgeerlingguy.apache : Add apache vhosts configuration. : 1.65sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.47sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.44sgeerlingguy.apache : Enable Apache mods. : 1.43sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 1.36sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.28sgeerlingguy.apache : Get installed version of Apache. : 1.10sgeerlingguy.apache : restart apache : 1.04sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.03sgeerlingguy.mysql : Create datadir if it does not exist : 0.82sgeerlingguy.mysql : Remove MySQL test database. : 0.81sgeerlingguy.mysql : Check if MySQL is already installed. : 0.80slundi 27 mars 2023 13:43:02 +0200 (0:00:03.471) 0:01:44.616 ************===============================================================================geerlingguy.mysql : 64.48sgeerlingguy.apache : 36.81sgather_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.8pyenv virtualenv 3.10.8 ansible-python3.10.8pyenv shell ansible-python3.10.8pip install ansible boto3
On lance le run :
Playbook run took 0 days, 0 hours, 2 minutes, 57 secondslundi 27 mars 2023 13:29:30 +0200 (0:00:07.267) 0:02:57.820 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 58.41sgeerlingguy.apache : Update apt cache. : 24.32sgeerlingguy.apache : Ensure Apache is installed on Debian. : 23.35sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 8.80sgeerlingguy.mysql : restart mysql : 7.27sGathering Facts : 5.90sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.49sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.74sgeerlingguy.apache : Add apache vhosts configuration. : 2.93sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.58sgeerlingguy.apache : Enable Apache mods. : 2.51sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.48sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.44sgeerlingguy.apache : restart apache : 2.11sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.00sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.73sgeerlingguy.apache : Get installed version of Apache. : 1.67sgeerlingguy.mysql : Remove MySQL test database. : 1.47sgeerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.38sgeerlingguy.apache : Configure Apache. : 1.33slundi 27 mars 2023 13:29:30 +0200 (0:00:07.267) 0:02:57.819 ************===============================================================================geerlingguy.mysql : 108.77sgeerlingguy.apache : 63.12sgather_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 secondslundi 27 mars 2023 13:53:42 +0200 (0:00:06.061) 0:02:50.820 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 54.84sgeerlingguy.apache : Update apt cache. : 23.32sgeerlingguy.apache : Ensure Apache is installed on Debian. : 21.76sgeerlingguy.mysql : Ensure MySQL Python libraries are installed. : 9.01sgeerlingguy.mysql : restart mysql : 6.06sGet minimal facts : 4.77sgeerlingguy.mysql : Update apt cache if MySQL is not yet installed. : 4.62sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.68sgeerlingguy.apache : Add apache vhosts configuration. : 3.09sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.72sgeerlingguy.apache : Enable Apache mods. : 2.59sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.55sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.54sgeerlingguy.apache : restart apache : 2.02sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 2.00sgeerlingguy.apache : Configure Apache. : 1.72sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 1.72sgeerlingguy.mysql : Get MySQL version. : 1.52sgeerlingguy.mysql : Create datadir if it does not exist : 1.51sgeerlingguy.apache : Add vhost symlink in sites-enabled. : 1.48slundi 27 mars 2023 13:53:42 +0200 (0:00:06.061) 0:02:50.819 ************===============================================================================geerlingguy.mysql : 105.29sgeerlingguy.apache : 60.73sansible.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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_enabled = timer, profile_tasks, profile_rolesgathering = smartfact_caching = jsonfilefact_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 secondslundi 27 mars 2023 14:05:35 +0200 (0:00:05.144) 0:02:46.575 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 62.22sgeerlingguy.apache : Update apt cache. : 24.69sgeerlingguy.apache : Ensure Apache is installed on Debian. : 22.79sGathering Facts : 5.71sgeerlingguy.mysql : restart mysql : 5.14sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 3.66sgeerlingguy.apache : Add apache vhosts configuration. : 3.02sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 2.62sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 2.52sgeerlingguy.apache : Enable Apache mods. : 2.52sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 2.44sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 2.03sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 1.99sgeerlingguy.apache : restart apache : 1.94sgeerlingguy.mysql : Remove MySQL test database. : 1.45sgeerlingguy.apache : Get installed version of Apache. : 1.42sgeerlingguy.mysql : Update MySQL root password for localhost root account (5.7.x). : 1.40sgeerlingguy.apache : Configure Apache. : 1.36sgeerlingguy.mysql : Get MySQL version. : 1.29sgeerlingguy.mysql : Get list of hosts for the root user. : 1.27slundi 27 mars 2023 14:05:35 +0200 (0:00:05.144) 0:02:46.575 ************===============================================================================geerlingguy.mysql : 98.03sgeerlingguy.apache : 62.82sgather_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 = ./rolesinventory=aws_ec2.ymlremote_user=ubuntuhost_key_checking = Falsecallbacks_enabled = timer, profile_tasks, profile_rolesgathering = smartfact_caching = jsonfilefact_caching_connection = /tmp/ansible-factsforks = 20
[ssh_connection]# -C permit compressionssh_args=-o ControlMaster=auto -o ControlPersist=600spipelining = True
Playbook run took 0 days, 0 hours, 1 minutes, 35 secondslundi 27 mars 2023 14:16:38 +0200 (0:00:03.075) 0:01:35.738 ************===============================================================================geerlingguy.mysql : Ensure MySQL packages are installed. : 35.49sgeerlingguy.apache : Update apt cache. : 15.13sgeerlingguy.apache : Ensure Apache is installed on Debian. : 13.41sGet minimal facts : 7.56sgeerlingguy.mysql : restart mysql : 3.08sgeerlingguy.mysql : Ensure MySQL is started and enabled on boot. : 1.57sgeerlingguy.apache : Add apache vhosts configuration. : 1.31sgeerlingguy.mysql : Copy my.cnf global MySQL configuration. : 1.06sgeerlingguy.mysql : Delete innodb log files created by apt package after initial install. : 1.00sgeerlingguy.apache : Ensure Apache has selected state and enabled on boot. : 0.93sgeerlingguy.apache : restart apache : 0.91sgeerlingguy.mysql : Copy .my.cnf file with root password credentials. : 0.89sgeerlingguy.apache : Get installed version of Apache. : 0.84sgeerlingguy.mysql : Ensure MySQL is stopped after initial install. : 0.83sgeerlingguy.apache : Enable Apache mods. : 0.77sgeerlingguy.mysql : Disallow root login remotely : 0.57sgeerlingguy.mysql : Remove MySQL test database. : 0.50sgeerlingguy.apache : Configure Apache. : 0.50sgeerlingguy.mysql : Create datadir if it does not exist : 0.42sgeerlingguy.mysql : Check if MySQL is already installed. : 0.42slundi 27 mars 2023 14:16:38 +0200 (0:00:03.075) 0:01:35.737 ************===============================================================================geerlingguy.mysql : 52.70sgeerlingguy.apache : 35.45sansible.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_inventorycallbacks_enabled = timer, profile_tasks, profile_rolesroles_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.