Każdy developer Magento 2 zna ten problem: produkcyjna baza danych ma 50GB, czas importu 3 godziny, a po imporcie konfiguracja wskazuje na produkcyjne URL-e i tokeny. Pokazuję kompletny workflow: jak wyciągnąć użyteczny dump z produkcji, automatycznie oczyścić dane wrażliwe, zmienić konfigurację środowiskową i skrócić czas importu z godzin do minut.
Dlaczego dump produkcji jest problematyczny
Bezpośredni dump produkcyjnej bazy Magento 2 ma kilka problemów:
- Rozmiar – tabele logów, quote, session i catalog search indeksy mogą stanowić 80% rozmiaru
- Dane wrażliwe – emaile klientów, hasła, dane kart (tokeny), adresy
- Konfiguracja – URL-e, klucze API, hasła do zewnętrznych serwisów
- Czas importu – 50GB MySQLdump przez standardowy import = kilka godzin
Skrypt dump z wykluczeniami – magedbm2 alternatywa
#!/bin/bash
# dump-prod.sh - inteligentny dump Magento 2
DB_HOST="db.prod.example.com"
DB_USER="magento_ro" # użytkownik tylko do odczytu!
DB_NAME="magento_prod"
OUTPUT_FILE="magento_$(date +%Y%m%d_%H%M).sql.gz"
# Tabele które dumujemy bez danych (tylko struktura)
# Logi, sesje, cache i tabele EAV indeksów
NO_DATA_TABLES=(
"admin_passwords"
"cache"
"cache_tag"
"catalog_product_index_eav"
"catalog_product_index_eav_decimal"
"catalog_product_index_eav_decimal_idx"
"catalog_product_index_eav_idx"
"catalog_product_index_price"
"catalog_product_index_price_bundle_idx"
"catalog_product_index_price_idx"
"catalogsearch_fulltext_scope1"
"customer_log"
"customer_visitor"
"importexport_importdata"
"oauth_nonce"
"oauth_token"
"persistent_session"
"quote"
"quote_address"
"quote_id_mask"
"quote_item"
"quote_payment"
"report_event"
"report_viewed_product_index"
"session"
)
echo "Tworzę dump Magento 2 z ${DB_NAME}..."
# 1. Dump z danymi (z wyłączeniem tabel bez danych)
IGNORE_ARGS=""
for table in "${NO_DATA_TABLES[@]}"; do
IGNORE_ARGS="${IGNORE_ARGS} --ignore-table=${DB_NAME}.${table}"
done
mysqldump \
--host="${DB_HOST}" \
--user="${DB_USER}" \
--single-transaction \
--quick \
--lock-tables=false \
--no-tablespaces \
${IGNORE_ARGS} \
"${DB_NAME}" | gzip > "${OUTPUT_FILE}.tmp"
# 2. Dodaj strukturę (bez danych) dla wykluczonych tabel
for table in "${NO_DATA_TABLES[@]}"; do
mysqldump \
--host="${DB_HOST}" \
--user="${DB_USER}" \
--no-data \
--single-transaction \
"${DB_NAME}" "${table}" >> "${OUTPUT_FILE}.tmp.sql" 2>/dev/null || true
done
echo "Dump gotowy: ${OUTPUT_FILE}"
echo "Rozmiar: $(du -sh ${OUTPUT_FILE} | cut -f1)"
Anonimizacja danych klientów – RODO compliance
<?php
declare(strict_types=1);
// Skrypt do uruchomienia po imporcie na dev/staging
// Maskuje dane osobowe klientów
class MagentoDataAnonymizer
{
public function __construct(private \PDO $pdo) {}
public function anonymize(): void
{
$this->pdo->beginTransaction();
try {
$this->anonymizeCustomers();
$this->anonymizeOrders();
$this->anonymizeNewsletterSubscribers();
$this->clearSensitiveConfig();
$this->pdo->commit();
echo "Anonimizacja zakończona\n";
} catch (\Exception $e) {
$this->pdo->rollBack();
throw $e;
}
}
private function anonymizeCustomers(): void
{
// Zaktualizuj emaile i dane osobowe zachowując strukturę
$this->pdo->exec("
UPDATE customer_entity
SET
email = CONCAT('customer_', entity_id, '@example.com'),
firstname = CONCAT('Firstname', entity_id),
lastname = CONCAT('Lastname', entity_id)
");
// Zamiast prawdziwych haseł – znane hasło dla dev/staging
// Password: 'Password123!'
$devHash = password_hash('Password123!', PASSWORD_BCRYPT, ['cost' => 10]);
$this->pdo->prepare("UPDATE customer_entity SET password_hash = ?")->execute([$devHash]);
echo "Zanonimizowano klientów: " . $this->pdo->query("SELECT ROW_COUNT()")->fetchColumn() . "\n";
}
private function anonymizeOrders(): void
{
// Anonimizuj adresy w zamówieniach
$this->pdo->exec("
UPDATE sales_order
SET
customer_email = CONCAT('order_', entity_id, '@example.com'),
customer_firstname = CONCAT('Order', entity_id),
customer_lastname = 'Customer'
");
$this->pdo->exec("
UPDATE sales_order_address
SET
email = CONCAT('addr_', entity_id, '@example.com'),
firstname = 'Test',
lastname = 'Customer',
street = 'ul. Testowa 1',
telephone = '123456789'
");
}
private function anonymizeNewsletterSubscribers(): void
{
$this->pdo->exec("
UPDATE newsletter_subscriber
SET subscriber_email = CONCAT('newsletter_', subscriber_id, '@example.com')
");
}
private function clearSensitiveConfig(): void
{
// Wyczyść klucze API, tokeny, hasła do zewnętrznych serwisów
$sensitiveKeys = [
'payment/braintree/private_key',
'payment/braintree/public_key',
'payment/stripe/api_key',
'system/smtp/password',
'carriers/fedex/key',
'carriers/dhl/id',
'catalog/magento_catalogpermissions/enrich_product_security_data',
];
foreach ($sensitiveKeys as $path) {
$this->pdo->prepare("
DELETE FROM core_config_data WHERE path = ?
")->execute([$path]);
}
}
}
Zmiana konfiguracji URL po imporcie
#!/bin/bash
# post-import.sh - uruchom po imporcie bazy na lokalnym środowisku
MAGE_ROOT="/var/www/magento"
echo "Ustawianie konfiguracji dev..."
# Zmień URL-e na lokalne
php "${MAGE_ROOT}/bin/magento" config:set web/unsecure/base_url "http://magento2-dev.ddev.site/"
php "${MAGE_ROOT}/bin/magento" config:set web/secure/base_url "https://magento2-dev.ddev.site/"
# Wyłącz cache na czas debugowania
php "${MAGE_ROOT}/bin/magento" config:set system/full_page_cache/caching_application 1
# Wyłącz płatności produkcyjne
php "${MAGE_ROOT}/bin/magento" config:set payment/braintree/active 0
php "${MAGE_ROOT}/bin/magento" config:set payment/stripe/active 0
# Ustaw tryb dev
php "${MAGE_ROOT}/bin/magento" deploy:mode:set developer
# Uruchom anonimizację danych
php "${MAGE_ROOT}/bin/console" vendor:anonymize-data
# Reindeksuj
php "${MAGE_ROOT}/bin/magento" indexer:reindex
# Flush cache
php "${MAGE_ROOT}/bin/magento" cache:flush
echo "Środowisko dev gotowe!"
echo "URL: https://magento2-dev.ddev.site/"
echo "Admin: https://magento2-dev.ddev.site/admin"
Szybszy import – mydumper zamiast mysqldump
# mydumper - wielowątkowy dump/restore, 5-10x szybszy od mysqldump
# Instalacja
apt-get install mydumper # Ubuntu
brew install mydumper # macOS
# Dump z 8 wątkami
mydumper \
--host db.prod.example.com \
--user magento_ro \
--database magento_prod \
--outputdir /tmp/magento-dump \
--threads 8 \
--compress \
--regex "^(?!(magento_prod\.(cache|session|quote)))" \
--verbose 3
# Czas: ~5 min zamiast ~40 min dla 20GB bazy
# Import z 8 wątkami
myloader \
--host localhost \
--user magento \
--password secret \
--database magento_dev \
--directory /tmp/magento-dump \
--threads 8 \
--verbose 3
# Czas: ~8 min zamiast ~60 min
Automatyzacja przez Makefile
# Makefile - jedno polecenie do pełnego refresh lokalnej bazy
.PHONY: db-refresh
db-refresh:
@echo "Pobieranie dumpu z produkcji..."
ssh deploy@prod.example.com "cd /var/www && bash dump-prod.sh" && \
scp deploy@prod.example.com:/var/www/magento_latest.sql.gz /tmp/
@echo "Importowanie do lokalnej bazy..."
ddev exec "gzip -dc /tmp/magento_latest.sql.gz | mysql magento"
@echo "Post-import konfiguracja..."
ddev exec "cd /var/www/magento && bash post-import.sh"
@echo "Gotowe! Środowisko dev zaktualizowane."
Podsumowanie
Odświeżanie lokalnej bazy Magento 2 z produkcji nie musi trwać godzinami i narażać danych klientów. Kluczowe elementy: wykluczanie tabel-śmietników z dumpa (zmniejsza rozmiar 5-10x), mydumper dla równoległego eksportu/importu (5-10x szybszy), automatyczna anonimizacja danych klientów (RODO compliance) i skrypt post-import który ustawia konfigurację dev. Zmakefile-owane w jedno polecenie – refresh bazy to kwestia kilku minut, nie godzin.
