Dockerizing and Orchestrating Legacy Shopify Systems on Modern OVH Infrastructure
Understanding the Challenge: Legacy Shopify & OVH Infrastructure
Migrating or modernizing legacy Shopify systems, often characterized by custom themes, extensive app integrations, and sometimes monolithic PHP architectures, onto a modern cloud infrastructure like OVH presents unique challenges. These systems were typically designed for a shared hosting environment with specific PHP versions, module dependencies, and filesystem structures. OVH’s Public Cloud, while offering robust IaaS and PaaS capabilities, requires a deliberate approach to containerization and orchestration to replicate or improve upon the original environment’s stability and performance.
The primary goal is to encapsulate the Shopify application, its dependencies (PHP, web server, database), and any custom code into portable, reproducible containers. This allows for consistent deployment across development, staging, and production environments on OVH, leveraging services like Kubernetes (via OVH’s managed Kubernetes offering) or Docker Swarm for orchestration.
Phase 1: Containerizing the Shopify Application Stack
We’ll start by building Docker images for the core components: the PHP application itself and its web server (typically Nginx or Apache). For a legacy Shopify setup, this often means a specific PHP version (e.g., PHP 7.x) with particular extensions enabled, and a database (MySQL/MariaDB).
Dockerfile for the Shopify PHP Application
This Dockerfile assumes a typical PHP application structure. We’ll use an official PHP base image and install necessary extensions. For Shopify, `gd`, `imagick`, `mysqli`, `pdo_mysql`, `curl`, and `zip` are commonly required.
# Use a specific PHP version compatible with your legacy Shopify setup
FROM php:7.4-fpm
# Set working directory
WORKDIR /var/www/html
# Install system dependencies
RUN apt-get update && apt-get install -y \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
libssl-dev \
libicu-dev \
libonig-dev \
unzip \
git \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install -j$(nproc) zip \
&& docker-php-ext-install -j$(nproc) pdo_mysql \
&& docker-php-ext-install -j$(nproc) mysqli \
&& docker-php-ext-install -j$(nproc) curl \
&& docker-php-ext-install -j$(nproc) intl \
&& docker-php-ext-install -j$(nproc) opcache
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Copy Shopify application code
# IMPORTANT: This assumes your Shopify code is in a 'shop' directory relative to the Dockerfile.
# You might need to adjust this based on your project structure.
COPY ./shop/ /var/www/html/
# Install Composer dependencies
RUN composer install --no-dev --optimize-autoloader
# Set appropriate permissions for web server
RUN chown -R www-data:www-data /var/www/html/storage \
&& chown -R www-data:www-data /var/www/html/bootstrap/cache
# Expose port 9000 for PHP-FPM
EXPOSE 9000
# Default command to run PHP-FPM
CMD ["php-fpm"]
Dockerfile for the Nginx Web Server
This Nginx configuration will serve the PHP application, forwarding requests to the PHP-FPM service.
FROM nginx:stable-alpine # Remove default Nginx configuration RUN rm -rf /etc/nginx/conf.d/* # Copy custom Nginx configuration COPY nginx.conf /etc/nginx/conf.d/default.conf # Copy static assets if any (e.g., for themes) # COPY ./public/ /usr/share/nginx/html/ # Expose port 80 EXPOSE 80 # Start Nginx in foreground CMD ["nginx", "-g", "daemon off;"]
Nginx Configuration (nginx.conf)
This configuration is crucial for routing requests to the PHP-FPM container and handling static files efficiently.
server {
listen 80;
server_name localhost; # Replace with your domain name
root /var/www/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
# Use the service name of your PHP-FPM container (e.g., 'php-fpm')
# and the port it's listening on (9000 by default)
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
location ~ /\.ht {
deny all;
}
# Cache static assets for a long time
location ~* \.(css|js|jpg|jpeg|gif|png|ico|svg|webp|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public";
}
}
Database Container (Docker Compose)
For simplicity in development and testing, we can use Docker Compose to define and run the multi-container Shopify application. For production, OVH’s managed database services (like Managed Databases for MySQL/PostgreSQL) are recommended.
version: '3.8'
services:
php-fpm:
build:
context: .
dockerfile: Dockerfile.php # Assuming your PHP Dockerfile is named Dockerfile.php
container_name: shopify_php_fpm
volumes:
- ./shop:/var/www/html # Mount your Shopify code for development
depends_on:
- mysql
networks:
- shopify_network
nginx:
build:
context: .
dockerfile: Dockerfile.nginx # Assuming your Nginx Dockerfile is named Dockerfile.nginx
container_name: shopify_nginx
ports:
- "8080:80" # Map host port 8080 to container port 80
volumes:
- ./shop:/var/www/html # Mount your Shopify code for development
depends_on:
- php-fpm
networks:
- shopify_network
mysql:
image: mysql:5.7 # Use a version compatible with your legacy system
container_name: shopify_mysql
environment:
MYSQL_ROOT_PASSWORD: your_root_password
MYSQL_DATABASE: shopify_db
MYSQL_USER: shopify_user
MYSQL_PASSWORD: shopify_password
volumes:
- mysql_data:/var/lib/mysql
networks:
- shopify_network
volumes:
mysql_data:
networks:
shopify_network:
driver: bridge
To build and run this stack locally:
# Navigate to the directory containing your Dockerfiles and docker-compose.yml cd /path/to/your/shopify/project # Build the images docker-compose build # Start the containers docker-compose up -d # Access your Shopify store at http://localhost:8080
Phase 2: Orchestration with OVH Managed Kubernetes (K8s)
For production deployments on OVH, Docker Compose is insufficient. We’ll leverage OVH’s Managed Kubernetes service. This involves defining Kubernetes resources (Deployments, Services, Ingress, PersistentVolumes) to manage the containerized application.
Kubernetes Deployment for PHP-FPM
This deployment will manage the PHP-FPM pods. We’ll use a persistent volume for the application code if it’s not baked into the image, or if updates are frequent.
apiVersion: apps/v1
kind: Deployment
metadata:
name: shopify-php-fpm
labels:
app: shopify
tier: backend
spec:
replicas: 3 # Adjust based on expected load
selector:
matchLabels:
app: shopify
tier: backend
template:
metadata:
labels:
app: shopify
tier: backend
spec:
containers:
- name: php-fpm
image: your-docker-registry/shopify-php-fpm:latest # Replace with your image path
ports:
- containerPort: 9000
volumeMounts:
- name: app-storage
mountPath: /var/www/html
volumes:
- name: app-storage
persistentVolumeClaim:
claimName: shopify-app-pvc # Defined below
Kubernetes Deployment for Nginx
This deployment manages the Nginx pods, which will serve traffic and proxy to PHP-FPM.
apiVersion: apps/v1
kind: Deployment
metadata:
name: shopify-nginx
labels:
app: shopify
tier: frontend
spec:
replicas: 3 # Adjust based on expected load
selector:
matchLabels:
app: shopify
tier: frontend
template:
metadata:
labels:
app: shopify
tier: frontend
spec:
containers:
- name: nginx
image: your-docker-registry/shopify-nginx:latest # Replace with your image path
ports:
- containerPort: 80
volumeMounts:
- name: app-storage
mountPath: /var/www/html # If Nginx needs access to app files
- name: nginx-config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf # Mount specific file
volumes:
- name: app-storage
persistentVolumeClaim:
claimName: shopify-app-pvc # Same PVC as PHP-FPM
- name: nginx-config-volume
configMap:
name: nginx-configmap # Defined below
Kubernetes Service for PHP-FPM
This Service provides a stable internal IP address and DNS name for Nginx to connect to PHP-FPM.
apiVersion: v1
kind: Service
metadata:
name: shopify-php-fpm-svc
labels:
app: shopify
spec:
selector:
app: shopify
tier: backend
ports:
- protocol: TCP
port: 9000
targetPort: 9000
clusterIP: None # Headless service for direct pod access if needed, or a ClusterIP for stable endpoint
Kubernetes Service for Nginx
This Service exposes the Nginx pods to the cluster, typically via an Ingress controller.
apiVersion: v1
kind: Service
metadata:
name: shopify-nginx-svc
labels:
app: shopify
spec:
selector:
app: shopify
tier: frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP # Ingress will handle external access
Kubernetes Ingress for External Access
An Ingress resource is used to manage external access to the services in a cluster, typically HTTP. OVH’s Managed Kubernetes often comes with an Ingress controller (like Nginx Ingress Controller or Traefik) pre-installed or easily deployable.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shopify-ingress
annotations:
# Annotations specific to your Ingress controller (e.g., Nginx Ingress)
nginx.ingress.kubernetes.io/rewrite-target: /
# Add SSL/TLS configuration here if using OVH Load Balancer with SSL termination
spec:
rules:
- host: your-shopify-domain.com # Replace with your actual domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shopify-nginx-svc
port:
number: 80
Kubernetes Persistent Volume Claim (PVC) and Storage
For persistent storage of application code, logs, or uploads, we need PVCs. OVH Public Cloud offers various storage options (e.g., Block Storage, Object Storage) that can be integrated with Kubernetes.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shopify-app-pvc
spec:
accessModes:
- ReadWriteOnce # Or ReadWriteMany if your storage driver supports it and needed
resources:
requests:
storage: 10Gi # Adjust size as needed
storageClassName: ovh-block-storage # Or your specific OVH storage class name
You’ll need to ensure the `storageClassName` matches a provisioner configured in your OVH K8s cluster that maps to OVH’s storage solutions.
Database Management on OVH
Instead of running MySQL in a container within Kubernetes for production, it’s highly recommended to use OVH’s Managed Databases for MySQL or PostgreSQL. This offloads database administration, scaling, and high availability to OVH.
Your application containers (PHP-FPM) will then connect to the managed database endpoint provided by OVH. Ensure your application’s database credentials and connection strings are managed securely, perhaps using Kubernetes Secrets.
Phase 3: CI/CD Pipeline and Monitoring
A robust CI/CD pipeline is essential for automating the build, test, and deployment process. Tools like GitLab CI, GitHub Actions, or Jenkins can be integrated with your OVH Kubernetes cluster.
Example GitLab CI Configuration (.gitlab-ci.yml)
This is a simplified example. You’ll need to configure your Docker registry (e.g., GitLab Container Registry, Docker Hub, OVH’s registry) and Kubernetes context.
variables:
DOCKER_REGISTRY: registry.gitlab.com/your-group/your-project
KUBE_CONTEXT: your-ovh-kube-context # Configured in GitLab CI/CD settings
stages:
- build
- deploy
build-php-fpm:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_REGISTRY/shopify-php-fpm:$CI_COMMIT_SHA -f Dockerfile.php .
- docker push $DOCKER_REGISTRY/shopify-php-fpm:$CI_COMMIT_SHA
only:
- main
build-nginx:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_REGISTRY/shopify-nginx:$CI_COMMIT_SHA -f Dockerfile.nginx .
- docker push $DOCKER_REGISTRY/shopify-nginx:$CI_COMMIT_SHA
only:
- main
deploy-to-k8s:
stage: deploy
image: google/cloud-sdk:latest # Or an image with kubectl and helm
script:
- echo "Deploying to Kubernetes..."
# Use kubectl apply with updated image tags
- sed "s|your-docker-registry/shopify-php-fpm:latest|$DOCKER_REGISTRY/shopify-php-fpm:$CI_COMMIT_SHA|g" kubernetes/deployment-php-fpm.yaml | kubectl apply -f - --context=$KUBE_CONTEXT
- sed "s|your-docker-registry/shopify-nginx:latest|$DOCKER_REGISTRY/shopify-nginx:$CI_COMMIT_SHA|g" kubernetes/deployment-nginx.yaml | kubectl apply -f - --context=$KUBE_CONTEXT
# Apply other resources like Services, Ingress, PVCs if they change
- kubectl apply -f kubernetes/service-php-fpm.yaml --context=$KUBE_CONTEXT
- kubectl apply -f kubernetes/service-nginx.yaml --context=$KUBE_CONTEXT
- kubectl apply -f kubernetes/ingress.yaml --context=$KUBE_CONTEXT
- kubectl apply -f kubernetes/pvc.yaml --context=$KUBE_CONTEXT
environment:
name: production
url: http://your-shopify-domain.com
only:
- main
Monitoring and Logging
Integrate monitoring tools like Prometheus and Grafana, and a logging solution (e.g., ELK stack, Loki) within your Kubernetes cluster. OVH’s Managed Kubernetes often provides integrations or allows for easy deployment of these tools.
Key metrics to monitor include:
- Pod CPU and Memory usage
- Nginx request rates, error rates (5xx, 4xx)
- PHP-FPM process counts and queue lengths
- Database connection counts and query latency
- Application-specific metrics (e.g., order processing time)
Conclusion and Next Steps
Dockerizing and orchestrating legacy Shopify systems on OVH infrastructure is a significant undertaking. It requires a deep understanding of both the legacy application’s dependencies and the capabilities of modern containerization and cloud platforms. By following a phased approach—containerization, orchestration with Kubernetes, and robust CI/CD and monitoring—you can achieve a more scalable, resilient, and manageable Shopify environment on OVH Public Cloud.
Future considerations might include exploring service meshes (like Istio) for advanced traffic management and security, implementing autoscaling for your deployments based on load, and optimizing database performance with OVH’s managed services.