Guida

Docker per PrestaShop: Configurazione ambiente di sviluppo

Docker per lo sviluppo PrestaShop: test multi-versione, workflow moduli, gestione database, debug con Xdebug e considerazioni per la produzione.

Perché eseguiamo ogni PrestaShop su Docker

La nostra intera infrastruttura di sviluppo e staging su mypresta.rocks è basata su Docker: un singolo host TrueNAS, container dedicati come ps176-dev, ps178-dev, ps8-dev e ps9-dev, un ps-redis condiviso, Nginx Proxy Manager davanti a tutto. Ogni errore descritto in questa pagina lo abbiamo commesso almeno una volta.

Il problema: PS 1.7 richiede PHP 7.1, PS 8.x richiede 8.1, PS 9.x richiede 8.3 o superiore. MySQL 5.7 rispetto a 8.0 cambia il plugin di autenticazione. Installare tutto questo in modo nativo è il modo migliore per passare un sabato a fare risoluzione dei problemi invece di scrivere moduli. Ogni container porta con sé esattamente il runtime che il suo PrestaShop si aspetta.

  • Test multi-versione. PS 1.6, 1.7, 8.x e 9.x contemporaneamente. Un audit di un modulo diventa il lavoro di un pomeriggio, non di una settimana passata a ricostruire stack LAMP.
  • Isolamento. Ogni negozio ha il proprio PHP, MySQL, filesystem. Un'installazione andata male non può contaminarne un'altra.
  • Riproducibilità. Tutto è in docker-compose.yml. Portatile, staging, macchina di un collega: la stessa configurazione.
  • Pulizia semplice. docker compose down -v e tutto sparisce.
Eseguiamo container PrestaShop separati dalla 1.6 fino alla 9.1 su un'unica macchina TrueNAS. Gli schemi descritti qui sotto sono ciò che abbiamo conservato dopo un decennio di perfezionamenti.

Prerequisiti

Installazione di Docker

Ubuntu/Debian: curl -fsSL https://get.docker.com | sh, poi sudo usermod -aG docker $USER. macOS: Docker Desktop: nelle impostazioni conviene impostare l'implementazione della condivisione file su VirtioFS. Windows: prima WSL2, poi Docker Desktop con il backend WSL2. La modalità Hyper-V è più lenta; noi la eviteremmo.

I quattro concetti che contano

  • Immagine: modello di sola lettura. Quella ufficiale è prestashop/prestashop su Docker Hub.
  • Container: un'istanza in esecuzione. Più container da una sola immagine: è così che funzionano le configurazioni multi-versione.
  • Volume: archiviazione persistente. Senza volume, niente dati dopo la rimozione del container. Abbiamo perso dati di test dimenticando questo punto.
  • Rete: rete virtuale che permette ai container di raggiungersi a vicenda (PrestaShop ↔ MySQL).

Hardware

Conviene prevedere 1,5-2GB di RAM per ogni istanza PrestaShop. 16GB sono comodi su un portatile da sviluppatore per 2-3 versioni. La nostra macchina di sviluppo ha 64GB e NVMe perché eseguiamo diversi container versionati contemporaneamente. I dischi meccanici ti faranno odiare Docker: non ci provare nemmeno.

Configurazione a due container

Inserisci questo in un file 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 deve corrispondere esattamente al nome del servizio (db): è il motivo più frequente per cui un nuovo compose fallisce. PS_FOLDER_INSTALL=disabled impedisce che l'installatore venga rieseguito a ogni riavvio. Esegui docker compose up -d, poi osserva l'avvio con docker compose logs -f prestashop; concedi 1-2 minuti alla prima esecuzione.

Il negozio è all'indirizzo http://localhost:8080, il Back Office è su http://localhost:8080/admin-dev.

Utilizza sempre volumi con nome per il database. Senza un volume, rimuovere il container distrugge ogni prodotto, ordine e cliente. Abbiamo perso mezza giornata di dati di test dimenticando questo su un container "usa e getta" creato in fretta.

Test multi-versione

Questo è lo scenario per cui Docker è stato pensato. Tre versioni di PrestaShop affiancate, ciascuna sulla propria porta e sul proprio database:

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
      # ... stesso schema visto sopra
    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

Scegli uno schema di porte: il nostro è 8178 per PS 1.7.8, 8082 per la 8.2, 8090 per la 9.0. Superati i dieci container, i numeri di porta smettono di essere memorizzabili; a quel punto abbiamo messo Nginx Proxy Manager davanti e abbiamo assegnato a ogni negozio un hostname come ps8-dev.mypresta.rocks. Conviene farlo fin dal primo giorno se prevedi di andare oltre una manciata di container.

