Poradnik

Docker dla PrestaShop: Konfiguracja środowiska deweloperskiego

Konfiguracja Dockera do pracy z PrestaShop: testy wielu wersji, praca z modułami, zarządzanie bazą danych, debugowanie Xdebug i wdrożenia produkcyjne.

Dlaczego każdy sklep PrestaShop uruchamiany jest na Dockerze

Cała infrastruktura deweloperska i stagingowa w mypresta.rocks opiera się na Dockerze, jeden host TrueNAS, dedykowane kontenery takie jak ps176-dev, ps178-dev, ps8-dev i ps9-dev, współdzielony ps-redis, a z przodu Nginx Proxy Manager. Każdy błąd opisany na tej stronie popełniliśmy przynajmniej raz.

Problem: PS 1.7 wymaga PHP 7.1, PS 8.x wymaga 8.1, PS 9.x wymaga 8.3+. MySQL 5.7 kontra 8.0 zmienia wtyczkę uwierzytelniania. Natywna instalacja tego wszystkiego to sposób na spędzenie soboty na rozwiązywaniu problemów zamiast na pisaniu modułów. Każdy kontener dostarcza dokładnie to środowisko uruchomieniowe, którego oczekuje dana wersja PrestaShop.

  • Testowanie wielu wersji. PS 1.6, 1.7, 8.x i 9.x jednocześnie. Audyt modułu zajmuje popołudnie, a nie tydzień przebudowywania stosów LAMP.
  • Izolacja. Każdy sklep ma własne PHP, MySQL i system plików. Nieudana instalacja nie przeniknie do innego.
  • Powtarzalność. Wszystko zapisane w docker-compose.yml. Laptop, staging, maszyna współpracownika, ta sama konfiguracja.
  • Łatwe sprzątanie. docker compose down -v i znika.
Na jednej maszynie TrueNAS uruchamiamy oddzielne kontenery PrestaShop od 1.6 aż po 9.1. Opisane poniżej wzorce to wynik dekady doskonalenia.

Wymagania wstępne

Instalacja Dockera

Ubuntu/Debian: curl -fsSL https://get.docker.com | sh, a następnie sudo usermod -aG docker $USER. macOS: Docker Desktop: w ustawieniach należy przełączyć implementację udostępniania plików na VirtioFS. Windows: najpierw WSL2, a potem Docker Desktop z backendem WSL2. Tryb Hyper-V jest wolniejszy; lepiej go unikać.

Cztery pojęcia, które mają znaczenie

  • Obraz: szablon tylko do odczytu. Oficjalny to prestashop/prestashop w Docker Hub.
  • Kontener: uruchomiona instancja. Z jednego obrazu można uruchomić kilka. Tak właśnie działają konfiguracje wielowersyjne.
  • Wolumin: trwałe miejsce zapisu. Bez woluminu nie ma danych po usunięciu kontenera. Zdarzyło nam się stracić dane testowe, zapominając o tym.
  • Sieć: sieć wirtualna, dzięki której kontenery mogą się komunikować (PrestaShop ↔ MySQL).

Sprzęt

Na każdą instancję PrestaShop warto przewidzieć 1,5–2 GB RAM. 16 GB wystarcza komfortowo na laptopie dewelopera dla 2–3 wersji. Nasza maszyna deweloperska ma 64 GB i dysk NVMe, ponieważ uruchamiamy jednocześnie kilka kontenerów w różnych wersjach. Dyski talerzowe sprawią, że Docker stanie się udręką, nie warto próbować.

Konfiguracja dwóch kontenerów

Wystarczy umieścić poniższy fragment w pliku 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 musi dokładnie odpowiadać nazwie usługi (db), to najczęstsza pojedyncza przyczyna niepowodzenia świeżego compose. PS_FOLDER_INSTALL=disabled zapobiega ponownemu uruchamianiu instalatora przy każdym restarcie. Po wpisaniu docker compose up -d należy obserwować logi poleceniem docker compose logs -f prestashop; za pierwszym razem warto dać temu 1–2 minuty.

