C’est quoi une stack LEMP ?
LEMP est un acronyme pour
- Linux
- Nginx (se prononce Engine X)
- MariaDB ou MySQL
- PHP
Une stack
est un ensemble de logiciels ’empilés’ les uns sur les
autres. Ici, le logiciel le plus proche de la machine, le système
d’exploitation GNU/Linux est le premier élément de cette
pile. Vient ensuite Nginx qui est un serveur web. Nginx se
base sur le réseau et le système de fichiers gérés par Linux pour
répondre aux requêtes HTTP du client (le visiteur de votre
site). Quand il rencontre un script PHP, Nginx passe la main à
l’interpréteur PHP via le protocole FastCGI. PHP lui même fait
appel à MariaDB pour le stockage de données structurées. Cet
ensemble de logiciels se empilés forme la stack LEMP.
Dans ce tutoriel, nous allons utiliser Ansible pour installer et configurer cette stack automatiquement sur Debian 11.
Installation de Nginx
Comme l’installation de la stack LEMP est succinte, nous allons utiliser un playbook Ansible qui contiendra tout le code. Dans un article suivant, nous verrons découper ce playbook en rôles quand sa complexité devient trop grande.
---
- name: Installation et configuration d'une stack LEMP
hosts: serveur_debian_test
become: yes
tasks:
- name: Installation du serveur web Nginx
apt:
name: nginx
update_cache: yes
La première tâche que nous venons d’ajouter au playbook installe Nginx
avec apt
. Nous n’avons pas encore configuré PHP sur notre serveur,
car il dépend de la configuration PHP-FastCGI que nous allons
réaliser. Voyons la configuration du serveur par défaut
/etc/nginx/sites-enabled/default
installée avec le paquet Nginx:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
}
La racine de ce site par défaut est située /var/www/html
. Nous
allons utiliser cette configuration par la suite. La configuration par
défaut comporte aussi une prise en charge des urls finissant par
.php
. Nous allons décommenter ces lignes une fois que le serveur PHP
FastCGI sera installé et disponible pour que Nginx sous-traite
l’exécution des scripts PHP par le serveur PHP-FastCGI.
Installation de PHP et du serveur FastCGI
---
- name: Installation et configuration d'une stack LEMP
hosts: conteneur_test
become: yes
tasks:
- name: Installation du serveur web Nginx
apt:
name: nginx
update_cache: yes
- name: Installation de l'interpréteur PHP et de ses modules
apt:
name:
- php # interpréteur PHP
- php-fpm # serveur FastCGI pour PHP
- php-mysql # Paquet pour utiliser MySQL ou MariaDB avec PHP
De la même façon, une configuration par défaut du serveur FastCGI a
été installée avec le paquet php-fpm
dans le fichier
/etc/php/7.4/fpm/pool.d/www.conf
. Le serveur par défaut est
configuré pour lancer PHP en tant qu’utilisateur www-data
et
écoutera les requêtes sur le socket unix situé
/run/php/php7.4-fpm.sock
d’après les lignes suivantes de la
configuration:
listen = /run/php/php7.4-fpm.sock
user = www-data
group = www-data
On va maintenant lancer le serveur PHP FastCGI:
- name: Lancement du serveur PHP FastCGI
service:
name: php7.4-fpm
state: started
Configuration de Nginx pour utiliser PHP
Le serveur FastCGI qui va nous permettre de lancer notre script PHP est maintenant prêt. On peut décommenter la configuration de PHP-FastCGI dans le serveur Nginx par défaut, et activer l’utilisation de PHP-FastCGI pour le traitement des scripts PHP. On obtient la configuration suivante:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.php index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}
}
On stocke cette configuration dans le fichier default
dans le même
répertoire que le playbook Ansible.
On ajoute le démarrage du serveur Nginx ainsi que la copie de la configuration du serveur par défaut au playbook:
- name: Configuration du serveur Nginx
copy:
src: default
dest: /etc/nginx/sites-available/default
- name: Lancement du serveur Nginx
service:
name: nginx
state: started
Installation de MariaDB
Il ne reste plus qu’à installer la base de données MariaDB pour compléter notre stack.
Nous allons également créer un superutilisateur dont le nom et le mot
de passe seront stockés dans les variables mariadb.root_user
et
mariadb.root_password
pour pouvoir accéder à la base de données via
PHP.
---
- name: Installation et configuration d'une stack LEMP
hosts: conteneur_test
become: yes
vars:
mariadb:
root_user: tutoriel
root_password: tutoriel
database: tutoriel_lemp
En plus de l’installation de la base, on va installer le SDK Python de
MySQL/MariaDB. Cette librairie permet de communiquer avec ces bases de
données en utilisant Python. Dans notre cas, on va avec cette
librairie et le rôle community.mysql.mysql_user
créer un utilisateur
MariaDB. Ensuite, on créera la base de données utilisée lors de ce
tutoriel.
La collection de rôles Ansible community.mysql
n’est pas installée
par défaut. Nous allons l’ajouter à l’aide d’Ansible galaxy:
ansible-galaxy collection install community.mysql
Voici le code à ajouter au playbook:
- name: Installation de la base de données MariaDB
apt:
name: mariadb-server
- name: Lancement de la base de données
service:
name: mariadb
state: started
enabled: yes
- name: Installation du SDK de la base de données
pip:
name: PyMySQL
- name: Création d'un utilisateur avec tous les privilèges
community.mysql.mysql_user:
name: "{{ mariadb.root_user }}"
password: "{{ mariadb.root_password }}"
plugin: mysql_native_password
priv: '*.*:ALL,GRANT'
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Sauvegarde de l'identifiant de cet utilisateur
template:
src: my.cnf.j2
dest: /root/.my.cnf
owner: root
group: root
mode: '0600'
- name: Création d'une base de données
community.mysql.mysql_db:
name: "{{ mariadb.database }}"
state: present
Script PHP de démo
On va ajouter un script PHP qui va se connecter à la base et compter le nombre de visites de ce script pour tester la stack. Voici le script que nous allons utiliser:
<?php
// Connection à la base de données
$pdo = new PDO(
'mysql:host=localhost;dbname={{ mariadb.database }}',
'{{ mariadb.root_user }}',
'{{ mariadb.root_password }}'
);
// Incrémentation du compteur de visites
$pdo->query("CREATE TABLE IF NOT EXISTS `visites`(id int PRIMARY KEY AUTO_INCREMENT, at DATETIME DEFAULT CURRENT_TIMESTAMP())");
$pdo->query("INSERT INTO `visites` VALUES ()");
// Récupération du nombre total de visites
$statement = $pdo->query("SELECT COUNT(*) as total_visites FROM `visites`");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo 'Nombre de visites total de /test.php: '. $row['total_visites'];
?>
Ce script PHP contient des variables Jinja2 comme {{ mariadb.database }}
qui vont permettre de paramétriser l’utilisateur/mot de passe
ainsi que la base de données utilisée par PHP. Pour refléter cela,
nous allons mettre deux extensions au fichier PHP contenant des
variables Jinja: test.php.j2
. Le module template
permet à Ansible
de transformer la template de script PHP en script PHP puis de le
copier vers le serveur distant (ici dans le fichier
/var/www/html/test.php
).
On place ce script à la racine de notre serveur de test, et on ajoute le programme cURL qui va nous permettre de faire des requêtes HTTP au serveur
- name: Ajout du script PHP de test à la racine
template:
src: test.php.j2
dest: /var/www/html/test.php
owner: www-data
group: www-data
mode: '0644'
- name: Installation de cURL
apt:
name: curl
Il est maintenant possible d’utiliser cURL à l’intérieur du conteneur de test afin de récupérer la page /test.php en HTTP, voici le résultat:
docker exec conteneur_test curl -s http://localhost/test.php
# Résultat (ici, 17 visites au total):
# Nombre de visites total de /test.php: 17
Conclusion
Vous pouvez retrouver les sources du projet
complet sur ce site. Lancez la construction
du conteneur de test avec
./conteneur-test.sh
. Puis exécutez le playbook avec
ansible-playbook -i hosts playbook.yml
. Ansible devra bien sûr être
préalablement installé sur votre
machine.
La configuration de la stack LEMP de cette article devra être améliorée pour correspondre à vos besoins. J’espère que cette première approche vous a permis d’avoir un aperçu de la conception d’une stack LEMP.