Guides Tutoriel

Docker pour PrestaShop : environnement de développement

Docker pour le développement PrestaShop : tests multi-versions, workflow modules, gestion de base de données, Xdebug et production.

Pourquoi nous faisons tourner chaque PrestaShop sous Docker

Chez mypresta.rocks, toute notre infrastructure de développement et de préproduction repose sur Docker, un seul hôte TrueNAS, des conteneurs dédiés comme ps176-dev, ps178-dev, ps8-dev et ps9-dev, un ps-redis partagé, et Nginx Proxy Manager en frontal. Nous avons commis chacune des erreurs décrites sur cette page au moins une fois.

Le problème : PS 1.7 réclame PHP 7.1, PS 8.x veut 8.1, PS 9.x exige 8.3 ou plus. MySQL 5.7 contre 8.0 change le plugin d'authentification. Installer tout cela en natif, c'est la garantie de passer un samedi entier à résoudre des incidents au lieu d'écrire des modules. Chaque conteneur embarque exactement l'environnement d'exécution que son PrestaShop attend.

  • Tests multi-versions. PS 1.7, 8.x et 9.x en même temps. Un audit de module devient l'affaire d'un après-midi, pas d'une semaine à reconstruire des stacks LAMP.
  • Isolation. Chaque boutique a son propre PHP, sa propre base MySQL, son propre système de fichiers. Une installation ratée ne peut pas contaminer une autre.
  • Reproductibilité. Tout est dans docker-compose.yml. Ordinateur portable, préproduction, machine d'un collègue, la même configuration partout.
  • Nettoyage facile. docker compose down -v et tout disparaît.
Nous faisons tourner des conteneurs PrestaShop distincts, de la version 1.6 à la 9.1, sur une seule machine TrueNAS. Les schémas présentés ci-dessous sont ce que nous avons conservé après une décennie d'affinage.

Prérequis

Installer Docker

Ubuntu/Debian : curl -fsSL https://get.docker.com | sh, puis sudo usermod -aG docker $USER. macOS : Docker Desktop : basculez l'implémentation de partage de fichiers sur VirtioFS dans les réglages. Windows : WSL2 d'abord, puis Docker Desktop avec le backend WSL2. Le mode Hyper-V est plus lent ; nous l'éviterions.

Les quatre notions qui comptent

  • Image : modèle en lecture seule. L'image officielle est prestashop/prestashop sur Docker Hub.
  • Conteneur : une instance en cours d'exécution. Plusieurs à partir d'une même image, c'est ainsi que fonctionnent les configurations multi-versions.
  • Volume : stockage persistant. Pas de volume, pas de données une fois le conteneur supprimé. Nous avons perdu des données de test en l'oubliant.
  • Réseau : réseau virtuel permettant aux conteneurs de communiquer entre eux (PrestaShop ↔ MySQL).

Matériel

Prévoyez 1,5 à 2 Go de RAM par instance PrestaShop. 16 Go suffisent confortablement sur un ordinateur portable de développeur pour 2 à 3 versions. Notre machine de développement dispose de 64 Go et d'un disque NVMe, car nous faisons tourner plusieurs conteneurs versionnés en parallèle. Les disques mécaniques vous feront détester Docker, n'essayez même pas.

Configuration à conteneur unique

Déposez ceci dans un fichier docker-compose.yml :

version: '3.8'
services:
  prestashop:
    image: prestashop/prestashop:8.2
    container_name: <your-shop>
    ports:
      - "8080:80"
    environment:
      - DB_SERVER=db
      - DB_USER=prestashop
      - DB_PASSWD=prestashop_password
      - DB_NAME=prestashop
      - PS_DOMAIN=localhost:8080
      - PS_FOLDER_ADMIN=admin-dev
      - PS_FOLDER_INSTALL=disabled
      - ADMIN_MAIL=admin@yourshop.com
      - ADMIN_PASSWD=admin_password_123
    volumes:
      - shop-data:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:8.0
    container_name: <your-shop>-db
    environment:
      - MYSQL_ROOT_PASSWORD=root_password
      - MYSQL_DATABASE=prestashop
      - MYSQL_USER=prestashop
      - MYSQL_PASSWORD=prestashop_password
    volumes:
      - shop-db-data:/var/lib/mysql

volumes:
  shop-data:
  shop-db-data:

DB_SERVER doit correspondre exactement au nom du service (db), c'est la cause la plus fréquente d'échec d'un compose tout neuf. PS_FOLDER_INSTALL=disabled empêche l'installateur de se relancer à chaque redémarrage. docker compose up -d, puis surveillez avec docker compose logs -f prestashop ; comptez 1 à 2 minutes au premier lancement.

