PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Docker Compose produkcja – pełny stack Magento 2, secrets, healthchecki, deployment

by Henryk Tews / wtorek, 01 października 2024 / Opublikowano w Środowiska

DDEV świetnie sprawdza się lokalnie. Ale jak wygląda stack Magento 2 na produkcji w Docker Compose? Pokażę kompletną konfigurację: nginx, PHP-FPM, MariaDB, Redis (trzy instancje), Elasticsearch i Varnish – z separacją sieci, sekretami Docker, healthcheckami i strategią deploymentu. To nie jest tutorial dla początkujących, ale referencja dla kogoś kto chce postawić Magento na własnej infrastrukturze.

Architektura stosu

Internet
    |
Varnish :80/:443 (FPC)
    |
Nginx :8080 (web server / FastCGI proxy)
    |
PHP-FPM :9000 (aplikacja Magento)
    |          |             |              |           |
MariaDB   Redis-Cache   Redis-Session   Redis-FPC   Elasticsearch
 :3306     :6379         :6380           :6381       :9200

docker-compose.yml – główny plik

# docker-compose.yml
version: '3.8'

services:

  varnish:
    image: varnish:7.4
    volumes:
      - ./varnish/magento.vcl:/etc/varnish/default.vcl:ro
    ports:
      - "80:6081"
    environment:
      VARNISH_SIZE: 512m
    depends_on:
      nginx:
        condition: service_healthy
    networks:
      - frontend
    restart: unless-stopped

  nginx:
    image: nginx:1.25-alpine
    volumes:
      - ./nginx/magento.conf:/etc/nginx/conf.d/default.conf:ro
      - magento_code:/var/www/magento:ro
    depends_on:
      php:
        condition: service_healthy
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 5s
      retries: 3
    restart: unless-stopped

  php:
    build:
      context: ./php
      dockerfile: Dockerfile
      args:
        PHP_VERSION: "8.3"
        MAGENTO_VERSION: "2.4.7"
    volumes:
      - magento_code:/var/www/magento
      - ./php/php.ini:/usr/local/etc/php/conf.d/custom.ini:ro
    environment:
      MAGE_MODE: production
    secrets:
      - db_password
      - redis_password
      - magento_admin_password
    depends_on:
      db:
        condition: service_healthy
      redis-cache:
        condition: service_healthy
      elasticsearch:
        condition: service_healthy
    networks:
      - backend
    healthcheck:
      test: ["CMD", "php-fpm", "-t"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 512M

  db:
    image: mariadb:10.11
    volumes:
      - db_data:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/conf.d/magento.cnf:ro
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: magento
      MYSQL_USER: magento
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
      - db_root_password
    networks:
      - backend
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 4G

  redis-cache:
    image: redis:7-alpine
    command: >
      redis-server
      --maxmemory 1gb
      --maxmemory-policy allkeys-lru
      --save ""
      --appendonly no
      --requirepass_file /run/secrets/redis_password
    secrets:
      - redis_password
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    restart: unless-stopped

  redis-session:
    image: redis:7-alpine
    command: >
      redis-server
      --maxmemory 512mb
      --maxmemory-policy noeviction
      --appendonly yes
      --appendfsync everysec
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
    restart: unless-stopped

  redis-fpc:
    image: redis:7-alpine
    command: >
      redis-server
      --maxmemory 2gb
      --maxmemory-policy allkeys-lru
      --save ""
      --appendonly no
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
    restart: unless-stopped

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
      - xpack.security.enabled=false
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es_data:/usr/share/elasticsearch/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 3G
        reservations:
          memory: 1G

volumes:
  magento_code:
    driver: local
  db_data:
    driver: local
  es_data:
    driver: local

secrets:
  db_password:
    file: ./secrets/db_password.txt
  db_root_password:
    file: ./secrets/db_root_password.txt
  redis_password:
    file: ./secrets/redis_password.txt
  magento_admin_password:
    file: ./secrets/magento_admin_password.txt

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # backend nie ma dostępu do internetu bezpośrednio

Dockerfile dla PHP-FPM

# php/Dockerfile
FROM php:8.3-fpm-alpine AS base

ARG MAGENTO_VERSION

# Zależności systemowe
RUN apk add --no-cache \
    libpng-dev libjpeg-turbo-dev freetype-dev \
    libzip-dev icu-dev libxslt-dev libsodium-dev \
    oniguruma-dev git unzip patch

# Rozszerzenia PHP dla Magento
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        gd pdo_mysql zip intl xsl soap bcmath \
        sockets opcache sodium

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/magento

# PHP-FPM konfiguracja
COPY php-fpm.conf /usr/local/etc/php-fpm.d/www.conf

# Healthcheck
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD php-fpm -t

# Production stage
FROM base AS production

# Kod Magento
COPY --chown=www-data:www-data . /var/www/magento

RUN composer install --no-dev --optimize-autoloader --no-interaction

USER www-data

Nginx konfiguracja dla Magento

# nginx/magento.conf
upstream fastcgi_backend {
    server php:9000;
    keepalive 32;
}

server {
    listen 8080;
    server_name _;
    root /var/www/magento/pub;

    index index.php;
    autoindex off;
    charset UTF-8;

    # Bezpieczeństwo - ukryj wersję
    server_tokens off;

    # Logi ze strukturą JSON - łatwiejsze do parsowania przez ELK/Loki
    access_log /dev/stdout json_combined;
    error_log  /dev/stderr warn;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check)\.php$ {
        fastcgi_pass   fastcgi_backend;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;

        fastcgi_param  MAGE_MODE production;
        fastcgi_param  MAGE_RUN_CODE base;

        fastcgi_read_timeout  600;
        fastcgi_send_timeout  600;
        fastcgi_buffer_size   128k;
        fastcgi_buffers       256 16k;
    }

    location /pub/static/ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
        try_files $uri $uri/ =404;
    }

    # Zablokuj dostęp do wrażliwych plików
    location ~ (\.php$|\.phtml$) { deny all; }
    location ~ /\.ht { deny all; }
    location ~ /\.git { deny all; }

    # Health check endpoint dla load balancera
    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
}

