Table des matières :
- Démos éphémères PrestaShop : contraintes réelles et objectifs CI/CD
- Architecture de référence : image applicative immuable + données versionnées
- Pipeline PrestaShop CI/CD : build, tests, packaging et publication d’une preview
- Provisionner la démo en minutes : DB snapshot, médias, cache et warmup
- Kubernetes (ou pas) : isolation, DNS/TLS, et routing propre par démo
- Sécurité, observabilité, et destruction automatique : éviter la démo-poubelle
Démos éphémères PrestaShop : contraintes réelles et objectifs CI/CD
Générer des démos PrestaShop « à la demande » en quelques minutes n’est pas un problème de YAML. C’est un problème de mutabilité (fichiers et base), de temps d’initialisation (install + cache + index), et de parité prod (stack PHP, extensions, limites mémoire, reverse-proxy, TLS). Une démo qui met 25 minutes à se monter, ou qui ne reflète pas les contraintes de prod (PHP-FPM, Redis, CDN, headers), est une fausse démo : elle déplace les bugs au moment où ça fait mal.
Sur PrestaShop, la mutabilité est souvent sous-estimée : certains modules écrivent des fichiers (cache, templates compilés, exports), modifient l’état de la base au premier hit, ou supposent des permissions spécifiques. Si votre preview n’isole pas correctement le runtime (cache, sessions, files) et l’état persistant (DB, médias), vous aurez des comportements « non reproductibles » d’une démo à l’autre.
En CI/CD, la cible réaliste est l’environnement éphémère (ou preview environment) déclenché par une Pull Request / Merge Request, détruit automatiquement à la fin, avec une URL stable et un jeu de données suffisamment proche. Martin Fowler — Continuous Integration rappelle la base :
« Continuous Integration is a software development practice where members of a team integrate their work frequently » (Martin Fowler, “Continuous Integration”, martinfowler.com/articles/continuousIntegration.html)
Pour PrestaShop, « intégrer fréquemment » doit aussi signifier : pouvoir montrer et reproduire.
Dans les faits, une “bonne” démo éphémère PrestaShop répond à des objectifs très concrets (et mesurables) :
- Time-to-demo : avoir une URL utilisable en ~3 à 8 minutes (au-delà, l’équipe arrête de s’en servir).
- Parité technique : même version PHP, mêmes extensions (intl, GD/Imagick, opcache…), mêmes limites mémoire, même reverse-proxy, mêmes headers.
- Parité fonctionnelle : checkout, panier, recherche/facettes, pages CMS et une partie des modules “à risque” (paiement, transport, avis…).
- Sécurité et conformité : données anonymisées (RGPD), back-office protégé, emails sortants neutralisés, secrets injectés proprement.
- Coût maîtrisé : la démo se détruit automatiquement, et les ressources externes (DB, volumes, buckets) ne s’accumulent pas.
Contexte versions (au moment d’écrire, 2026-08) : pour un setup moderne, partez sur PrestaShop 9.x et PHP 8.3 (cf. guide de migration PrestaShop 9). Pré-requis côté infra : un registre d’images (GitLab Registry, GHCR, Harbor), un orchestrateur (Kubernetes recommandé si vous voulez des démos en parallèle), et une stratégie data (dump anonymisé, seed, ou sous-ensemble). Les risques sont connus : exposition involontaire du back-office, secrets injectés en clair, dumps non anonymisés, et coûts qui explosent si rien ne garbage-collect.
Un mini-scenario typique “agence / client” illustre pourquoi la parité prod compte : une MR modifie le thème et ajoute un module de paiement. En local tout va bien (SQLite absente, opcache différent, pas de reverse-proxy), mais en démo le checkout renvoie 500 parce qu’une extension PHP manque, ou parce que des headers de sécurité (CSP) cassent un script tiers. Une preview fidèle vous fait gagner des jours d’aller-retour.
Architecture de référence : image applicative immuable + données versionnées
La manière la plus fiable de tenir la promesse « en minutes » est de séparer strictement : (1) l’artefact applicatif immuable (image Docker) et (2) l’état (DB + médias + configs runtime). L’anti-pattern classique sur PrestaShop : copier un répertoire complet (vendor + var/cache + img + override + modules) via rsync/FTP vers un dossier « demo-xyz » et bricoler app/config/parameters.php. Ça marche… jusqu’à ce que les caches, les permissions et les modules non idempotents vous fassent perdre plus de temps que vous n’en gagnez.
Un bon test mental : “si je redeploie la même MR 10 fois, est-ce que j’obtiens 10 fois le même résultat ?” Si la réponse dépend d’un cache partagé, d’un volume réutilisé, ou d’un répertoire var/ conservé, vous n’êtes pas sur un modèle CI/CD fiable.
En pratique : construisez une image contenant le code + dépendances + assets front compilés, et laissez à l’exécution seulement :
- un volume persistant ou objet (S3/Swift) pour
img/si vous avez besoin de médias réels ; - un endpoint DB dédié par démo (MariaDB) ;
- des variables d’environnement / secrets pour
PS_DOMAIN, clés, DSN, etc.
Pour clarifier ce qui doit être “baked” vs “runtime”, une règle simple fonctionne bien :
| Élément | Doit être immuable (dans l’image) | Doit être runtime (monté/injecté) |
|---|---|---|
| Code + thème + modules versionnés | ✅ | ❌ |
Dépendances Composer (vendor/) |
✅ | ❌ |
| Assets compilés (front) | ✅ | ❌ |
Cache applicatif (var/cache, cache Smarty) |
❌ | ✅ (mais local au pod) |
| Base de données | ❌ | ✅ (DB dédiée par preview) |
Médias (img/) |
selon besoin | selon besoin |
| Secrets (clé cookie, tokens, DSN) | ❌ | ✅ (secret manager) |
Si vous avez besoin d’une base « proche prod » sans risques, standardisez un pipeline d’export anonymisé (suppression PII, hashing emails, purge logs). Dans un contexte France/UE, c’est ce qui vous évite de transformer une “simple démo” en incident RGPD (adresses, téléphones, historique commandes, etc.). Les erreurs « page blanche » liées à un mismatch PHP/extension/mémoire n’ont rien de théorique : elles apparaissent exactement quand vous devez présenter. Gardez sous la main une procédure de diagnostic rapide (voir aussi : diagnostiquer l’erreur fatale PHP).
Pour industrialiser, partez d’un environnement Docker fidèle (services, versions, ini PHP) afin d’éviter l’écart entre local, CI et démo. Deux références utiles : créer un staging Docker proche de la prod et structurer un docker-compose de services. Sur PrestaShop, la parité d’environnement n’est pas un luxe : beaucoup de modules dépendent implicitement de GD/Imagick, intl, opcache, et de comportements PHP-FPM (timeouts, pm.max_children, memory_limit, etc.).
Enfin, pensez aussi à la “parité réseau” : si la prod est derrière un reverse-proxy (Nginx/Traefik/Ingress) avec TLS, compression, et redirections, votre preview doit l’être aussi. Beaucoup de bugs PrestaShop (liens absolus, cookies Secure, mixed content) n’apparaissent qu’en HTTPS.
Pipeline PrestaShop CI/CD : build, tests, packaging et publication d’une preview
Le pipeline minimal pour des démos à la demande a 4 jobs : build, tests, publish, deploy preview. L’objectif n’est pas « déployer vite » mais « déployer toujours pareil ». GitLab Review Apps documente ce pattern. GitHub Actions a le même concept via deployment + environment.
Voici un squelette GitLab CI (adaptable GitHub Actions) orienté PrestaShop 9 / PHP 8.3. Le point important : BuildKit + cache Composer pour tenir un temps de build cohérent, sinon vous perdez vos “minutes” dans les dépendances.
stages: [build, test, publish, preview]
variables:
DOCKER_BUILDKIT: "1"
IMAGE: "$CI_REGISTRY_IMAGE/prestashop"
build:image:
stage: build
script:
- docker build --pull -t "$IMAGE:$CI_COMMIT_SHA" .
artifacts:
reports:
dotenv: build.env
test:smoke:
stage: test
services:
- name: mariadb:10.11
script:
- docker run --rm "$IMAGE:$CI_COMMIT_SHA" php -v
- docker run --rm "$IMAGE:$CI_COMMIT_SHA" php -m | grep -E "(intl|gd|opcache)"
publish:image:
stage: publish
script:
- docker push "$IMAGE:$CI_COMMIT_SHA"
- docker tag "$IMAGE:$CI_COMMIT_SHA" "$IMAGE:mr-$CI_MERGE_REQUEST_IID"
- docker push "$IMAGE:mr-$CI_MERGE_REQUEST_IID"
preview:deploy:
stage: preview
when: manual
script:
- ./ci/deploy_preview.sh "mr-$CI_MERGE_REQUEST_IID" "$IMAGE:mr-$CI_MERGE_REQUEST_IID"
environment:
name: review/mr-$CI_MERGE_REQUEST_IID
url: https://mr-$CI_MERGE_REQUEST_IID.demo.example.com
on_stop: preview:destroy
preview:destroy:
stage: preview
when: manual
script:
- ./ci/destroy_preview.sh "mr-$CI_MERGE_REQUEST_IID"
environment:
name: review/mr-$CI_MERGE_REQUEST_IID
action: stop
Le when: manual n’est pas un détail : toutes les PR n’ont pas besoin d’une démo. Pour une équipe qui a 30 MR actives, déclencher systématiquement = facture Kubernetes/DB + bruit opérationnel. À l’inverse, pour un contexte agence/validation client, vous pouvez ajouter un trigger par label (demo) ou un slash command.
Deux améliorations “haut rendement” (sans complexifier à l’excès) :
- Tagging immuable + traçabilité : gardez toujours un tag par commit (
$CI_COMMIT_SHA) et, en plus, un tag “humain” par MR (mr-123). Ça facilite le rollback et l’analyse (quelle image correspond à quelle MR). - Garde-fous de pipeline : refusez le
deploy previewsi les tests smoke échouent (extensions PHP manquantes, erreurs fatales au bootstrap). Une preview cassée consomme du temps de QA et de CI pour rien.
Dernier point : testez ce que la démo est censée prouver. Une preview qui « s’affiche » mais dont le checkout 500 est inutilisable n’apporte rien. Capitalisez sur les tests existants (ex : PHPUnit dans des modules, cf. PS One Page Checkout — tests PHPUnit avec Docker) et ajoutez un smoke HTTP (200 sur / + /panier + /commande) et un login BO si la démo l’exige.
Un bon compromis, côté CI, est de viser un test “technique” (PHP extensions + connectivité DB) et un test “métier” (un panier + une page checkout). Même sans E2E complet, ça capture déjà la majorité des régressions bloquantes en démo.
Provisionner la démo en minutes : DB snapshot, médias, cache et warmup
Le goulet d’étranglement réel, ce n’est pas le déploiement, c’est la donnée. Une installation PrestaShop “from scratch” + seed + compilation assets peut tenir en 5–10 minutes, mais la plupart des démos attendent : catalogue, règles panier, CMS, facettes, modules, traductions. La stratégie qui marche : snapshot DB (dump compressé) + restauration parallèle + post-provision idempotent.
Pattern éprouvé :
- Dump nocturne (ou à chaque release) :
mysqldump --single-transaction+ compressionzstd -19. - Upload vers un stockage objet interne (ou artefact CI) + checksum.
- Restauration :
zstd -d | mysqldans une DB vide, puis exécution de scripts de post-traitement.
Pour tenir la promesse “en minutes”, vous devez aussi empêcher les ralentissements faciles :
- Importer sans UI : restauration en ligne de commande uniquement (pas d’import via back-office).
- Limiter la concurrence : 10 restores DB simultanés peuvent saturer les IO, même sur un cluster solide. Fixez une limite de démos “en provisioning” en parallèle.
- Isoler le stockage DB : un stockage réseau lent ruinera vos temps, même avec une image pullée en 10 secondes.
À titre opérationnel, suivez au moins ces 3 métriques par preview (et logguez-les) :
| Étape | Ce que vous mesurez | Objectif réaliste |
|---|---|---|
| Pull image | temps entre “start deploy” et container ready | < 30–60 s (si cache node local) |
| Restore DB | durée import dump + migrations éventuelles | 60–180 s (selon taille) |
| Warmup | cache Symfony/Smarty + 1er hit front | 10–60 s |
Les scripts de post-traitement doivent corriger tout ce qui rend une base “prod-like” dangereuse ou inutilisable en preview :
- reset des comptes BO/FO et désactivation emails (ou bascule vers un SMTP de capture) ;
- réécriture du domaine (tables de configuration,
shop_url, liens médias) ; - purge des files et caches applicatifs ;
- activation d’un mode debug uniquement si la preview est restreinte.
Exemple minimal (à adapter aux préfixes, au multiboutique et à vos modules) : exécuter un SQL idempotent après import pour neutraliser les risques.
-- À adapter : PREFIX_ = préfixe de tables, et clés de config selon votre version
UPDATE PREFIX_configuration SET value = '0'
WHERE name IN ('PS_SSL_ENABLED', 'PS_SSL_ENABLED_EVERYWHERE');
-- Neutraliser l’envoi d’emails sortants (selon votre stratégie, vous pouvez plutôt router vers un SMTP de capture)
UPDATE PREFIX_configuration SET value = '2'
WHERE name = 'PS_MAIL_METHOD';
L’idée n’est pas d’avoir “le SQL parfait” dans l’article, mais un principe : vos previews doivent être safe by default. En plus des emails, pensez aux modules “externes” : paiement, transport, avis, analytics. Beaucoup doivent être passés en sandbox, ou carrément désactivés, sinon votre démo peut appeler des APIs réelles.
Sur PrestaShop, les médias (img/) et les caches (var/cache/) sont un piège récurrent. Ne montez jamais var/cache en volume partagé : c’est du runtime, pas un artefact. Pour img/, deux options réalistes : (a) pack “minimal” (quelques centaines d’images) pour rester sous 1–2 minutes de synchronisation, (b) externalisation via CDN/objet + réécriture, si vos thèmes/modules le supportent proprement. Si votre objectif est d’auditer performance et rendu, vous pouvez aussi réutiliser les recommandations de tuning (Redis, opcache, cache Smarty) : optimiser la performance via cache/Smarty/CCC/CDN.
Enfin, sur PrestaShop 9 (Symfony), un warmup explicite peut éviter le “premier hit lent” qui donne une mauvaise impression en démo : lancer un cache:warmup (ou faire un hit HTTP interne) après déploiement, puis seulement marquer la preview comme “ready”.
Pour tenir un SLA « démo en 3 minutes » en charge, vous devez mesurer : temps de pull image, temps de restore DB, temps de warmup. Typiquement, sur une DB de 1–2 Go compressée en zstd (~200–400 Mo), une restauration MariaDB sur SSD NVMe peut rester sous 60–120 s si le pod a des IO correctes. En revanche, si vous restaurez sur un stockage réseau lent ou si vous lancez 10 restores en parallèle sans limites, vous allez simplement saturer.
Kubernetes (ou pas) : isolation, DNS/TLS, et routing propre par démo
Si vous voulez plusieurs démos en parallèle, Kubernetes simplifie l’isolation : un namespace par MR, des ressources bornées, et une destruction atomique. Kubernetes — docs sur les namespaces. Sur PrestaShop, cette isolation évite surtout les collisions : DB partagée, Redis partagé, cron partagé, et surtout “un module a modifié l’état global”.
Côté exposé web, l’approche la plus stable est : wildcard DNS *.demo.example.com pointant vers l’Ingress, puis un Ingress par preview sur mr-123.demo.example.com. Pour TLS, évitez les certificats bricolés à la main : utilisez un contrôleur standard (cert-manager — docs) avec un issuer DNS-01 (pratique si vous n’exposez pas de challenge HTTP). Pour les spécificités PrestaShop (multiboutique, redirections, base URI), les détails DNS/TLS sont exactement ce qui casse les démos “à la demande” : vous pouvez recouper avec les points d’attention Kubernetes (https://www.expertise-prestashop.fr/2026/08/10/migration-prestashop-kubernetes-tls-dns-et-configuration-multiboutique/).
Pour éviter les “démos qui marchent chez vous mais pas chez le client”, sécurisez aussi ces paramètres de routage :
- Headers proxy (
X-Forwarded-Proto,X-Forwarded-Host) cohérents, sinon génération d’URLs HTTP au lieu de HTTPS. - Cookies en HTTPS : si la prod force
Secure, la preview doit le faire également. - Redirections : PrestaShop redirige facilement si le domaine stocké en base ne correspond pas (d’où l’importance du post-provision).
Kubernetes n’est pas obligatoire si votre volume de démos est faible. Une alternative acceptable : un hôte Docker dédié aux previews, un reverse-proxy (Traefik/Nginx) et une convention de ports + vhosts. Le problème : la concurrence et le nettoyage. Dès que vous avez 5–10 previews en parallèle avec bases séparées, l’outillage “fait maison” devient une source d’incidents (ports occupés, volumes orphelins, logs introuvables). Si vous êtes CTO/DevOps, le choix se fait sur des critères opérationnels, pas idéologiques.
Enfin, n’oubliez pas le “non-fonctionnel” : limiter CPU/RAM par preview (sinon une MR mange le cluster), désactiver les cron/queues si inutiles, et prévoir des readiness probes qui reflètent la réalité (front OK + DB OK). Une preview qui reçoit du trafic interne (QA/client) sans limites peut aussi déclencher des comportements anti-bot, des locks MySQL et des timeouts : ce n’est pas “juste une démo”.
Un point souvent rentable : poser des ResourceQuota ou, a minima, des limites par pod (ex. PHP-FPM vs MariaDB) pour éviter qu’une MR “lourde” (génération thumbnails, import massif) ne fasse tomber toutes les autres previews.
Sécurité, observabilité, et destruction automatique : éviter la démo-poubelle
Une démo éphémère est un environnement exposé sur Internet. Donc, par défaut, c’est une surface d’attaque. Le minimum : authentification (basic auth au niveau Ingress, ou restriction IP/VPN), et zéro secret en clair dans le repo/CI logs. Si vous êtes sur Kubernetes, External Secrets Operator + KMS (ou équivalent) est un pattern solide pour synchroniser des secrets au runtime (External Secrets Operator — synchronisation). Pour les APIs/modules, pensez SBOM + contrôle de dépendances + WAF, et traitez la CI/CD comme un composant de sécurité (sécuriser APIs et modules).
Sur PrestaShop, l’erreur classique est d’exposer un back-office de preview sans garde-fous, avec des credentials partagés et un email SMTP réel. Résultat : envoi d’emails accidentels, ou pire, fuite de données si la base a été clonée sans purge. Le durcissement doit être systématique dans le post-provision : désactivation des emails sortants, changement des clés, et idéalement purge des tokens / cookies. Si vous utilisez l’Admin API, traitez les scopes et secrets par client (sécuriser Admin API par client).
Ajoutez aussi un garde-fou très concret : ne pas réutiliser des credentials d’une preview à l’autre. Même si l’accès est restreint, un mot de passe “demo/demo” finit inévitablement dans un ticket, une capture d’écran ou un chat. Une stratégie simple : générer un mot de passe BO unique par MR et l’afficher dans les logs protégés (ou le publier dans un commentaire MR réservé aux membres).
Côté observabilité, une preview sans logs centralisés est un trou noir. Vous avez besoin au minimum : logs applicatifs PHP, logs Nginx/Ingress, et erreurs JS si vous validez un thème. Pour centraliser, un OpenSearch managé (ou ELK) fait le job pour des environnements jetables, tant que vous taggez correctement (namespace, MR id) pour purger facilement (OpenSearch managé — logs & recherche). Ajoutez des métriques basiques (latence p95, erreurs 5xx) pour éviter de “valider” une MR sur une démo déjà en souffrance.
Un détail qui change tout en debug : ajoutez un identifiant partout (au minimum MR_ID et ENV_NAME) dans :
- le nom du namespace,
- les labels Kubernetes,
- les tags de logs,
- et, si possible, un header de réponse (
X-Preview-Env: mr-123).
La destruction automatique n’est pas optionnelle. Implementez une TTL : suppression du namespace après X jours d’inactivité, destruction à la fermeture de la MR, et nettoyage des ressources externes (DB, buckets, certificats, DNS records si vous n’êtes pas en wildcard). N’oubliez pas les “restes” qui coûtent cher : volumes persistants, snapshots, bases “orphelines”, certificats émis à l’infini.
Une check-list pragmatique pour que « générer des démos à la demande en minutes » reste vrai dans la durée :
- build d’image reproductible (cache Composer/Node, tags immuables) ;
- data snapshot versionnée + anonymisée + restore mesuré ;
- post-provision idempotent (domain, emails off, comptes reset) ;
- Ingress + wildcard DNS + TLS automatisé ;
- secrets via gestionnaire (pas via variables CI en clair) ;
- logs centralisés + purge ;
- garbage collection (TTL) vérifiée par tests.
Si un seul de ces points est traité “plus tard”, votre CI/CD va produire des démos… puis produire des incidents.