Separa sempre i database. Lo schema diverge tra le versioni maggiori; le migrazioni si corromperebbero a vicenda.

Attenzione: docker network connect / disconnect su un container in esecuzione riscrive la mappatura delle porte. Lo abbiamo visto disattivare silenziosamente la porta 443 sul nostro container NPM, mettendo offline tutti i siti in un colpo solo. Definisci la rete nel compose e ricrea il container se hai bisogno di modificarla.

Flusso di lavoro per lo sviluppo dei moduli

Esegui il bind-mount della directory del tuo modulo: ogni modifica è subito attiva. È il singolo vantaggio più grande che Docker offre per il lavoro sui moduli:

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

Per il test multi-versione, monta la stessa directory dell'host in ogni container. Modifica un file, aggiorna tre schede. Montiamo ogni ~/modules/mpr* in tutti i container ps178-dev, ps8-dev e ps9-dev proprio per questo.

Permessi: la trappola ricorrente

I file dell'host appartengono a te (UID 1000); Apache gira come www-data (UID 33). PrestaShop tenta di scrivere su var/cache, img, modules e si vede negare l'accesso: è per questo che il caricamento dei moduli fallisce sulle configurazioni appena create.

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

# Esegue una singola query
docker exec <your-shop>-db mysql -u root -p'root' -e "SELECT COUNT(*) FROM ps_product;" prestashop

Conviene tenere a portata di mano uno script fix-container-perms.sh. Il nostro lo eseguiamo più spesso di quanto vorremmo, soprattutto dopo aver fatto rsync dei moduli.

Cache

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

Oppure disabilita la cache dei modelli in Parametri Avanzati → Prestazioni durante il lavoro attivo. Riabilitala prima di considerare concluso il lavoro: i bug legati alla cache si manifestano solo con la cache attiva. Se la produzione usa Redis, aggiungi lo stesso servizio anche in locale; Redis Dashboard & Key Manager è utile quando vuoi che il cablaggio della cache lato PrestaShop corrisponda alla produzione invece di simularlo con la cache su filesystem.

Gestione del database

Mantieni i database di test abbastanza piccoli da poterli spostare facilmente. Prima di esportare un negozio live in Docker, ripulisci carrelli abbandonati, log e dati temporanei con Database Cleanup o un'equivalente operazione di pulizia SQL, poi esegui il 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

Il servizio phpMyAdmin qui sopra si collega a ogni database presente sulla rete; i comandi qui sotto esportano e importano un database:

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

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

Importazione ed esportazione

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

Due insidie tipiche della produzione: passa i dump compressi attraverso gunzip e indica sempre SET NAMES utf8mb4 all'inizio di qualsiasi ripristino eseguito via docker exec; altrimenti i nomi dei prodotti in UTF-8 finiscono con doppia codifica e correggerlo è più sgradevole che prevenirlo.

Persistente o effimero

Persistente (volumi con nome): qualsiasi cosa ti dispiacerebbe perdere. Effimero: i test di installazione. Teniamo un container PS 9.0 effimero per la domanda "questo modulo si installa senza problemi su un negozio vuoto?": ricreato da zero a ogni esecuzione.

Test delle email con Mailpit

Mailpit intercetta le email in uscita inviate tramite il suo servizio e le mostra in un'interfaccia web, utilizzando la configurazione del servizio riportata sopra. Il Dockerfile qui sotto aggiunge Xdebug a PrestaShop:

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 → Parametri Avanzati → E-mail: SMTP mailpit, porta 1025, nessuna crittografia, nessuna autenticazione. La posta intercettata è su http://localhost:8025. Un unico Mailpit condiviso serve ogni negozio di sviluppo sulla nostra rete.

Debug con Xdebug

L'immagine ufficiale non include Xdebug. Costruisci il wrapper utilizzando il Dockerfile qui sopra, poi configura xdebug.ini e VS Code come mostrato qui sotto:

[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}"
    }
  }]
}

Su Linux, host.docker.internal non esiste in modo predefinito: aggiungi extra_hosts: ["host.docker.internal:host-gateway"]. Abbiamo perso mezz'ora su questo dettaglio.

VS Code

Installa PHP Debug e aggiungi la configurazione JSON mostrata sopra al file .vscode/launch.json. I comandi qui sotto correggono i permessi del container:

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 è ciò che fa scattare il breakpoint. Senza, VS Code non fa nulla in silenzio.

PHPStorm

Impostazioni → PHP → Server: localhost + porta del container, mappa /var/www/html sul progetto. Porta Xdebug 9003 in PHP → Debug, "Avvia ascolto".

Comandi utili