Sklep pod adresem http://localhost:8080, Panel administracyjny pod http://localhost:8080/admin-dev.

Dla bazy danych zawsze należy używać nazwanych woluminów. Bez takiego woluminu usunięcie kontenera niszczy każdy produkt, zamówienie i klienta. Zdarzyło nam się stracić pół dnia danych testowych, zapominając o tym przy „szybkim”, jednorazowym kontenerze.

Testowanie wielu wersji

To przypadek, do którego Docker został stworzony. Trzy wersje PrestaShop obok siebie, każda na własnym porcie i własnej bazie danych:

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
      # ... ten sam wzorzec co powyżej
    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

Należy wybrać schemat portów, nasz to 8178 dla PS 1.7.8, 8082 dla 8.2, 8090 dla 9.0. Powyżej dziesięciu kontenerów numery portów przestają być możliwe do zapamiętania; wtedy właśnie ustawiamy z przodu Nginx Proxy Manager i nadajemy każdemu sklepowi nazwę hosta w rodzaju ps8-dev.mypresta.rocks. Warto zrobić to od pierwszego dnia, jeśli planuje się więcej niż kilka kontenerów.

Zawsze należy oddzielać bazy danych. Schemat różni się między wersjami głównymi; migracje wzajemnie uszkodzą sobie dane.

Ostrzeżenie: docker network connect / disconnect na działającym kontenerze przepisuje mapowanie portów. Widzieliśmy, jak po cichu wyłączyło to port 443 na naszym kontenerze NPM, kładąc jednocześnie wszystkie witryny. Sieć należy definiować w compose i ponownie utworzyć kontener, jeśli zachodzi potrzeba jej zmiany.

Proces pracy nad modułem

Warto zamontować katalog modułu (bind-mount). Każda edycja od razu jest aktywna. To największa pojedyncza korzyść, jaką Docker daje przy pracy nad modułami:

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

Przy testowaniu wielu wersji ten sam katalog hosta należy zamontować do każdego kontenera. Edycja jednego pliku, odświeżenie trzech kart. Montujemy każdy katalog ~/modules/mpr* do wszystkich ps178-dev, ps8-dev i ps9-dev właśnie w tym celu.

Uprawnienia: powracająca pułapka

Pliki hosta należą do bieżącego użytkownika (UID 1000); Apache działa jako www-data (UID 33). PrestaShop próbuje zapisać do var/cache, img, modules i otrzymuje odmowę, to dlatego wgrywanie modułów zawodzi na świeżych konfiguracjach.

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

# Wykonanie pojedynczego zapytania
docker exec <your-shop>-db mysql -u root -p'root' -e "SELECT COUNT(*) FROM ps_product;" prestashop

Warto trzymać pod ręką skrypt fix-container-perms.sh. My uruchamiamy nasz częściej, niżbyśmy chcieli, zwłaszcza po wgraniu modułów przez rsync.

Pamięć podręczna

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

Można też wyłączyć pamięć podręczną szablonów w Parametry zaawansowane → Wydajność na czas aktywnej pracy. Należy ją włączyć ponownie przed uznaniem pracy za zakończoną, błędy związane z pamięcią podręczną ujawniają się dopiero przy jej włączeniu. Jeśli produkcja korzysta z Redis, ten sam serwis warto dodać lokalnie; Redis Dashboard & Key Manager przydaje się, gdy zależy nam na tym, by konfiguracja pamięci podręcznej po stronie PrestaShop odpowiadała produkcji, a nie udawać tego pamięcią podręczną na systemie plików.

Zarządzanie bazą danych

Bazy testowe warto utrzymywać na tyle małe, by dało się je łatwo przenosić. Przed wyeksportowaniem działającego sklepu do Dockera należy przyciąć nieaktualne koszyki, logi i dane tymczasowe za pomocą Database Cleanup lub równoważnego przejścia czyszczącego w SQL, a dopiero potem wykonać zrzut.

  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

