Dockerizing and Orchestrating Legacy Magento 2 Systems on Modern Google Cloud Infrastructure
Containerizing the Magento 2 Monolith
Migrating a legacy Magento 2 installation to a containerized environment on Google Cloud Platform (GCP) requires a methodical approach, especially when dealing with complex dependencies and stateful services like databases and caches. Our goal is to break down the monolithic Magento application into manageable, independently scalable services.
We’ll start by creating a Dockerfile for the core Magento application. This involves carefully selecting a base image, installing necessary PHP extensions, configuring web server settings, and ensuring all Magento dependencies are met. For a typical Magento 2 setup, this means PHP with specific extensions (e.g., `gd`, `intl`, `soap`, `zip`, `opcache`), Composer, and a web server like Nginx or Apache.
Dockerfile for Magento Application
This Dockerfile assumes a Debian-based image and installs common Magento 2 requirements. We’ll use Nginx as the web server. Adjust PHP version and extensions as per your specific Magento installation.
# Use an official PHP image with a specific version and extensions
FROM php:8.1-fpm-buster
# Set environment variables
ENV MAGENTO_VERSION=2.4.6 \
COMPOSER_HOME=/composer \
COMPOSER_ALLOW_SUPERUSER=1 \
PATH="/composer/vendor/bin:$PATH"
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libxslt1-dev \
libicu-dev \
libonig-dev \
libxml2-dev \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libcurl4-openssl-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libxslt1-dev \
libicu-dev \
libonig-dev \
libxml2-dev \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libcurl4-openssl-dev \
acl \
supervisor \
nginx \
vim \
cron \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install -j$(nproc) intl \
&& docker-php-ext-install -j$(nproc) soap \
&& docker-php-ext-install -j$(nproc) zip \
&& docker-php-ext-install -j$(nproc) opcache \
&& docker-php-ext-install -j$(nproc) bcmath \
&& docker-php-ext-install -j$(nproc) sockets \
&& docker-php-ext-install -j$(nproc) xml \
&& docker-php-ext-install -j$(nproc) pdo pdo_mysql \
&& pecl install redis \
&& docker-php-ext-enable redis
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Configure Nginx
COPY nginx/default.conf /etc/nginx/sites-available/default
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default \
&& sed -i 's/listen \[::\]\:80/listen 80/g' /etc/nginx/sites-available/default
# Configure Supervisor for PHP-FPM and Cron
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Set working directory
WORKDIR /var/www/html
# Copy Magento application code (assuming it's in a 'src' directory locally)
# In a real-world scenario, you'd likely use a volume or a build-time copy
COPY src/ /var/www/html/
# Ensure correct permissions for Magento files
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& find /var/www/html -type f -exec chmod 644 {} \; \
&& find /var/www/html -type d -exec chmod 755 {} \; \
&& chown www-data:www-data /var/www/html/var/log \
&& chown www-data:www-data /var/www/html/var/session \
&& chown www-data:www-data /var/www/html/pub/media \
&& chown www-data:www-data /var/www/html/pub/static
# Install Magento dependencies via Composer
RUN composer install --no-dev --optimize-autoloader
# Magento specific commands (run these during deployment, not necessarily in Dockerfile)
# RUN php bin/magento setup:upgrade
# RUN php bin/magento setup:static-content:deploy en_US en_GB --theme Magento/luma
# RUN php bin/magento indexer:reindex
# RUN php bin/magento cache:flush
# Expose port 80 for Nginx
EXPOSE 80
# Start Supervisor to manage Nginx and PHP-FPM
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
Nginx Configuration
The Nginx configuration is crucial for serving Magento efficiently. It needs to handle static files, proxy requests to PHP-FPM, and manage SSL termination (which we’ll offload to GCP Load Balancer later).
server {
listen 80;
server_name localhost; # Replace with your domain name
root /var/www/html/pub;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public";
access_log off;
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust socket path if needed
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Prevent other PHP files from being executed
try_files $uri =404;
}
# Deny access to hidden files
location ~ /\. {
deny all;
}
# Magento specific security settings
location ~ ^/(app/etc|bin/Magento|composer\.json|composer\.lock) {
deny all;
}
# Magento static content directory
location /static/ {
# Ensure static content is served directly by Nginx
expires 1y;
add_header Cache-Control "public";
access_log off;
autoindex off;
try_files $uri $uri/ /static/index.php?$args;
}
# Magento media directory
location /media/ {
expires 1y;
add_header Cache-Control "public";
access_log off;
autoindex off;
try_files $uri $uri/ /media/index.php?$args;
}
}
Supervisor Configuration
Supervisor will manage the PHP-FPM process and the cron daemon, ensuring they restart if they crash. This is essential for a robust containerized application.
[program:php-fpm] command=/usr/sbin/php-fpm8.1 --nodaemonize autostart=true autorestart=true priority=10 stdout_logfile=/var/log/supervisor/php-fpm.log stderr_logfile=/var/log/supervisor/php-fpm.err.log [program:nginx] command=/usr/sbin/nginx -g "daemon off;" autostart=true autorestart=true priority=20 stdout_logfile=/var/log/supervisor/nginx.log stderr_logfile=/var/log/supervisor/nginx.err.log [program:cron] command=/usr/sbin/cron -f autostart=true autorestart=true priority=30 stdout_logfile=/var/log/supervisor/cron.log stderr_logfile=/var/log/supervisor/cron.err.log
Database and Cache Services
Magento 2 relies heavily on a robust database (MySQL) and a fast caching layer (Redis). These will be deployed as separate services within GCP.
Cloud SQL for MySQL
For production, we’ll use Google Cloud SQL for MySQL. This managed service handles patching, backups, and replication, significantly reducing operational overhead. When deploying, ensure your Magento container can connect to the Cloud SQL instance. This typically involves configuring firewall rules and using the Cloud SQL Auth Proxy or direct IP access (less recommended for security).
Key Configuration Points:
- Instance Type: Choose an instance size that can handle your read/write load.
- High Availability: Enable for production to ensure uptime.
- Backups: Configure automated backups and retention policies.
- Replication: Set up read replicas for scaling read operations if necessary.
- Network: Configure private IP for secure access from your GKE cluster.
- Database User: Create a dedicated user for Magento with appropriate privileges.
Memorystore for Redis
Redis is critical for Magento’s performance. Google Cloud Memorystore offers a managed Redis service. Similar to Cloud SQL, configure network access for your containers.
Key Configuration Points:
- Instance Size: Select based on your cache hit rate and data volume.
- Replication: Use read replicas if your application pattern benefits from it.
- Network: Configure private IP for secure access.
- Persistence: Understand Redis persistence options (RDB, AOF) and their impact on performance and data durability. For caching, durability might be less critical than for a primary data store.
Orchestration with Google Kubernetes Engine (GKE)
GKE is the ideal platform for orchestrating our containerized Magento services. It provides automated deployment, scaling, and management of containerized applications.
Kubernetes Deployment and Service Definitions
We’ll define Kubernetes Deployments for our Magento application and potentially for background worker processes (if you separate them). Services will expose these deployments internally and externally.
Magento Application Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: magento-app
labels:
app: magento
spec:
replicas: 3 # Adjust based on traffic
selector:
matchLabels:
app: magento
template:
metadata:
labels:
app: magento
spec:
containers:
- name: magento
image: your-gcr-repo/magento-app:latest # Replace with your image
ports:
- containerPort: 80
env:
- name: DB_HOST
value: "your-cloudsql-instance-ip" # Or use Cloud SQL Auth Proxy sidecar
- name: DB_NAME
value: "magento_db"
- name: DB_USER
value: "magento_user"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: magento-db-secrets
key: password
- name: REDIS_HOST
value: "your-redis-instance-ip" # Or use Memorystore DNS name
- name: REDIS_PORT
value: "6379"
# Add other Magento specific environment variables
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /healthz # You might need to create a health check endpoint
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 5
# Consider using a sidecar for Cloud SQL Auth Proxy if not using private IP
# - name: cloudsql-proxy
# image: gcr.io/cloudsql-docker/gce-proxy:1.19.1
# command: ["/cloud_sql_proxy", "-instances=your-project:your-region:your-instance=tcp:3306"]
# ports:
# - containerPort: 3306
Magento Application Service:
apiVersion: v1
kind: Service
metadata:
name: magento-service
spec:
selector:
app: magento
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP # This will be exposed via an Ingress or LoadBalancer
Ingress Controller and Load Balancing
We’ll use a GCP Load Balancer, managed by a Kubernetes Ingress controller, to expose the Magento application to the internet. This handles SSL termination, traffic routing, and can integrate with Cloud CDN for static assets.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: magento-ingress
annotations:
kubernetes.io/ingress.class: "gce" # For GCP GKE
# For SSL termination with Google-managed certificates
networking.gke.io/managed-certificates: "magento-managed-cert"
spec:
rules:
- host: "your.magento.domain.com" # Replace with your domain
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: magento-service
port:
number: 80
---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: magento-managed-cert
spec:
domains:
- "your.magento.domain.com" # Replace with your domain
Ensure you have a Google-managed SSL certificate configured for your domain. The Ingress controller will provision and manage this certificate.
Persistent Storage for Media and Static Content
Magento’s `pub/media` and `pub/static` directories need persistent storage. We’ll use Google Cloud Filestore or Persistent Disks. For `pub/media`, a shared filesystem is often preferred if you have multiple Magento instances or need to sync uploads across pods. For `pub/static`, it can be generated on deployment or mounted from a persistent volume.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: magento-media-pvc
spec:
accessModes:
- ReadWriteMany # For Filestore or NFS-compatible storage
resources:
requests:
storage: 10Gi # Adjust size as needed
storageClassName: "your-filestore-class" # Or a suitable CSI driver for PD
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: magento-static-pvc
spec:
accessModes:
- ReadWriteOnce # For Persistent Disk
resources:
requests:
storage: 5Gi
storageClassName: "your-pd-class" # e.g., standard-rwo
You would then mount these PVCs into your Magento application pods. For `pub/static`, consider generating it during the Docker build or as a Kubernetes Job on deployment to avoid mounting a read-write volume for static assets.
Deployment Workflow and CI/CD
A robust CI/CD pipeline is essential for managing deployments, updates, and rollbacks. We’ll leverage Cloud Build for automated builds and deployments to GKE.
Cloud Build Configuration
A `cloudbuild.yaml` file defines the steps for building the Docker image, pushing it to Google Container Registry (GCR), and deploying to GKE.
steps:
# Build the Docker image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/magento-app:$COMMIT_SHA', '.']
# Push the Docker image to GCR
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/magento-app:$COMMIT_SHA']
# Deploy to GKE
- name: 'gcr.io/cloud-builders/kubectl'
args:
- 'apply'
- '-f'
- 'k8s/deployment.yaml' # Your deployment YAML
env:
- 'CLOUDSDK_COMPUTE_ZONE=your-gke-zone'
- 'CLOUDSDK_CONTAINER_CLUSTER=your-gke-cluster'
# Optional: Run Magento setup/upgrade commands as a Kubernetes Job
# - name: 'gcr.io/cloud-builders/kubectl'
# args:
# - 'create'
# - 'job'
# - '--from=file=k8s/magento-setup-job.yaml'
# env:
# - 'CLOUDSDK_COMPUTE_ZONE=your-gke-zone'
# - 'CLOUDSDK_CONTAINER_CLUSTER=your-gke-cluster'
images:
- 'gcr.io/$PROJECT_ID/magento-app:$COMMIT_SHA'
You’ll need to create a Kubernetes Job YAML for running `bin/magento setup:upgrade`, `setup:static-content:deploy`, and `indexer:reindex` post-deployment. This job should run only when a new image is deployed and should have access to the database and Redis.
Managing Magento Configuration and Secrets
Magento’s configuration (`app/etc/env.php`) and sensitive credentials should be managed securely. Use Kubernetes Secrets for database passwords, API keys, etc. For `app/etc/env.php`, you can either:
- Mount it as a ConfigMap or Secret, updating it dynamically during deployment.
- Use environment variables and have an entrypoint script in your Docker image that generates `app/etc/env.php` on startup based on these variables.
The latter approach is generally more flexible in a containerized environment.
Performance Tuning and Monitoring
Once deployed, continuous monitoring and tuning are critical for a high-performance Magento store.
GCP Monitoring and Logging
Leverage Google Cloud’s operations suite (formerly Stackdriver) for metrics, logging, and tracing. Ensure your containers are configured to send logs to stdout/stderr, which Cloud Logging can collect.
Magento Specific Tuning
Key areas to monitor and tune:
- OpCache: Ensure it’s enabled and configured optimally in your PHP-FPM settings.
- Redis: Monitor hit/miss ratios and memory usage.
- Database: Analyze slow queries, optimize indexing, and ensure proper connection pooling.
- Cron Jobs: Offload cron jobs to dedicated pods or a separate cron service to avoid impacting web requests.
- Static Content Deployment: Automate this process during deployments to ensure fresh assets.
- Varnish Cache: For high-traffic sites, consider integrating Varnish as a caching layer in front of Nginx. This would typically be another service in your GKE cluster or a managed service.
By containerizing your legacy Magento 2 system and orchestrating it on GCP with GKE, Cloud SQL, and Memorystore, you gain scalability, resilience, and a more manageable infrastructure for your e-commerce platform.