03 Guide pas-à-pas - Atelier 3

PHP, Apache et MariaDB
dans un seul container

Pas de Compose, pas de supervisord, pas d'outil externe : un Dockerfile et un script de démarrage maison qui orchestre les deux processus. Sept étapes guidées pour le pattern "tout-en-un".

Ce qu'on vous fournit

Le dossier labos/semaine-1/demande-3-web-data/ contient :

Vous y ajoutez : Dockerfile, demarrer-services.sh (le ENTRYPOINT), .dockerignore, construire.sh, demarrer.sh, arreter.sh, votre README.md.

Avant de commencer

Pré-requis

Vue d'ensemble

Le parcours en sept étapes

La grande nouveauté : deux processus tournent dans le même container. MariaDB en arrière-plan, Apache en avant-plan. Un script de démarrage maison fait l'orchestration. C'est moins idiomatique que Docker Compose, mais parfaitement valide et simple.

Étape 01

Inspecter app et schéma

Étape 02

Comprendre le défi multi-processus

Étape 03

Écrire le Dockerfile

Étape 04

Écrire demarrer-services.sh

Étape 05

Construire et lancer

Étape 06

Démontrer la persistance

Étape 07

Encapsuler et finaliser

01

Inspecter l'application et le schéma

Comprendre ce que la stagiaire info a livré : une app PHP qui interroge MariaDB, et un schéma SQL avec 12 espèces de démonstration.

Indications

Ouvrez les fichiers fournis :

  • application/connexion-bd.php - la fonction ouvrirConnexionBd() retourne un objet PDO. Notez les paramètres : hôte = localhost, base = catalogue_especes, utilisateur = root, mot de passe vide. Pourquoi vide ? Parce que MariaDB tournera dans le même container, sans exposition externe.
  • application/index.php - une requête SQL préparée avec filtre par écosystème et recherche par nom. Si la connexion échoue (BD pas prête), un encart d'erreur s'affiche.
  • application/fiche.php - charge une espèce par son identifiant entier.
  • schema.sql - CREATE DATABASE, CREATE TABLE espece, puis 12 INSERT. C'est ce qui sera joué au tout premier démarrage.
Vous pouvez identifier le nom de la BD (catalogue_especes), le nom de la table (espece), les 3 valeurs possibles de l'écosystème (marin, forestier, pollinisateurs).
02

Comprendre le défi : deux processus dans un container

Saisir pourquoi un container "normal" ne fait tourner qu'un seul processus, et pourquoi notre cas justifie une exception.

Pour mieux comprendre

Un container Docker démarre en exécutant un seul programme : ce que dit l'instruction CMD ou ENTRYPOINT du Dockerfile. Quand ce programme se termine, le container s'arrête. C'est la philosophie "un processus par container".

Mais cette règle est une convention, pas une contrainte technique. On peut tout à fait :

  • Faire du ENTRYPOINT un script bash qui lance plusieurs processus.
  • Démarrer les processus secondaires en arrière-plan (avec & ou via un script de démarrage de service).
  • Faire que le processus PRINCIPAL (celui qui occupe le PID 1 du container) reste au premier plan : si lui s'arrête, le container s'arrête.

Pour notre cas :

  • MariaDB doit démarrer en arrière-plan.
  • Apache reste au premier plan : c'est lui le PID 1.

Important : un container avec deux processus ne sera pas Compose-compatible directement. Compose préfère un service par container. Mais pour ce TP, on apprend à faire les deux dans un, ce qui est très utile pour comprendre les bases. Compose viendra plus tard dans le cursus.

Vous pouvez expliquer ce que fait exec apache2-foreground à la fin d'un script ENTRYPOINT, et pourquoi MariaDB est lancé en premier.
03

Écrire le Dockerfile

Construire une image qui contient PHP, Apache, MariaDB, l'extension PDO MySQL, le code de l'application, le schéma SQL, et le script de démarrage.

Indications

  • Image de base : php:8.2-apache, comme à l'Atelier 2.
  • Installer MariaDB via apt : apt-get update && apt-get install -y --no-install-recommends mariadb-server mariadb-client. --no-install-recommends évite d'installer les paquets recommandés non essentiels.
  • Activer l'extension PDO MySQL : docker-php-ext-install pdo pdo_mysql. Cette commande est fournie par les images officielles PHP.
  • Définir DEBIAN_FRONTEND=noninteractive avant le RUN apt pour éviter des prompts interactifs.
  • Nettoyer le cache apt après l'installation : apt-get clean && rm -rf /var/lib/apt/lists/*. Ça réduit la taille de l'image.
  • Copier l'application dans /var/www/html/ et le schéma dans un dossier dédié comme /docker-entree/schema.sql.
  • Copier le script demarrer-services.sh dans /usr/local/bin/ et le rendre exécutable avec un RUN chmod +x.
  • Définir ENTRYPOINT ["/usr/local/bin/demarrer-services.sh"].

Squelette à compléter :

