Zero-Downtime Blue-Green Deployment Pipelines for WordPress Applications on OVH
Understanding the Blue-Green Deployment Model
Blue-Green deployment is a strategy that minimizes downtime and risk by running two identical production environments, referred to as “Blue” and “Green.” At any given time, only one environment is live and serving production traffic. The other environment is idle, used for deployment and testing. Once the new version is deployed and validated on the idle environment, traffic is switched over. This allows for instant rollback if issues arise, as the previous stable environment remains untouched.
OVH Infrastructure Prerequisites
For this setup on OVH, we’ll assume the following:
- Two identical Public Cloud instances (e.g., Ubuntu 22.04 LTS) configured identically for the WordPress application stack (web server, PHP-FPM, database).
- A load balancer (e.g., OVHcloud Load Balancer) capable of directing traffic to either the Blue or Green environment.
- A mechanism for managing application code and database migrations. We’ll use Git for code and a simple scripting approach for database changes.
- SSH access to all instances.
Setting Up Identical Environments (Blue & Green)
The core of blue-green deployment is having two fully independent, identical environments. This means:
- Web Server Configuration: Both instances must have the same web server configuration (e.g., Nginx or Apache) pointing to the application’s document root.
- PHP-FPM Configuration: Identical PHP-FPM pool configurations.
- Database: This is often the trickiest part. For simplicity in this example, we’ll assume a single, shared database instance that both Blue and Green environments connect to. This requires careful handling of database schema changes. A more robust approach might involve database replication or separate databases with data synchronization, but that significantly increases complexity.
- Application Code: The application code will reside in a specific directory (e.g.,
/var/www/html). - File Permissions: Ensure consistent file ownership and permissions across both environments.
Load Balancer Configuration (OVHcloud Load Balancer)
We’ll use the OVHcloud Load Balancer to manage traffic routing. The goal is to have the load balancer point to either the Blue or Green instance. We’ll use a simple health check to ensure the active environment is responsive.
Assume your Blue instance has the IP address 192.0.2.10 and your Green instance has 192.0.2.11. Your load balancer will have a public IP address, say 203.0.113.5.
Load Balancer Frontend Configuration
Configure a frontend listener on port 80 (and 443 if using SSL) on the load balancer’s public IP.
Load Balancer Backend Pool Configuration
Create a backend pool with both Blue and Green instances. Crucially, we’ll use a mechanism to *disable* one of them during deployment. This can be done via the OVHcloud API or control panel.
Health Check Configuration
Configure a health check that targets a specific file on your WordPress site (e.g., /healthcheck.php). This file should return a 200 OK status if WordPress is healthy.
<?php
// /var/www/html/healthcheck.php
header('Content-Type: text/plain');
echo 'OK';
exit(0);
?>
The load balancer will periodically request http://<instance_ip>/healthcheck.php. If it receives a 200 OK, the instance is considered healthy. If not, it will be temporarily removed from the active pool.
Deployment Workflow: Zero Downtime
Let’s assume the “Blue” environment is currently serving live traffic. We will deploy the new version to the “Green” environment.
Step 1: Prepare the Green Environment
First, ensure the Green environment is *not* receiving live traffic. This is typically managed by the load balancer. We will temporarily remove it from the active pool or ensure its health check fails.
Step 2: Deploy New Code to Green
Clone the latest stable version of your WordPress application code into the Green environment’s web root. This can be done via SSH and Git.
# SSH into the Green instance (e.g., 192.0.2.11) ssh [email protected] # Navigate to the web root cd /var/www/html # Ensure the directory is clean and ready for a new deployment git reset --hard HEAD git clean -fdx # Pull the latest code from your Git repository git pull origin main # Or your deployment branch # Set correct file permissions (adjust user/group as needed) chown -R www-data:www-data . find . -type d -exec chmod 755 {} \; find . -type f -exec chmod 644 {} \; # Clear any caching mechanisms (e.g., WP_CACHE, object cache) # This might involve clearing files or invalidating cache entries.
Step 3: Database Migrations (Critical Step)
This is where careful planning is essential. If your new code requires database schema changes, these must be backward-compatible with the *current* Blue environment’s code. The ideal scenario is to deploy code that can run with *both* the old and new schema versions for a brief period.
Option A: Backward-Compatible Schema Changes
- Deploy code that adds new tables or columns, but doesn’t remove or alter existing ones in a breaking way.
- Run database migration scripts on the shared database.
- The new code on Green can now utilize these changes. The old code on Blue will simply ignore them.
Option B: Separate Database (More Complex)
- Maintain separate databases for Blue and Green.
- Deploy code to Green.
- Run migration scripts on Green’s database.
- Synchronize data from Blue’s DB to Green’s DB (if necessary).
- Switch traffic.
- This requires robust data synchronization mechanisms.
For this example, we’ll assume Option A. A simple migration script might look like this:
<?php
// Example: /var/www/html/wp-content/mu-plugins/migration.php
// This is a simplified example. Use a proper migration library for production.
function run_my_custom_migration() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'my_new_table';
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
// Add a new column to an existing table (backward compatible)
$posts_table = $wpdb->prefix . 'posts';
if (null === $wpdb->get_var("SHOW COLUMNS FROM {$posts_table} LIKE 'custom_meta_field'")) {
$wpdb->query("ALTER TABLE {$posts_table} ADD COLUMN custom_meta_field VARCHAR(255) NULL AFTER post_content;");
}
}
// Hook into a WordPress action that runs on admin_init or similar
// Ensure this runs only once or is idempotent.
// For a true blue-green, you might run this manually via WP-CLI or a dedicated script.
// add_action('admin_init', 'run_my_custom_migration');
// In a real scenario, you'd trigger this via WP-CLI:
// wp --path=/var/www/html/ --allow-root plugin activate my-migration-plugin
// OR run a custom script that calls run_my_custom_migration()
?>
Important: Database migrations should be designed to be idempotent and backward-compatible. The best practice is to use a dedicated WordPress migration plugin or a WP-CLI command to execute these changes.
Step 4: Test the Green Environment
Before switching traffic, thoroughly test the Green environment. This can involve:
- Automated Tests: Run your full suite of integration and end-to-end tests against the Green environment’s IP address or a staging URL pointing to it.
- Manual QA: Have your QA team perform manual checks on critical user flows.
- Staging Access: If possible, configure a temporary DNS entry or use
/etc/hostson your local machine to access the Green environment directly via its IP address for testing.
Step 5: Switch Traffic
Once you are confident that the Green environment is stable and functioning correctly, it’s time to switch traffic. This is done at the load balancer level.
Method: API Call to OVHcloud Load Balancer
You’ll need to use the OVHcloud API to modify the backend pool. The exact API calls depend on your load balancer’s configuration (e.g., HAProxy, L7). Generally, you’ll want to:
- Get the current configuration of your backend pool.
- Update the pool to point *only* to the Green instance (
192.0.2.11). - Disable or remove the Blue instance (
192.0.2.10) from the active pool. - Apply the updated configuration.
This process should be very fast, ideally taking seconds. The load balancer will start directing new incoming requests to the Green environment. Existing connections to the Blue environment will complete without interruption.
Step 6: Monitor and Verify
Immediately after the switch, closely monitor your application’s performance, error logs, and key metrics. Check the load balancer’s health checks to ensure the Green environment is reporting as healthy.
Step 7: Rollback (If Necessary)
If any critical issues are detected in the Green environment after the switch, you can instantly roll back by reversing Step 5: re-enable the Blue instance in the load balancer’s backend pool and disable the Green instance. Since the Blue environment was untouched, it remains in its previous stable state.
Step 8: Prepare for Next Deployment
Once the Green environment is stable and serving traffic, the Blue environment becomes the idle environment. For the *next* deployment, you will repeat the process, deploying to the Blue environment while Green is live.
Automating the Deployment Pipeline
To make this truly effective, automation is key. Consider using CI/CD tools like:
- Jenkins, GitLab CI, GitHub Actions: To orchestrate the Git pull, database migration execution, and potentially API calls to the OVHcloud Load Balancer.
- WP-CLI: Essential for running database migrations, clearing caches, and performing other WordPress-specific tasks programmatically.
- OVHcloud API Client: A script (e.g., in Python or Bash using
curl) to interact with the OVHcloud Load Balancer API for traffic switching.
Example: Bash Script for Deployment Trigger
This is a conceptual script. You’d need to adapt it for your specific OVH API credentials and load balancer configuration.
#!/bin/bash
# Configuration
TARGET_ENV="green" # or "blue"
GREEN_INSTANCE_IP="192.0.2.11"
BLUE_INSTANCE_IP="192.0.2.10"
SSH_USER="your_ssh_user"
GIT_REPO="git@your-git-server:your-repo.git"
LB_API_ENDPOINT="https://api.ovh.com/1.0/loadBalancer/your-lb-id/backend/your-backend-id" # Placeholder
OVH_CONSUMER_KEY="YOUR_CONSUMER_KEY"
OVH_CONSUMER_SECRET="YOUR_CONSUMER_SECRET"
OVH_ACCESS_KEY="YOUR_ACCESS_KEY"
OVH_SECRET_KEY="YOUR_SECRET_KEY"
# --- Functions ---
# Function to execute commands on a remote server via SSH
remote_exec() {
local server_ip=$1
local cmd=$2
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SSH_USER}@${server_ip} "${cmd}"
}
# Function to deploy code to an instance
deploy_code() {
local instance_ip=$1
echo "Deploying code to ${instance_ip}..."
remote_exec ${instance_ip} "
cd /var/www/html && \
git reset --hard HEAD && \
git clean -fdx && \
git pull ${GIT_REPO} main && \
chown -R www-data:www-data . && \
find . -type d -exec chmod 755 {} \; && \
find . -type f -exec chmod 644 {} \; && \
echo 'Code deployment complete.'
"
if [ $? -ne 0 ]; then
echo "Error: Code deployment failed on ${instance_ip}."
exit 1
fi
}
# Function to run database migrations (using WP-CLI)
run_migrations() {
local instance_ip=$1
echo "Running database migrations on ${instance_ip}..."
remote_exec ${instance_ip} "
wp --path=/var/www/html/ --allow-root plugin activate my-migration-plugin || \
wp --path=/var/www/html/ --allow-root eval 'run_my_custom_migration();'
# Replace with your actual migration command
echo 'Migrations complete.'
"
if [ $? -ne 0 ]; then
echo "Error: Database migrations failed on ${instance_ip}."
exit 1
fi
}
# Function to switch traffic via OVH API (simplified example)
switch_traffic() {
local active_ip=$1
local inactive_ip=$2
echo "Switching traffic to ${active_ip}..."
# This is a placeholder. You'll need to construct the actual API call
# with proper authentication (signature generation) and payload.
# Example payload structure (hypothetical):
# {
# "servers": [
# {"address": "ACTIVE_IP", "status": "active", "port": 80},
# {"address": "INACTIVE_IP", "status": "inactive", "port": 80}
# ]
# }
echo "Simulating API call to OVHcloud Load Balancer to switch traffic."
echo "Current active: ${active_ip}, Inactive: ${inactive_ip}"
# curl -X PUT -H "Content-Type: application/json" -d '{"servers": [...] }' ${LB_API_ENDPOINT} --auth-basic ...
sleep 5 # Simulate API call duration
echo "Traffic switch complete."
}
# --- Main Deployment Logic ---
echo "Starting Blue-Green deployment to ${TARGET_ENV} environment..."
if [ "${TARGET_ENV}" == "green" ]; then
ACTIVE_IP=${BLUE_INSTANCE_IP}
TARGET_IP=${GREEN_INSTANCE_IP}
echo "Deploying to Green (currently inactive)."
else
ACTIVE_IP=${GREEN_INSTANCE_IP}
TARGET_IP=${BLUE_INSTANCE_IP}
echo "Deploying to Blue (currently inactive)."
fi
# 1. Deploy code
deploy_code ${TARGET_IP}
# 2. Run migrations (ensure they are backward compatible)
run_migrations ${TARGET_IP}
# 3. (Optional) Run automated tests against TARGET_IP
# 4. Switch traffic
switch_traffic ${TARGET_IP} ${ACTIVE_IP}
# 5. Monitor and verify (manual or automated checks)
echo "Deployment to ${TARGET_ENV} complete. Please monitor application health."
exit 0
Considerations and Best Practices
- Database Schema: This is the most critical part. Always ensure schema changes are backward-compatible. Deploying code that can work with both old and new schema versions is ideal.
- Stateful Applications: If your WordPress application relies heavily on session state or file uploads that are not managed centrally, ensure these are handled consistently across both environments or are not affected by the switch.
- Caching: Implement robust cache clearing mechanisms in your deployment script. This includes WordPress object cache, page cache plugins, and any CDN invalidation.
- SSL Certificates: Ensure SSL certificates are correctly installed and configured on both environments. If using a load balancer with SSL termination, ensure the certificate is managed there.
- Monitoring: Comprehensive monitoring of both environments and the load balancer is crucial for detecting issues early.
- Rollback Strategy: Always have a well-tested rollback procedure.
- Testing: Automate as much of the testing phase as possible.
By implementing a Blue-Green deployment strategy with careful attention to database management and automation, you can achieve zero-downtime deployments for your WordPress applications on OVH, significantly improving reliability and reducing operational risk.