Powyższa usługa phpMyAdmin łączy się z każdą bazą danych w sieci; poniższe polecenia służą do eksportu i importu bazy danych:

# Eksport
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 i eksport

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

Dwie produkcyjne pułapki: skompresowane zrzuty należy przepuścić przez gunzip oraz zawsze przekazywać SET NAMES utf8mb4 na początku każdego przywracania przez docker exec, w przeciwnym razie nazwy produktów w UTF-8 trafią zakodowane podwójnie, a naprawa jest brzydsza niż zapobieganie.

Trwałe czy tymczasowe

Trwałe (nazwane woluminy): wszystko, czego utrata byłaby przykra. Tymczasowe: testy instalatora. Utrzymujemy tymczasowy kontener PS 9.0 do pytania „czy ten moduł instaluje się czysto na pustym sklepie?”, świeży przy każdym uruchomieniu.

Testowanie poczty e-mail za pomocą Mailpit

Mailpit przechwytuje wychodzące wiadomości e-mail wysyłane za jego pośrednictwem i wyświetla je w interfejsie webowym, korzystając z powyższej konfiguracji usługi. Poniższy Dockerfile dodaje Xdebug do 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

Panel administracyjny → Parametry zaawansowane → E-mail: SMTP mailpit, port 1025, bez szyfrowania, bez uwierzytelniania. Przechwycona poczta pod http://localhost:8025. Jeden współdzielony Mailpit obsługuje każdy sklep deweloperski w naszej sieci.

Debugowanie za pomocą Xdebug

Oficjalny obraz nie zawiera Xdebug. Należy zbudować nakładkę przy użyciu powyższego pliku Dockerfile, a następnie skonfigurować xdebug.ini i VS Code zgodnie z poniższymi przykładami:

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

W systemie Linux host.docker.internal domyślnie nie istnieje, należy dodać extra_hosts: ["host.docker.internal:host-gateway"]. Straciliśmy na tym pół godziny.

VS Code

Należy zainstalować PHP Debug i dodać pokazaną wyżej konfigurację JSON do .vscode/launch.json. Poniższe polecenia poprawiają uprawnienia w kontenerze:

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

To właśnie pathMappings sprawia, że punkt przerwania zadziała. Bez niego VS Code po cichu nic nie robi.

PHPStorm

Ustawienia → PHP → Serwery: localhost + port kontenera, mapowanie /var/www/html na projekt. Port Xdebug 9003 w PHP → Debug, „Rozpocznij nasłuchiwanie”.

Przydatne polecenia