Dockerfile - squelette
FROM image-de-base

LABEL pavillon="catalogue-especes"

ENV DEBIAN_FRONTEND=noninteractive

# Installer MariaDB et l'extension PDO MySQL
RUN apt-get update \
    && apt-get install -y --no-install-recommends paquets \
    && docker-php-ext-install extensions \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Copier l'application et le schéma
COPY application/ /var/www/html/
RUN mkdir -p /docker-entree
COPY schema.sql /docker-entree/schema.sql

# Script de démarrage
COPY demarrer-services.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/demarrer-services.sh

# Préparer le dossier MariaDB
RUN mkdir -p /var/lib/mysql && chown -R mysql:mysql /var/lib/mysql

EXPOSE 80

ENTRYPOINT ["/usr/local/bin/demarrer-services.sh"]
N'oubliez pas chown sur /var/lib/mysql. MariaDB refuse de démarrer si l'utilisateur mysql n'a pas les droits sur son dossier de données. Symptôme : le container démarre puis se ferme, le log montre une erreur de permission.
Le Dockerfile contient les 8 sections clés : FROM, LABEL, ENV, RUN d'installation, COPY application+schema, COPY script + chmod, RUN mkdir+chown mysql, EXPOSE et ENTRYPOINT.
04

Écrire le script demarrer-services.sh

C'est le coeur de l'atelier. Ce script s'exécute au démarrage de chaque container et orchestre les deux processus.

Comportement attendu

  1. Démarrer MariaDB en arrière-plan.
  2. Boucler en attendant que MariaDB réponde aux requêtes (au démarrage, MariaDB peut mettre 5 à 10 secondes à devenir prête).
  3. Si c'est le premier démarrage, jouer le schéma SQL pour créer la base et insérer les espèces. Ne jamais le rejouer aux démarrages suivants.
  4. Lancer Apache en avant-plan, qui devient le PID 1 du container.

Indications techniques

  • Démarrer MariaDB : la commande standard sur Debian/Ubuntu est service mariadb start (qui lance mysqld_safe en arrière-plan).
  • Vérifier qu'elle est prête : mysqladmin ping -h localhost --silent. Code de retour 0 si la BD répond, autre chose sinon.
  • Boucler tant qu'elle ne répond pas : until commande; do sleep 1; done.
  • Détecter le premier démarrage : créez un fichier témoin (par exemple /var/lib/mysql/initialise.flag) APRÈS avoir joué le schéma. La condition if [ ! -f "..." ] détecte son absence.
  • Jouer le schéma : mysql < /docker-entree/schema.sql (en tant que root local sans mot de passe).
  • Lancer Apache en avant-plan : exec apache2-foreground. Le exec remplace le processus du script par Apache, qui devient PID 1.

Squelette du script :

demarrer-services.sh - squelette
#!/bin/bash
set -e

cheminFichierTemoin="/var/lib/mysql/initialise.flag"
cheminSchema="/docker-entree/schema.sql"

# 1. Démarrer MariaDB en arrière-plan
service mariadb start

# 2. Attendre que MariaDB réponde
until commande-de-ping; do
    sleep 1
done

# 3. Premier démarrage : jouer le schéma
if [ ! -f "$cheminFichierTemoin" ]; then
    commande-mysql-pour-jouer-le-schema
    touch "$cheminFichierTemoin"
fi

# 4. Apache en avant-plan = PID 1 du container
exec commande-apache
Sans exec devant apache2-foreground, le script bash reste en PID 1 et Apache devient son enfant. Quand vous arrêtez le container avec docker stop, Docker envoie SIGTERM au PID 1 (le bash) qui ne le transmet pas forcément à Apache. Avec exec, Apache devient PID 1 et reçoit les signaux directement. Comportement plus propre.
Ne mettez PAS de mot de passe pour root MariaDB dans cette demande (juste pour ce TP). MariaDB sur Debian utilise par défaut l'authentification socket : root local n'a pas besoin de mot de passe. En production, on utiliserait des variables d'environnement.
Le script existe, est exécutable, fait les 4 étapes (start, wait, init si premier, exec apache).
05

Construire l'image et lancer un premier container

Construire l'image (le build prend 1 à 2 minutes la première fois à cause de l'apt-get) et démarrer un container, en montant un dossier hôte sur /var/lib/mysql pour la persistance.

Build

Comme aux ateliers précédents : docker build -t zoo-catalogue-especes:1.0 -t zoo-catalogue-especes:latest .

Run avec volume MariaDB

Cette fois, le volume est sur /var/lib/mysql (le dossier où MariaDB stocke ses données).

Terminal
$ docker run -d --name "zoo-catalogue-en-marche" \
    -p "8080:80" \
    -v "chemin-hote:cheminContainer" \
    nom-image

