Server Monitoring Best Practices: Keeping Your Shopify App and MongoDB Clusters Alive on DigitalOcean
Proactive MongoDB Health Checks with `mongostat` and `mongotop`
Maintaining the stability of your MongoDB clusters, especially those powering critical Shopify applications, hinges on continuous, granular monitoring. Relying solely on DigitalOcean’s basic droplet metrics (CPU, RAM, Disk I/O) is insufficient for diagnosing deep-seated performance bottlenecks within MongoDB itself. We need tools that speak MongoDB’s language.
The MongoDB distribution includes two invaluable command-line utilities: mongostat and mongotop. These tools provide real-time insights into the operational status and performance characteristics of your MongoDB instances. Integrating their output into your monitoring pipeline is a crucial step beyond generic infrastructure monitoring.
Leveraging `mongostat` for Operational Metrics
mongostat offers a snapshot of key operational metrics for each MongoDB instance in a replica set or sharded cluster. It’s excellent for identifying immediate issues like high query latency, excessive lock contention, or network saturation.
To get started, ensure you have SSH access to your MongoDB nodes. The basic command is straightforward:
ssh user@mongodb-node-1 "mongostat --host localhost --port 27017 --username your_mongo_user --password your_mongo_password --authenticationDatabase admin --discover --interval 5"
Let’s break down the essential flags:
--hostand--port: Specify the MongoDB instance to connect to.--username,--password,--authenticationDatabase: For authenticated connections. Always use dedicated monitoring users with least privilege.--discover: Crucial for replica sets and sharded clusters. It automatically discovers and monitors all members.--interval 5: Collects and displays statistics every 5 seconds. Adjust this based on your desired granularity and monitoring system’s polling frequency.
The output of mongostat is tabular and includes metrics like:
insert,query,update,delete: Operations per second. Spikes here can indicate application load changes.getmore: Number ofgetMoreoperations per second. High values can suggest inefficient cursor usage or large result sets being fetched incrementally.command: Number of commands per second.dirty: Percentage of dirty pages in the cache. High values might indicate insufficient RAM or heavy write loads.used: Percentage of RAM used by the MongoDB process.qrw,qw: Queue length for read and write operations. Non-zero values indicate operations are waiting.arw,aw: Active operations for read and write.netIn,netOut: Network traffic in KB/sec.conn: Number of active client connections.locks: Lock acquisition statistics. High lock contention (e.g.,.for global locks,Wfor write locks) is a major performance killer.
To integrate this into a monitoring system like Prometheus, you’d typically use a tool like prom-client (Node.js) or a custom Python script that parses mongostat output and exposes it as Prometheus metrics via an exporter. For a simpler approach, consider using tools like collectd with its MongoDB plugin, which can directly query MongoDB and expose metrics.
Using `mongotop` for Read/Write Latency Insights
While mongostat gives you operation counts, mongotop focuses on the time spent on read and write operations, providing a more direct measure of latency at the operation level.
Similar to mongostat, you can run it via SSH:
ssh user@mongodb-node-1 "mongotop --host localhost --port 27017 --username your_mongo_user --password your_mongo_password --authenticationDatabase admin --interval 10"
The key flags are:
--interval 10: Update statistics every 10 seconds.
mongotop‘s output shows the time spent (in milliseconds) reading and writing per collection. The columns are:
ns: Namespace (database.collection).total: Total time spent on operations for this namespace.read: Time spent on read operations.write: Time spent on write operations.
High values in the read or write columns for specific collections are strong indicators of performance issues within those collections. This could be due to inefficient queries, missing indexes, or heavy contention.
For automated monitoring, you can parse this output. A Python script could look like this:
import subprocess
import re
import time
MONGO_HOST = "mongodb-node-1"
MONGO_PORT = "27017"
MONGO_USER = "your_mongo_user"
MONGO_PASS = "your_mongo_password"
AUTH_DB = "admin"
INTERVAL = 10
def get_mongotop_data():
command = [
"ssh", f"user@{MONGO_HOST}",
f"mongotop --host localhost --port {MONGO_PORT} --username {MONGO_USER} --password {MONGO_PASS} --authenticationDatabase {AUTH_DB} --interval {INTERVAL}"
]
try:
# Run mongotop for a single interval and capture output
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate(timeout=INTERVAL + 5) # Add buffer
if process.returncode != 0:
print(f"Error running mongotop: {stderr.decode()}")
return None
output = stdout.decode()
# Parse the output, skipping header lines
lines = output.strip().split('\n')
data = {}
# Skip the first two lines which are headers/info
for line in lines[2:]:
parts = re.split(r'\s+', line)
if len(parts) >= 4:
ns = parts[0]
try:
read_time = float(parts[1])
write_time = float(parts[2])
total_time = float(parts[3])
data[ns] = {"read": read_time, "write": write_time, "total": total_time}
except ValueError:
continue # Skip malformed lines
return data
except FileNotFoundError:
print("Error: ssh command not found. Is it in your PATH?")
return None
except subprocess.TimeoutExpired:
print(f"Error: mongotop command timed out after {INTERVAL + 5} seconds.")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None
if __name__ == "__main__":
print(f"Monitoring MongoDB on {MONGO_HOST}:{MONGO_PORT} every {INTERVAL} seconds...")
while True:
stats = get_mongotop_data()
if stats:
print(f"--- {time.strftime('%Y-%m-%d %H:%M:%S')} ---")
for ns, times in stats.items():
print(f" {ns}: Read={times['read']:.2f}ms, Write={times['write']:.2f}ms, Total={times['total']:.2f}ms")
else:
print("Failed to retrieve mongotop data.")
time.sleep(INTERVAL)
This script can be adapted to push metrics to a time-series database or trigger alerts based on thresholds (e.g., if write time for any collection exceeds 50ms for more than a minute).
Shopify App Integration: API Health and Performance Monitoring
Your Shopify app, likely running on DigitalOcean droplets (e.g., using PHP/Laravel, Node.js/Express, or Python/Django), needs its own layer of monitoring. This goes beyond just ensuring the web server is up.
API Endpoint Latency and Error Rate Tracking
The Shopify API is the lifeline of your app. Monitoring its responsiveness and error rates is paramount. You can achieve this by instrumenting your application code.
For a PHP/Laravel application, you can use middleware to wrap API calls to Shopify. Here’s a conceptual example using Guzzle:
<?php
namespace App\Http\Middleware;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
use Carbon\Carbon;
class ShopifyApiMonitor
{
protected $client;
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handle(Request $request, Closure $next): Response
{
$startTime = microtime(true);
$response = $next($request);
$endTime = microtime(true);
$duration = ($endTime - $startTime) * 1000; // Duration in milliseconds
// Example: Monitoring a call to fetch products
if ($request->route()->getName() === 'shopify.products.index') {
$this->logApiCall(
'GET',
config('services.shopify.api_url') . '/admin/api/2023-10/products.json', // Example endpoint
$duration,
$response->status()
);
}
// Add more checks for other Shopify API interactions
return $response;
}
protected function logApiCall(string $method, string $url, float $durationMs, int $statusCode): void
{
$logData = [
'timestamp' => Carbon::now()->toIso8601String(),
'method' => $method,
'url' => $url,
'duration_ms' => round($durationMs, 2),
'status_code' => $statusCode,
'is_error' => $statusCode >= 400,
];
if ($statusCode >= 400) {
Log::channel('shopify_api_errors')->error('Shopify API Error', $logData);
} else {
Log::channel('shopify_api_requests')->info('Shopify API Request', $logData);
}
// Optionally, push metrics to a monitoring system like Prometheus via an exporter
// Example: $this->pushToPrometheus('shopify_api_request_duration_ms', $durationMs, ['method' => $method, 'url' => $url, 'status_code' => $statusCode]);
// Example: $this->pushToPrometheus('shopify_api_request_count', 1, ['method' => $method, 'url' => $url, 'status_code' => $statusCode, 'error' => ($statusCode >= 400 ? 'true' : 'false')]);
}
// Placeholder for pushing metrics to Prometheus (requires a Prometheus client library)
// protected function pushToPrometheus(string $metricName, float $value, array $labels): void { ... }
}
?>
In this example:
- We use Laravel’s middleware to intercept requests.
- We record the start time before the request is processed by the application.
- We record the end time after the response is generated.
- The duration is calculated and logged.
- We differentiate between successful requests and errors (status codes 4xx and 5xx).
- Logs are directed to specific channels (
shopify_api_errors,shopify_api_requests) for easier parsing and alerting. - Placeholders are included for pushing metrics to a system like Prometheus.
You’ll need to configure logging channels in config/logging.php and potentially set up a Prometheus exporter (e.g., using prom-client for Node.js or a custom PHP exporter) to scrape these metrics.
Application Performance Monitoring (APM) Tools
For more comprehensive insights, consider integrating Application Performance Monitoring (APM) tools. Services like Datadog, New Relic, or even open-source options like Jaeger (for distributed tracing) can provide deep visibility into your application’s performance, including:
- Transaction tracing: See the full lifecycle of a request, from the web server through your application code, database calls, and external API interactions.
- Database query analysis: Identify slow queries directly within your application context.
- Error tracking: Aggregate and analyze application errors.
- Dependency mapping: Visualize how different services and components interact.
Most APM tools offer agents or libraries for various languages (PHP, Node.js, Python) that can be easily installed and configured. For instance, the Datadog PHP agent can automatically instrument your Laravel application.
DigitalOcean Infrastructure Monitoring & Alerting
While we’ve focused on MongoDB and application-level monitoring, robust infrastructure monitoring on DigitalOcean is the foundation. This involves leveraging DigitalOcean’s built-in monitoring and potentially augmenting it with external tools.
Leveraging DigitalOcean’s Built-in Monitoring
DigitalOcean provides basic monitoring for Droplets, Managed Databases, and Load Balancers directly within the control panel. Key metrics include:
- Droplets: CPU Utilization, Memory Usage, Disk I/O, Network Traffic.
- Managed Databases (MongoDB): Similar metrics to Droplets, plus database-specific metrics like connections, operations, and storage usage.
- Load Balancers: Request Rate, Droplet Health, Bandwidth.
These metrics are essential for understanding the overall health of your infrastructure. However, they are often reactive. To make them proactive, you need to set up alerts.
Configuring Alerts for Critical Thresholds
DigitalOcean allows you to configure alerts based on these metrics. It’s crucial to set meaningful thresholds that indicate potential problems before they cause an outage.
Example alert configurations:
- Droplet CPU Utilization: Alert if CPU usage exceeds 85% for more than 15 minutes. This could indicate an overloaded application or a runaway process.
- Droplet Memory Usage: Alert if memory usage exceeds 90% for more than 10 minutes. This can lead to excessive swapping and severe performance degradation.
- Droplet Disk I/O Wait: Alert if I/O wait time is consistently high (e.g., > 20%). This points to disk bottlenecks, often impacting database performance.
- Droplet Network In/Out: Alert on sudden, unexplained spikes in network traffic, which could indicate a DDoS attack or a misbehaving service.
- Managed MongoDB Connections: Alert if the number of active connections approaches the configured limit (e.g., 1000 connections).
- Managed MongoDB Operations: Monitor read/write operations per second. While
mongostatprovides more detail, a high-level alert here can catch gross anomalies. - Load Balancer Health: Alert if one or more backend Droplets are marked as unhealthy for more than 5 minutes.
Configure these alerts in the DigitalOcean control panel under the “Alerts” section. Ensure your alert notifications are routed to a reliable channel (e.g., Slack, PagerDuty, email) that your operations team actively monitors.
Augmenting with External Monitoring Tools (Prometheus/Grafana)
For a more sophisticated and unified monitoring experience, consider deploying Prometheus and Grafana. This allows you to:
- Aggregate metrics from DigitalOcean (via its API or exporters), MongoDB (using
node_exporterwith MongoDB plugins or custom exporters), and your Shopify application (via application metrics endpoints). - Create rich, customizable dashboards in Grafana to visualize all your key metrics in one place.
- Set up advanced alerting rules in Prometheus Alertmanager.
A common setup involves:
- A Prometheus server running on a dedicated Droplet.
- Grafana server running alongside Prometheus.
node_exporteron each Droplet to expose system-level metrics.mongodb_exporter(or similar) to scrape MongoDB metrics.- Your application exposing metrics via an HTTP endpoint (e.g., using
prom-client). - Prometheus configured to scrape these targets.
- Alertmanager configured for sophisticated alert routing and de-duplication.
This provides a powerful, centralized view of your entire stack, enabling proactive identification and resolution of issues before they impact your Shopify app’s users.