AttivitàComando
Avviare tutti i containerdocker compose up -d
Arrestare tutti i containerdocker compose down
Arrestare ed eliminare tutti i datidocker compose down -v
Visualizzare i logdocker compose logs -f
Svuotare la cache di PSdocker exec CONTAINER rm -rf /var/www/html/var/cache/*
CLI di MySQLdocker exec -it CONTAINER-db mysql -u root -p'PASS' prestashop
Esportare il databasedocker exec CONTAINER-db mysqldump -u root -p'PASS' prestashop > backup.sql
Importare il databasedocker exec -i CONTAINER-db mysql -u root -p'PASS' prestashop < backup.sql
Aprire una shell nel containerdocker exec -it CONTAINER bash
Controllare l'uso delle risorsedocker stats --no-stream
Ricostruire dopo modifiche al Dockerfiledocker compose up -d --build
Eseguire la console di PrestaShopdocker exec CONTAINER php bin/console cache:clear --env=prod
Eseguire Composer in un modulodocker exec -w /var/www/html/modules/my_module CONTAINER composer install

Produzione: la nostra opinione sincera

Per la maggior parte dei commercianti, Docker è uno strumento di sviluppo. La produzione di solito gira meglio su hosting tradizionale: un singolo stack LAMP è più facile da monitorare e da passare ad altri, l'I/O nativo dei file batte i volumi Docker (PrestaShop carica migliaia di file PHP a ogni richiesta) e la maggior parte degli host adatti a PrestaShop offre cPanel, non un demone Docker.

Docker in produzione vale lo sforzo con CI/CD, picchi di capacità per eventi di vendita, dieci o più negozi, oppure deployment a zero tempo di inattività.

Compose, Kubernetes o Swarm. Compose gestisce 20-30 container su un singolo host: è quello che eseguiamo noi, ed è più che sufficiente. Kubernetes aggiunge auto-scaling e orchestrazione multi-nodo al costo di un vero team infrastrutturale; per PrestaShop non abbiamo ancora visto un caso in cui fosse la scelta giusta. Docker Swarm è una scomoda via di mezzo che eviteremmo: la community è andata oltre. E kompose (lo strumento "converti compose in k8s") sembra ottimo finché non lo provi e scopri che l'output richiede tante modifiche manuali quante ne richiederebbe scriverlo da zero.

Due impostazioni dell'host che vale la pena configurare

  • live-restore: true in /etc/docker/daemon.json. I riavvii del demone smettono di terminare i container in esecuzione.
  • Un data-root personalizzato su un pool SSD veloce, non sul disco del sistema operativo. Le immagini e i volumi MySQL crescono; i dischi di sistema si riempiono silenziosamente.

Cosa può andare storto

"Impossibile connettersi al database"

  • DB_SERVER non corrisponde al nome del servizio MySQL (identico, trattini compresi).
  • MySQL non ha finito di avviarsi. Usa un healthcheck e depends_on: condition: service_healthy.
  • I container sono su reti diverse: mettili entrambi su un bridge con nome.
  • MySQL 8.0 usa per impostazione predefinita caching_sha2_password; il mysqli di PrestaShop richiede mysql_native_password. Aggiungi command: --default-authentication-plugin=mysql_native_password.

I/O lento su macOS

I bind mount su macOS un tempo erano lentissimi (10-30s per caricamento di pagina). Tre soluzioni: VirtioFS nelle impostazioni di Docker Desktop (il singolo miglioramento più grande), montare di meno (solo la directory del modulo, il resto su un volume con nome), Mutagen (sincronizzazione bidirezionale dei file che aggira lo strato di mount: è una soluzione drastica, ma funziona).

SSL in sviluppo

I moduli di pagamento e il social login rifiutano il semplice HTTP. Nginx Proxy Manager termina l'SSL per ogni container: è quello che usiamo noi, l'opzione più pulita. mkcert per certificati attendibili in locale. Traefik per il rilevamento e il provisioning automatici.

MySQL che consuma tutta la RAM

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

Su Docker Desktop, alza il limite di memoria in Impostazioni → Risorse ad almeno 6GB quando hai diversi container attivi. Arresta quelli che non stai usando: docker stop ps178-dev.

Cicli di reinstallazione

Se PrestaShop si reinstalla a ogni riavvio, il volume /var/www/html non sta persistendo oppure PS_FOLDER_INSTALL=disabled non viene letto. Verifica che docker volume ls mostri un volume reale (non anonimo).

Contenuto misto dopo un cambio di porta

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 avvertimento: non eseguire mai quella query su un negozio cliente live senza un backup. L'abbiamo vista mandare in tilt il frontend di una produzione nel bel mezzo del pomeriggio.

Letture correlate

Domande correlate

Caricamento...
Torna su