ZadaniePolecenie
Uruchomienie wszystkich kontenerówdocker compose up -d
Zatrzymanie wszystkich kontenerówdocker compose down
Zatrzymanie i usunięcie wszystkich danychdocker compose down -v
Podgląd logówdocker compose logs -f
Czyszczenie pamięci podręcznej PSdocker exec CONTAINER rm -rf /var/www/html/var/cache/*
Wiersz poleceń MySQLdocker exec -it CONTAINER-db mysql -u root -p'PASS' prestashop
Eksport bazy danychdocker exec CONTAINER-db mysqldump -u root -p'PASS' prestashop > backup.sql
Import bazy danychdocker exec -i CONTAINER-db mysql -u root -p'PASS' prestashop < backup.sql
Powłoka w kontenerzedocker exec -it CONTAINER bash
Sprawdzenie zużycia zasobówdocker stats --no-stream
Przebudowa po zmianach w Dockerfiledocker compose up -d --build
Uruchomienie konsoli PrestaShopdocker exec CONTAINER php bin/console cache:clear --env=prod
Uruchomienie Composera w moduledocker exec -w /var/www/html/modules/my_module CONTAINER composer install

Produkcja: nasza szczera opinia

Dla większości sprzedawców Docker jest narzędziem deweloperskim. Produkcja zwykle działa lepiej na tradycyjnym hostingu, pojedynczy stos LAMP łatwiej monitorować i przekazać, natywne wejście/wyjście plików bije woluminy Dockera (PrestaShop wczytuje tysiące plików PHP na każde żądanie), a większość hostingów przyjaznych PrestaShop daje cPanel, a nie demona Dockera.

Docker w produkcji zarabia na siebie przy CI/CD, skokach przepustowości na czas wydarzeń sprzedażowych, dziesięciu i więcej sklepach lub wdrożeniach bez przestojów.

Compose kontra Kubernetes kontra Swarm. Compose obsługuje 20–30 kontenerów na jednym hoście. Tyle u nas działa i to w zupełności wystarcza. Kubernetes dokłada automatyczne skalowanie i orkiestrację wielowęzłową kosztem prawdziwego zespołu infrastrukturalnego; w przypadku PrestaShop nie spotkaliśmy jeszcze sytuacji, w której byłby właściwym wyborem. Docker Swarm to niewygodny środek, który pominęlibyśmy, społeczność poszła dalej. A kompose (narzędzie do „konwersji compose na k8s”) brzmi świetnie, dopóki się go nie wypróbuje i nie okaże się, że wynik wymaga tyle samo ręcznej obróbki co napisanie wszystkiego od nowa.

Dwa ustawienia hosta warte ustawienia

  • live-restore: true w /etc/docker/daemon.json. Restarty demona przestają zabijać działające kontenery.
  • Niestandardowy data-root na szybkiej puli SSD, a nie na dysku systemowym. Obrazy i woluminy MySQL rosną; dyski systemowe zapełniają się po cichu.

Co bywa nie tak

„Nie można połączyć się z bazą danych”

  • DB_SERVER nie odpowiada nazwie usługi MySQL (identyczna, łącznie z myślnikami).
  • MySQL nie zakończył uruchamiania. Należy użyć healthcheck i depends_on: condition: service_healthy.
  • Kontenery w różnych sieciach: oba należy umieścić w nazwanym mostku (bridge).
  • MySQL 8.0 domyślnie używa caching_sha2_password; mysqli w PrestaShop wymaga mysql_native_password. Należy dodać command: --default-authentication-plugin=mysql_native_password.

Wolne wejście/wyjście na macOS

Montowania bind na macOS bywały ślimacze (10–30 s na wczytanie strony). Trzy rozwiązania: VirtioFS w ustawieniach Docker Desktop (największa pojedyncza korzyść), mniej montowania (tylko katalog modułu, reszta na nazwanym woluminie), Mutagen (dwukierunkowa synchronizacja plików, która omija warstwę montowania. Rozwiązanie siłowe, ale działa).

SSL w środowisku deweloperskim

Moduły płatności i logowanie społecznościowe odmawiają działania na zwykłym HTTP. Nginx Proxy Manager obsługuje terminację SSL dla każdego kontenera, tego używamy, najczystsza opcja. mkcert do lokalnie zaufanych certyfikatów. Traefik do automatycznego wykrywania i provisioningu.

MySQL pożerający cały RAM

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

W Docker Desktop limit pamięci w Ustawienia → Zasoby warto podnieść do co najmniej 6 GB, gdy ma się już kilka kontenerów. Te, których się nie używa, należy zatrzymywać: docker stop ps178-dev.

Pętle ponownej instalacji

Jeśli PrestaShop instaluje się ponownie przy każdym restarcie, wolumin /var/www/html nie zachowuje danych albo PS_FOLDER_INSTALL=disabled nie jest odczytywane. Należy sprawdzić, czy docker volume ls pokazuje prawdziwy (a nie anonimowy) wolumin.

Mieszana zawartość po zmianie portu

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

Jedno ostrzeżenie: nigdy nie należy uruchamiać tego zapytania na działającym sklepie klienta bez kopii zapasowej. Widzieliśmy, jak zablokowało frontend produkcyjny w środku popołudnia.

Powiązane lektury

Powiązane pytania

Ładowanie...
Do góry