Au premier démarrage, suivez les logs en direct pour voir l'orchestration : docker logs -f zoo-catalogue-en-marche. Vous devriez voir :

  1. Le démarrage de MariaDB.
  2. Plusieurs lignes "Attente que MariaDB soit prête..."
  3. "MariaDB répond." puis "Initialisation du schéma..."
  4. "Schéma initialisé." puis "Lancement d'Apache."
  5. Les logs Apache au format normal.

Vérifier dans le navigateur

Ouvrez http://localhost:8080/. La page d'accueil affiche les 12 espèces réparties sur les 3 écosystèmes. Filtrez. Cherchez. Cliquez sur une espèce pour ouvrir sa fiche.

Le catalogue s'affiche, les 3 filtres écosystèmes fonctionnent (Marin, Forestier, Pollinisateurs), la barre de recherche filtre par nom commun ou nom latin, les fiches s'ouvrent.
Si la page d'accueil affiche un encart d'erreur "La base de données ne répond pas", c'est sans doute que vous l'avez ouverte trop vite : MariaDB n'a pas fini de s'initialiser au tout premier démarrage. Patientez 10 secondes et rechargez. Si l'erreur persiste, regardez docker logs nom-container pour identifier la cause.
06

Démontrer la persistance des données

Prouver que vos données survivent à docker stop, docker rm, et même à un nouveau docker run.

Démarche

  1. Container démarré, 12 espèces visibles. Notez ce nombre.
  2. Ajoutez une espèce manuellement via le client MariaDB du container :
Terminal - ajout d'une espèce de test
$ docker exec -it zoo-catalogue-en-marche \
    mysql catalogue_especes -e "INSERT INTO espece
        (nom_commun, nom_latin, ecosysteme, statut_residence, description)
    VALUES
        ('Espèce test', 'Testus testus', 'marin', 'Test', 'Ajoutée pour démontrer la persistance.');"
  1. Rechargez la page d'accueil : 13 espèces.
  2. ./arreter.sh (ou docker stop + rm).
  3. ./demarrer.sh de nouveau.
  4. Rechargez : toujours 13 espèces, l'espèce de test est encore là.

Si à l'étape 6 il n'y a que 12 espèces, c'est que votre volume n'est pas correctement configuré. Le dossier /var/lib/mysql du container doit être lié à un dossier sur l'hôte qui survit au container.

Les 13 espèces sont toujours présentes après un cycle complet arrêter/démarrer. Le dossier monté sur l'hôte (par exemple donnees-bd/) contient des fichiers MariaDB (notamment initialise.flag que vous avez créé).
07

Encapsuler dans des scripts et finaliser

Trois scripts d'usage et la documentation, comme aux Ateliers 1 et 2.

Trois scripts

  • construire.sh - docker build avec deux tags. Mention dans les logs que le build prend 1 à 2 minutes (apt-get).
  • demarrer.sh - création du dossier donnees-bd/ s'il n'existe pas, suppression de l'ancien container s'il existe, docker run avec mappage de port et volume.
  • arreter.sh - docker stop + docker rm. Message qui rappelle que le dossier donnees-bd/ reste sur l'hôte.

.dockerignore

Au minimum :

.dockerignore
.git
*.log
construire.sh
demarrer.sh
arreter.sh
README.md
.dockerignore
donnees-bd

Le dossier donnees-bd/ doit absolument être ignoré : il contient les fichiers MariaDB une fois le container démarré, et ils peuvent peser plusieurs Mo. Pas question de les envoyer au démon Docker à chaque rebuild.

README.md

Sections essentielles :

  • Ce que c'est - architecture (PHP + Apache + MariaDB en un), 12 espèces, 3 écosystèmes.
  • Comment lancer - les 3 scripts.
  • Démontrer la persistance - la procédure de l'étape 6.
  • Comment ajouter une espèce - via docker exec + mysql, ou en modifiant le schéma.
  • Limites du pattern - mentionnez les inconvénients du tout-en-un (logs mêlés, mise à jour difficile, anti-pattern Docker), pour montrer que vous comprenez ce que Compose résoudra plus tard.
Le séquence complète ./construire.sh ; ./demarrer.sh démarre le catalogue. La persistance est documentée et démontrable. Le README explique les limites du pattern.

Trois défis optionnels

Défi A - Variables d'environnement pour le mot de passe

Modifiez votre Dockerfile et votre script de démarrage pour que le mot de passe root MariaDB soit fourni via une variable d'environnement (ENV ou -e au runtime). C'est une étape vers la sécurité de production.

Défi B - Healthcheck applicatif

Ajoutez une instruction HEALTHCHECK qui vérifie périodiquement que MariaDB répond ET qu'Apache sert la page d'accueil. Comment Docker se comporte-t-il quand le healthcheck échoue ?

Défi C - Migration du schéma

Au lieu d'un seul schema.sql joué une fois, organisez plusieurs fichiers schema-001-tables.sql, schema-002-donnees.sql, etc. Faites jouer chaque fichier qui n'a pas encore été appliqué (concept de "migration" en SQL). Avantage en production : faire évoluer le schéma sans tout recréer.