La boutique est sur http://localhost:8080, le Back Office sur http://localhost:8080/admin-dev.

Utilisez toujours des volumes nommés pour la base de données. Sans cela, la suppression du conteneur détruit chaque produit, chaque commande et chaque client. Nous avons perdu une demi-journée de données de test en l'oubliant sur un conteneur jetable monté « vite fait ».

Tests multi-versions

C'est le cas d'usage pour lequel Docker a été conçu. Trois versions de PrestaShop côte à côte, chacune sur son propre port et sa propre base de données :

version: '3.8'
services:
  ps176-dev:
    image: prestashop/prestashop:1.7.6
    container_name: ps176-dev
    ports: ["8076:80"]
    environment:
      - DB_SERVER=<your-db-176>
      - PS_DOMAIN=localhost:8076
      # ... même schéma que ci-dessus
    volumes:
      - ps176-data:/var/www/html
    networks: [ps-network]

  <your-db-176>:
    image: mysql:5.7
    volumes: [your-db-176-data:/var/lib/mysql]
    networks: [ps-network]

  ps8-dev:
    image: prestashop/prestashop:8.2
    container_name: ps8-dev
    ports: ["8082:80"]
    environment:
      - DB_SERVER=<your-db-8>
      - PS_DOMAIN=localhost:8082
    volumes:
      - ps8-data:/var/www/html
    networks: [ps-network]

  <your-db-8>:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes: [your-db-8-data:/var/lib/mysql]
    networks: [ps-network]

  ps9-dev:
    image: prestashop/prestashop:9.0
    container_name: ps9-dev
    ports: ["8083:80"]
    environment:
      - DB_SERVER=<your-db-9>
      - PS_DOMAIN=localhost:8083
    volumes:
      - ps9-data:/var/www/html
    networks: [ps-network]

  <your-db-9>:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes: [your-db-9-data:/var/lib/mysql]
    networks: [ps-network]

networks:
  ps-network:
    driver: bridge

Choisissez un plan de ports. Le nôtre est 8178 pour PS 1.7.8, 8082 pour 8.2, 8090 pour 9.0. Passé une dizaine de conteneurs, les numéros de port cessent d'être mémorisables ; c'est à ce moment-là que nous avons placé Nginx Proxy Manager en frontal et attribué à chaque boutique un nom d'hôte comme ps8-dev.mypresta.rocks. Faites-le dès le premier jour si vous comptez dépasser une poignée de conteneurs.

Séparez toujours les bases de données. Le schéma diverge entre versions majeures ; les migrations se corrompraient mutuellement.

Avertissement : docker network connect / disconnect sur un conteneur en cours d'exécution réécrit le mappage de ports. Nous avons vu cette commande tuer silencieusement le port 443 de notre conteneur NPM, faisant tomber tous les sites d'un coup. Définissez le réseau dans le compose et recréez le conteneur s'il faut le modifier.

Flux de développement de modules

Montez votre répertoire de module en bind-mount. Chaque modification est immédiatement active. C'est le plus grand atout que Docker apporte au travail sur les modules :

volumes:
  - ps8-data:/var/www/html
  - /home/user/modules/my_module:/var/www/html/modules/my_module

Pour les tests multi-versions, montez le même répertoire hôte dans chaque conteneur. Modifiez un seul fichier, actualisez trois onglets. Nous montons chaque ~/modules/mpr* dans tous les conteneurs ps178-dev, ps8-dev et ps9-dev précisément pour cela.

Permissions : le piège récurrent

Les fichiers de l'hôte vous appartiennent (UID 1000) ; Apache s'exécute sous www-data (UID 33). PrestaShop tente d'écrire dans var/cache, img et modules, et l'accès lui est refusé, c'est pourquoi les envois de modules échouent sur les installations toutes neuves.

# Session MySQL interactive
docker exec -it <your-shop>-db mysql -u root -p'root' prestashop

# Exécuter une seule requête
docker exec <your-shop>-db mysql -u root -p'root' -e "SELECT COUNT(*) FROM ps_product;" prestashop

Gardez un script fix-container-perms.sh sous la main. Nous lançons le nôtre plus souvent que nous le voudrions, surtout après avoir synchronisé des modules par rsync.

Cache