Deployment – rolling update bez downtime

#!/bin/bash
# deploy.sh - deployment bez downtime

set -e

echo "=== Magento 2 Deployment ==="

# 1. Włącz maintenance mode PRZED budowaniem
docker compose exec php bin/magento maintenance:enable

# 2. Zbuduj nowy obraz PHP z nowym kodem
docker compose build php

# 3. Zatrzymaj stary PHP-FPM, uruchom nowy
# Nginx i Varnish zostają - serwują cached content przez chwilę
docker compose up -d --no-deps php

# 4. Poczekaj aż PHP-FPM będzie healthy
echo "Czekam na PHP-FPM..."
timeout 120 bash -c 'until docker compose ps php | grep -q "healthy"; do sleep 2; done'

# 5. Magento setup
docker compose exec -T php bin/magento setup:upgrade --keep-generated
docker compose exec -T php bin/magento setup:di:compile
docker compose exec -T php bin/magento setup:static-content:deploy pl_PL en_US -f --jobs=$(nproc)

# 6. Flush cache
docker compose exec -T php bin/magento cache:flush

# 7. Wyłącz maintenance mode
docker compose exec php bin/magento maintenance:disable

# 8. Purge Varnish FPC
docker compose exec varnish varnishadm "ban req.url ~ ."

echo "=== Deployment zakończony ==="

MySQL tuning dla Magento

# mysql/my.cnf
[mysqld]
# InnoDB - kluczowe ustawienia dla Magento
innodb_buffer_pool_size        = 2G      # ~70% dostępnej RAM
innodb_buffer_pool_instances   = 4
innodb_log_file_size           = 512M
innodb_flush_log_at_trx_commit = 2       # wydajność vs bezpieczeństwo (0,1,2)
innodb_flush_method            = O_DIRECT
innodb_file_per_table          = 1

# Połączenia
max_connections                = 300
wait_timeout                   = 600
interactive_timeout            = 600

# Query cache wyłączony w MySQL 8+ (usunięty)
# Zamiast tego: Magento cache + Varnish

# Slow query log
slow_query_log                 = 1
slow_query_log_file            = /var/log/mysql/slow.log
long_query_time                = 2
log_queries_not_using_indexes  = 1

Monitorowanie healthchecków

# Sprawdź stan wszystkich serwisów
docker compose ps

# Live monitoring statusów healthchecków
watch -n 5 'docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Health}}"'

# Alerty gdy serwis unhealthy - integracja z alertmanager lub prosta wersja:
docker compose events --filter type=health_status | while read event; do
    if echo "$event" | grep -q "unhealthy"; then
        # Wyślij alert (email, Slack, PagerDuty)
        echo "ALERT: $(date) - $event" | mail -s "Magento Health Alert" ops@example.com
    fi
done

Podsumowanie

Docker Compose na produkcji dla Magento 2 wymaga uwagi na kilka obszarów: separacja sieci (frontend/backend), Docker secrets dla haseł, healthchecki dla każdego serwisu, trzy osobne instancje Redis z właściwymi politykami eksmisji i MySQL tuning pod EAV schema Magento. Deployment bez downtime przez maintenance mode + rolling update PHP-FPM + Varnish purge to sprawdzony pattern. Dla większej skali i redundancji – Docker Swarm lub Kubernetes są naturalnym kolejnym krokiem.

About Henryk Tews

Co możesz przeczytać następne

DDEV zaawansowany – mutagen, własne serwisy, hooks, współdzielona konfiguracja w teamie
Blackfire – instalacja w DDEV, profilowanie HTTP i CLI, asercje w CI/CD
XAMPP vs DDEV vs Warden – pełne porównanie w tabeli
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}