Disaster Recovery 101: Architecting Auto-Failovers for MongoDB and Laravel Deployments on DigitalOcean
Establishing a MongoDB Replica Set for High Availability
A robust disaster recovery strategy for MongoDB hinges on a well-configured replica set. This ensures data redundancy and automatic failover in case of node failure. We’ll focus on a three-node replica set deployed across different DigitalOcean availability zones for maximum resilience.
First, ensure MongoDB is installed on your DigitalOcean Droplets. For this example, we’ll assume three Droplets: `mongo-primary` (primary), `mongo-secondary-1`, and `mongo-secondary-2`. Each Droplet should have MongoDB installed and running.
Configuring MongoDB for Replication
On each MongoDB server, edit the MongoDB configuration file (typically `/etc/mongod.conf`). We need to enable replication and specify a replica set name.
On `mongo-primary`:
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: "myReplicaSet"
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod.pid
On `mongo-secondary-1` and `mongo-secondary-2`, the configuration will be identical, except for the `bindIp` which should be set to `0.0.0.0` to allow connections from other nodes in the replica set. Restart MongoDB on all nodes after applying these changes.
Initializing the Replica Set
Connect to the MongoDB instance on the `mongo-primary` Droplet using the `mongo` shell. Then, initiate the replica set configuration.
mongo --host mongo-primary --port 27017
rs.initiate(
{
_id: "myReplicaSet",
members: [
{ _id: 0, host: "mongo-primary:27017" },
{ _id: 1, host: "mongo-secondary-1:27017" },
{ _id: 2, host: "mongo-secondary-2:27017" }
]
}
)
After running `rs.initiate()`, you can check the status with `rs.status()`. It might take a few moments for all members to sync and elect a primary. Ensure all members are in an `PRIMARY` or `SECONDARY` state.
Architecting Laravel for MongoDB Failover
Laravel applications need to be configured to connect to the MongoDB replica set. The connection string should point to the replica set name, allowing the driver to discover and connect to the current primary.
Configuring the Database Connection
In your Laravel application’s `.env` file, update the MongoDB connection details. We’ll use the `mongodb` driver. The connection string format is crucial for replica set awareness.
DB_CONNECTION=mongodb DB_HOST=mongo-primary,mongo-secondary-1,mongo-secondary-2 DB_PORT=27017 DB_DATABASE=your_database_name DB_USERNAME=your_db_user DB_PASSWORD=your_db_password DB_REPLICA_SET=myReplicaSet
If you are using a specific MongoDB driver package (e.g., `jenssegers/laravel-mongodb`), ensure its configuration supports replica set connections. The `DB_REPLICA_SET` variable is often used by these packages to construct the correct connection string.
For example, the `jenssegers/laravel-mongodb` package constructs the connection URI like this:
$uri = "mongodb://{$username}:{$password}@{$host}:{$port}/{$database}?replicaSet={$replicaSet}";
Ensure your Laravel application’s `config/database.php` file correctly uses these environment variables to build the MongoDB connection string.
Handling Connection Failures in Laravel
While the MongoDB driver and replica set handle the automatic failover at the database level, your Laravel application should be prepared for transient connection issues or situations where a failover might take a few seconds. Implementing retry logic or graceful error handling is essential.
You can leverage Laravel’s event system to react to database connection errors. For instance, you could dispatch a custom event when a database connection fails, which can then trigger notifications or fallback mechanisms.
// In a Service Provider or dedicated listener
use Illuminate\Support\Facades\Event;
use Illuminate\Database\Events\DatabaseConnectionFailed;
Event::listen(DatabaseConnectionFailed::class, function (DatabaseConnectionFailed $event) {
// Log the error, send a Slack notification, etc.
\Log::error("Database connection failed: " . $event->connectionName);
// Potentially trigger a more robust monitoring alert
// or attempt a read-only connection to a secondary if configured.
});
For critical operations, consider implementing a read preference that allows reading from secondaries, which can sometimes remain available even if the primary is temporarily unreachable during a failover. This requires careful consideration of data consistency requirements.
Automating Failover with DigitalOcean and External Monitoring
While MongoDB’s replica set handles internal failover, a comprehensive disaster recovery plan involves external monitoring and potentially automated actions on the infrastructure level. This is where DigitalOcean’s capabilities and external tools come into play.
External Health Checks and Load Balancers
For your Laravel application servers, you should implement health check endpoints. These endpoints should verify not only the application’s health but also its ability to connect to the MongoDB primary.
// routes/api.php or routes/web.php in Laravel
Route::get('/health', function () {
try {
// Attempt a simple database query to check connectivity
DB::connection('mongodb')->collection('users')->count(); // Or any small, quick query
return response()->json(['status' => 'ok', 'database' => 'connected']);
} catch (\Exception $e) {
report($e); // Log the exception
return response()->json(['status' => 'error', 'database' => 'disconnected'], 503);
}
});
Configure DigitalOcean Load Balancers to use these health check endpoints. If a Droplet running your Laravel application becomes unhealthy, the load balancer will automatically stop sending traffic to it. This is a crucial first layer of defense.
For MongoDB itself, while the replica set handles internal failover, you might want external monitoring to alert you to issues. Tools like Prometheus with MongoDB exporter, or DigitalOcean’s own monitoring, can be configured to alert on replica set health, oplog lag, and node status.
Automated Recovery and Scaling Actions
For more advanced automation, consider using DigitalOcean’s Kubernetes (DOKS) or custom scripts triggered by monitoring alerts. If a MongoDB node fails permanently, you might want to automate the process of provisioning a new Droplet, installing MongoDB, and adding it to the replica set.
This can be achieved with:
- Terraform/Ansible: Infrastructure as Code tools can define the desired state of your MongoDB cluster. If a node is missing, these tools can be used to provision and configure a replacement.
- Custom Scripts with DO API: A script could be triggered by an alert (e.g., via a webhook to a serverless function or a dedicated monitoring server). This script would use the DigitalOcean API to create a new Droplet, run a configuration management tool (like Ansible) to set up MongoDB, and then use the `rs.add()` command in MongoDB to join the new node to the replica set.
- Kubernetes Operators: If using DOKS, a MongoDB Operator can manage the replica set lifecycle, including automated recovery and scaling.
For example, a simple bash script using the `mongo` shell and `doctl` (DigitalOcean CLI) could look like this (this is a simplified illustration and would require robust error handling and idempotency for production):
#!/bin/bash # Assume a monitoring system detected a failed MongoDB node (e.g., mongo-secondary-3) FAILED_NODE="mongo-secondary-3" NEW_NODE_NAME="mongo-new-secondary-$(date +%s)" NEW_NODE_IP=$(doctl compute droplet create $NEW_NODE_NAME --size s-2vcpu-4gb --image ubuntu-22-04-x64 --region nyc3 --ssh-key YOUR_SSH_KEY_ID --wait | jq -r '.ipv4_address') if [ -z "$NEW_NODE_IP" ]; then echo "Failed to create new Droplet." exit 1 fi echo "Created new Droplet: $NEW_NODE_NAME with IP: $NEW_NODE_IP" # Provision MongoDB on the new node (using Ansible or similar is recommended) # For simplicity, we'll assume a basic setup script is run remotely ssh root@$NEW_NODE_IP "apt update && apt install -y mongodb && systemctl start mongod && sed -i 's/bindIp: 127.0.0.1/bindIp: 0.0.0.0/' /etc/mongod.conf && systemctl restart mongod" # Wait for MongoDB to be ready (add proper checks) sleep 60 # Add the new node to the replica set mongo --host mongo-primary --port 27017 <This script outlines the basic steps: create a new Droplet, install and configure MongoDB, and add it to the replica set. In a production environment, this would be orchestrated by a more robust system like Ansible or a Kubernetes operator.