docker exec ps8-dev rm -rf /var/www/html/var/cache/*

Ou désactivez le cache des templates dans Paramètres avancés → Performances pendant que vous travaillez activement. Réactivez-le avant de considérer le travail terminé, les bugs liés au cache n'apparaissent qu'avec le cache activé. Si la production utilise Redis, ajoutez le même service en local ; Redis Dashboard & Key Manager est utile lorsque vous voulez que le câblage du cache côté PrestaShop corresponde à la production plutôt que de le simuler avec un cache sur le système de fichiers.

Gestion des bases de données

Gardez les bases de test suffisamment petites pour être faciles à déplacer. Avant d'exporter une boutique en production vers Docker, purgez les paniers obsolètes, les journaux et les données temporaires avec Database Cleanup ou une passe de nettoyage SQL équivalente, puis réalisez le dump.

  phpmyadmin:
    image: phpmyadmin:latest
    ports: ["9090:80"]
    environment:
      - PMA_HOSTS=<your-db-176>,<your-db-8>,<your-db-9>
      - PMA_USER=root
      - PMA_PASSWORD=root
    networks: [ps-network]

phpMyAdmin

Un seul conteneur dessert chaque base de données du réseau :

# Export
docker exec <your-shop>-db mysqldump -u root -p'root' prestashop > backup.sql

# Import
docker exec -i <your-shop>-db mysql -u root -p'root' prestashop < backup.sql

Import et export

  mailpit:
    image: axllent/mailpit
    ports:
      - "8025:8025"  # Interface web
      - "1025:1025"  # SMTP
    networks: [ps-network]

Deux pièges en production : passez par gunzip pour les dumps compressés, et passez toujours SET NAMES utf8mb4 au début de toute restauration via docker exec, sinon les noms de produits en UTF-8 arrivent doublement encodés et la correction est plus laborieuse que la prévention.

Persistant ou éphémère

Persistant (volumes nommés) : tout ce que vous seriez désolé de perdre. Éphémère : les tests d'installation. Nous gardons un conteneur PS 9.0 éphémère pour la question « ce module s'installe-t-il proprement sur une boutique vierge ? », recréé à chaque exécution.

Test des e-mails avec Mailpit

Mailpit capture chaque e-mail sortant et l'affiche dans une interface web :

FROM prestashop/prestashop:8.2
RUN pecl install xdebug && docker-php-ext-enable xdebug
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

Back Office → Paramètres avancés → E-mail : SMTP mailpit, port 1025, sans chiffrement, sans authentification. Les e-mails capturés sont sur http://localhost:8025. Un seul Mailpit partagé dessert chaque boutique de développement de notre réseau.

Débogage avec Xdebug

L'image officielle ne fournit pas Xdebug. Construisez un fin wrapper :

[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.idekey=VSCODE
{
  "version": "0.2.0",
  "configurations": [{
    "name": "Listen for Xdebug (Docker)",
    "type": "php",
    "request": "launch",
    "port": 9003,
    "pathMappings": {
      "/var/www/html/modules/my_module": "${workspaceFolder}"
    }
  }]
}

Sous Linux, host.docker.internal n'existe pas par défaut, ajoutez extra_hosts: ["host.docker.internal:host-gateway"]. Nous avons perdu une demi-heure à cause de ce détail.

VS Code

Installez PHP Debug et ajoutez à .vscode/launch.json :

docker exec <your-shop> chown -R www-data:www-data /var/www/html/var
docker exec <your-shop> chown -R www-data:www-data /var/www/html/img
docker exec <your-shop> chown -R www-data:www-data /var/www/html/modules

pathMappings est ce qui déclenche le point d'arrêt. Sans lui, VS Code ne fait silencieusement rien.

PHPStorm

Paramètres → PHP → Serveurs : localhost + port du conteneur, mappez /var/www/html vers le projet. Port Xdebug 9003 sous PHP → Debug, « Démarrer l'écoute ».

Commandes utiles

TâcheCommande
Démarrer tous les conteneursdocker compose up -d
Arrêter tous les conteneursdocker compose down
Arrêter et supprimer toutes les donnéesdocker compose down -v
Consulter les journauxdocker compose logs -f
Vider le cache PSdocker exec CONTAINER rm -rf /var/www/html/var/cache/*
CLI MySQLdocker exec -it CONTAINER-db mysql -u root -p'PASS' prestashop
Exporter la base de donnéesdocker exec CONTAINER-db mysqldump -u root -p'PASS' prestashop > backup.sql
Importer la base de donnéesdocker exec -i CONTAINER-db mysql -u root -p'PASS' prestashop < backup.sql
Ouvrir un shell dans le conteneurdocker exec -it CONTAINER bash
Vérifier l'utilisation des ressourcesdocker stats --no-stream
Reconstruire après modification du Dockerfiledocker compose up -d --build
Lancer la console PrestaShopdocker exec CONTAINER php bin/console cache:clear --env=prod
Lancer Composer dans un moduledocker exec -w /var/www/html/modules/my_module CONTAINER composer install

Production : notre avis honnête

Pour la plupart des marchands, Docker est un outil de développement. La production tourne généralement mieux sur un hébergement traditionnel. Une stack LAMP unique est plus simple à superviser et à transmettre, les entrées/sorties fichier natives surpassent les volumes Docker (PrestaShop charge des milliers de fichiers PHP par requête), et la plupart des hébergeurs adaptés à PrestaShop vous donnent cPanel, pas un démon Docker.

Docker en production justifie son intérêt avec le CI/CD, les pics de capacité lors d'événements commerciaux, dix boutiques ou plus, ou les déploiements sans interruption de service.

Compose contre Kubernetes contre Swarm. Compose gère 20 à 30 conteneurs sur un seul hôte, c'est ce que nous utilisons, et c'est largement suffisant. Kubernetes ajoute la mise à l'échelle automatique et l'orchestration multi-nœuds, au prix d'une véritable équipe d'infrastructure ; pour PrestaShop, nous n'avons pas encore vu de cas où c'était le bon choix. Docker Swarm est un entre-deux maladroit que nous écarterions. La communauté est passée à autre chose. Et kompose (l'outil de « conversion de compose vers k8s ») semble formidable jusqu'à ce que vous l'essayiez et constatiez que le résultat demande autant de retouches manuelles que de l'écrire à neuf.

Deux réglages d'hôte qui valent le coup

  • live-restore: true dans /etc/docker/daemon.json. Les redémarrages du démon cessent de tuer les conteneurs en cours d'exécution.
  • data-root personnalisé sur un pool SSD rapide, pas sur le disque système. Les images et les volumes MySQL grossissent ; les disques système se remplissent en silence.

Ce qui peut mal tourner

« Impossible de se connecter à la base de données »

  • DB_SERVER ne correspond pas au nom du service MySQL (identique, tirets compris).
  • MySQL n'a pas fini de démarrer. Utilisez un healthcheck et depends_on: condition: service_healthy.
  • Les conteneurs sont sur des réseaux différents, placez-les tous les deux sur un même bridge nommé.
  • MySQL 8.0 utilise par défaut caching_sha2_password ; le mysqli de PrestaShop a besoin de mysql_native_password. Ajoutez command: --default-authentication-plugin=mysql_native_password.

Entrées/sorties lentes sous macOS

Les bind-mounts sous macOS étaient autrefois d'une lenteur glaciale (10 à 30 s par chargement de page). Trois remèdes : VirtioFS dans les réglages de Docker Desktop (le gain le plus important), monter moins (seulement le répertoire du module, le reste sur un volume nommé), Mutagen (synchronisation de fichiers bidirectionnelle qui contourne la couche de montage, solution radicale, mais qui fonctionne).

SSL en développement

Les modules de paiement et la connexion via les réseaux sociaux refusent le HTTP en clair. Nginx Proxy Manager termine le SSL pour chaque conteneur, c'est ce que nous utilisons, l'option la plus propre. mkcert pour des certificats approuvés localement. Traefik pour la découverte et le provisionnement automatiques.

MySQL qui dévore toute votre RAM

command: >
  --innodb-buffer-pool-size=128M
  --max-connections=50

Sur Docker Desktop, augmentez la limite de mémoire dans Paramètres → Ressources à au moins 6 Go une fois que vous avez plusieurs conteneurs. Arrêtez ceux que vous n'utilisez pas : docker stop ps178-dev.

Boucles de réinstallation

Si PrestaShop se réinstalle à chaque redémarrage, c'est que le volume /var/www/html ne persiste pas, ou que PS_FOLDER_INSTALL=disabled n'est pas pris en compte. Vérifiez que docker volume ls affiche un vrai volume (et non un volume anonyme).

Contenu mixte après un changement de port

docker exec <your-shop>-db mysql -u root -p'root' -e "
  UPDATE ps_configuration SET value='localhost:8080'
    WHERE name IN ('PS_SHOP_DOMAIN','PS_SHOP_DOMAIN_SSL');
  UPDATE ps_shop_url SET domain='localhost:8080', domain_ssl='localhost:8080';
" prestashop

Un avertissement : n'exécutez jamais cette requête sur la boutique d'un client en production sans sauvegarde. Nous l'avons vue rendre inutilisable un frontend de production en plein après-midi.

À lire également

Questions liées

Chargement...
Retour en haut