• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Server Monitoring Best Practices: Keeping Your Magento 2 App and MongoDB Clusters Alive on Google Cloud

Server Monitoring Best Practices: Keeping Your Magento 2 App and MongoDB Clusters Alive on Google Cloud

Proactive MongoDB Cluster Health Checks with `mongostat` and `mongotop`

Maintaining the health of a distributed MongoDB cluster, especially one powering a critical Magento 2 instance on Google Cloud, demands more than just reactive alerts. Proactive, granular monitoring of key performance indicators (KPIs) is essential. We’ll leverage the built-in MongoDB tools, mongostat and mongotop, to gain deep insights into cluster behavior, integrating their output into a robust monitoring pipeline.

mongostat provides a real-time snapshot of database operations, including network traffic, query performance, and resource utilization. mongotop, on the other hand, focuses on read and write activity per collection, helping to identify hot spots or inefficient queries.

Automating `mongostat` Data Collection and Alerting

To effectively monitor a MongoDB replica set or sharded cluster, we need to collect data from each member periodically and analyze it for anomalies. A simple yet powerful approach is to use a shell script that runs mongostat with specific intervals and outputs, then parses this data for critical metrics. We’ll then forward this data to a time-series database like Prometheus for long-term storage and alerting.

Here’s a Python script designed to run mongostat on a specified MongoDB host, parse its output, and output metrics in a Prometheus-compatible format. This script should be deployed on a dedicated monitoring instance or on each MongoDB node itself (though a separate instance is generally preferred for isolation).

First, ensure you have the MongoDB database tools installed on the machine running this script. The script connects to the MongoDB instance using its connection string.

Python Script for `mongostat` Monitoring

This script will execute mongostat, capture its output, and extract key metrics. It’s designed to be run via cron or a systemd timer.

Save this as monitor_mongo_stat.py:

import subprocess
import json
import sys
import re
from datetime import datetime

# --- Configuration ---
MONGO_HOST = "mongodb://your_mongo_user:your_mongo_password@your_mongo_host:27017/admin" # Replace with your actual connection string
INTERVAL_SECONDS = 10 # How often to run mongostat
OUTPUT_FORMAT = "json" # Use JSON output for easier parsing

# --- Metrics to Extract ---
# These are common metrics. Adjust based on your specific needs.
METRICS_TO_EXTRACT = [
    "insert", "query", "update", "delete", "getmore", "command", # Operations
    "flushes", "mapped", "anon", "bits", "res", "locked", "idx%wit", "idx%total", # Memory & Disk
    "qrw", "qw", "arw", "aw", # Queue/Lock metrics (may vary by version)
    "netIn", "netOut", "conn", "time", # Network & Connections
    "dirty", "used", "free", "swap", "tempAvg" # Disk/Cache (if available)
]

def run_mongostat(host, interval, output_format):
    """Runs mongostat and returns its output."""
    command = [
        "mongostat",
        f"--host={host}",
        f"--interval={interval}",
        f"--format={output_format}",
        "--discover", # Important for replica sets/sharded clusters
        "--nojournal" # Exclude journal metrics if not needed
    ]
    try:
        # Use text=True for Python 3.7+ to get string output directly
        result = subprocess.run(command, capture_output=True, text=True, check=True, encoding='utf-8')
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"Error running mongostat: {e}", file=sys.stderr)
        print(f"Stderr: {e.stderr}", file=sys.stderr)
        return None
    except FileNotFoundError:
        print("Error: mongostat command not found. Is MongoDB installed and in your PATH?", file=sys.stderr)
        sys.exit(1)

def parse_mongostat_json(output):
    """Parses mongostat JSON output."""
    if not output:
        return []

    # mongostat JSON output can be a bit tricky. It often prints multiple JSON objects
    # separated by newlines, or a single JSON array. We'll try to handle both.
    data_points = []
    lines = output.strip().split('\n')
    for line in lines:
        try:
            # Attempt to parse as a single JSON object per line
            data = json.loads(line)
            if isinstance(data, list): # If it's a list of objects
                data_points.extend(data)
            else: # Assume it's a single object
                data_points.append(data)
        except json.JSONDecodeError:
            # If it's not valid JSON, try to find JSON within the line (less common for --format=json)
            # This part might need adjustment based on exact mongostat output variations.
            # For --format=json, it's usually clean JSON.
            pass
    return data_points

def format_for_prometheus(metric_name, value, labels=None):
    """Formats a metric for Prometheus exposition."""
    if labels:
        label_str = ",".join([f'{k}="{v}"' for k, v in labels.items()])
        return f'# HELP {metric_name} {metric_name}\n# TYPE {metric_name} gauge\n{metric_name}{{{label_str}}} {value}\n'
    else:
        return f'# HELP {metric_name} {metric_name}\n# TYPE {metric_name} gauge\n{metric_name} {value}\n'

def main():
    """Main execution function."""
    raw_output = run_mongostat(MONGO_HOST, INTERVAL_SECONDS, OUTPUT_FORMAT)
    if not raw_output:
        return

    parsed_data = parse_mongostat_json(raw_output)

    if not parsed_data:
        print("No data parsed from mongostat output.", file=sys.stderr)
        return

    # Assuming the first entry in parsed_data is representative or we want to process all
    # For replica sets, mongostat might return multiple entries if --discover is used.
    # We'll iterate through each member's stats.
    for entry in parsed_data:
        if not entry: continue

        # Extract common labels
        labels = {
            "host": entry.get("host", "unknown"),
            "opid": str(entry.get("opid", "unknown")), # Operation ID, useful for sharding
            "state": entry.get("state", "unknown"), # e.g., PRIMARY, SECONDARY
            "set": entry.get("set", "unknown") # Replica set name
        }

        # Filter out non-numeric or irrelevant fields for Prometheus
        for key, value in entry.items():
            if key in METRICS_TO_EXTRACT and isinstance(value, (int, float)):
                # Special handling for percentage metrics if they appear as strings
                if isinstance(value, str) and value.endswith('%'):
                    try:
                        numeric_value = float(value.strip('%'))
                        print(format_for_prometheus(f"mongodb_{key}", numeric_value, labels))
                    except ValueError:
                        pass # Ignore if conversion fails
                else:
                    print(format_for_prometheus(f"mongodb_{key}", value, labels))
            elif key == "qrw" and isinstance(value, str): # Handle potential string representations of counts
                 try:
                     numeric_value = int(value)
                     print(format_for_prometheus(f"mongodb_qrw", numeric_value, labels))
                 except ValueError:
                     pass

        # Add a timestamp metric
        timestamp = datetime.now().timestamp()
        print(format_for_prometheus("mongodb_scrape_timestamp", timestamp, labels))

if __name__ == "__main__":
    main()

Integrating with Prometheus and Grafana

The Python script above is designed to output metrics in a format that can be scraped by Prometheus. To achieve this, we’ll set up a Prometheus server and configure it to scrape the output of our Python script. A common pattern is to run the Python script via a systemd service that exposes its output via a simple HTTP server (e.g., using Flask or a basic Python HTTP server), which Prometheus can then scrape.

Prometheus Exporter Setup (using Flask)

Install Flask and the prometheus_client library:

pip install Flask prometheus_client pymongo

Modify the Python script to act as a Prometheus exporter. Save this as mongo_exporter.py:

import subprocess
import json
import sys
import re
from datetime import datetime
from flask import Flask, Response
from prometheus_client import generate_latest, CollectorRegistry, Gauge
from pymongo import MongoClient

# --- Configuration ---
MONGO_HOST = "mongodb://your_mongo_user:your_mongo_password@your_mongo_host:27017/admin" # Replace with your actual connection string
INTERVAL_SECONDS = 10 # How often to run mongostat
OUTPUT_FORMAT = "json" # Use JSON output for easier parsing
EXPORTER_PORT = 9101 # Port for the exporter

# --- Prometheus Metrics ---
REGISTRY = CollectorRegistry()

# Use a dictionary to store gauges dynamically based on metric names
GAUGES = {}

def get_or_create_gauge(metric_name, documentation, labels=None):
    """Helper to get or create a Prometheus Gauge."""
    # Labels need to be sorted for consistent key generation
    if labels:
        sorted_labels = sorted(labels.keys())
        label_key = "_".join(sorted_labels)
    else:
        label_key = "no_labels"

    gauge_key = f"{metric_name}_{label_key}"

    if gauge_key not in GAUGES:
        if labels:
            GAUGES[gauge_key] = Gauge(metric_name, documentation, labelnames=sorted_labels, registry=REGISTRY)
        else:
            GAUGES[gauge_key] = Gauge(metric_name, documentation, registry=REGISTRY)
    return GAUGES[gauge_key]

def run_mongostat(host, interval, output_format):
    """Runs mongostat and returns its output."""
    command = [
        "mongostat",
        f"--host={host}",
        f"--interval={interval}",
        f"--format={output_format}",
        "--discover",
        "--nojournal"
    ]
    try:
        result = subprocess.run(command, capture_output=True, text=True, check=True, encoding='utf-8')
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"Error running mongostat: {e}", file=sys.stderr)
        print(f"Stderr: {e.stderr}", file=sys.stderr)
        return None
    except FileNotFoundError:
        print("Error: mongostat command not found. Is MongoDB installed and in your PATH?", file=sys.stderr)
        sys.exit(1)

def parse_mongostat_json(output):
    """Parses mongostat JSON output."""
    if not output:
        return []
    data_points = []
    lines = output.strip().split('\n')
    for line in lines:
        try:
            data = json.loads(line)
            if isinstance(data, list):
                data_points.extend(data)
            else:
                data_points.append(data)
        except json.JSONDecodeError:
            pass
    return data_points

def collect_metrics():
    """Collects metrics from mongostat and updates Prometheus gauges."""
    raw_output = run_mongostat(MONGO_HOST, INTERVAL_SECONDS, OUTPUT_FORMAT)
    if not raw_output:
        return

    parsed_data = parse_mongostat_json(raw_output)

    if not parsed_data:
        print("No data parsed from mongostat output.", file=sys.stderr)
        return

    # Clear previous metrics to avoid stale data if a host disappears
    # This is a simplified approach. For dynamic environments, more robust handling is needed.
    # For now, we'll rely on Prometheus's scrape timeout and relabeling.

    for entry in parsed_data:
        if not entry: continue

        labels = {
            "host": entry.get("host", "unknown"),
            "opid": str(entry.get("opid", "unknown")),
            "state": entry.get("state", "unknown"),
            "set": entry.get("set", "unknown")
        }
        # Remove 'unknown' labels if they are not meaningful
        labels = {k: v for k, v in labels.items() if v != "unknown"}

        for key, value in entry.items():
            if isinstance(value, (int, float)):
                metric_name = f"mongodb_{key}"
                gauge = get_or_create_gauge(metric_name, f"MongoDB {key} metric", labels.keys())
                gauge.labels(**labels).set(value)
            elif isinstance(value, str) and value.endswith('%'):
                try:
                    numeric_value = float(value.strip('%'))
                    metric_name = f"mongodb_{key}"
                    gauge = get_or_create_gauge(metric_name, f"MongoDB {key} metric", labels.keys())
                    gauge.labels(**labels).set(numeric_value)
                except ValueError:
                    pass # Ignore conversion errors

    # Add a scrape timestamp
    timestamp_gauge = get_or_create_gauge("mongodb_scrape_timestamp", "Timestamp of the last successful scrape")
    timestamp_gauge.set(datetime.now().timestamp())

app = Flask(__name__)

@app.route('/metrics')
def metrics():
    collect_metrics() # Collect metrics before serving
    return Response(generate_latest(REGISTRY), mimetype='text/plain')

if __name__ == '__main__':
    print(f"Starting MongoDB exporter on port {EXPORTER_PORT}...")
    app.run(host='0.0.0.0', port=EXPORTER_PORT)

Run this exporter script on your monitoring instance:

python mongo_exporter.py

Now, configure your Prometheus server (prometheus.yml) to scrape this exporter:

scrape_configs:
  - job_name: 'mongodb'
    static_configs:
      - targets: ['your_exporter_ip:9101'] # Replace with the IP of your exporter
    # You can add relabeling rules here if needed, e.g., to add instance labels
    # relabel_configs:
    #   - source_labels: [__address__]
    #     target_label: instance
    #     regex: '([^:]+):.*'
    #     replacement: '$1'

Restart Prometheus for the changes to take effect. You should now see MongoDB metrics appearing in Prometheus. You can then build Grafana dashboards to visualize these metrics.

Monitoring Magento 2 Application Performance with Blackfire.io

While MongoDB health is crucial, the performance of the Magento 2 application itself is paramount for user experience and conversion rates. Blackfire.io is an excellent tool for profiling PHP applications, providing deep insights into code execution, bottlenecks, and memory usage.

Setting up Blackfire.io for Magento 2

1. Install Blackfire Agent: The agent runs on your server and collects profile data. Follow the official Blackfire documentation for installation specific to your OS (e.g., Debian/Ubuntu, CentOS/RHEL).

# Example for Debian/Ubuntu
wget https://blackfire.io/api/agent/linux/amd64/stable -O blackfire-agent.deb
sudo dpkg -i blackfire-agent.deb
sudo systemctl enable blackfire-agent
sudo systemctl start blackfire-agent

2. Install Blackfire PHP Extension: This extension integrates with your PHP runtime to generate profiles.

# Ensure you have the correct PHP version installed
sudo apt-get install php-blackfire # Or php8.1-blackfire, etc.

3. Configure Blackfire Credentials: Obtain your Blackfire credentials (an agent token and an environment ID) from your Blackfire.io account dashboard. Place them in the /etc/blackfire/agent configuration file or set them as environment variables.

# /etc/blackfire/agent
[blackfire]
  ; The agent token obtained from your Blackfire account.
  token="your_agent_token"

  ; The environment ID for this agent.
  ; If not set, the agent will try to detect it.
  ; environment-id="your_environment_id"

Restart the Blackfire agent after configuration:

sudo systemctl restart blackfire-agent

Automated Profiling and Alerting with Blackfire.io

Blackfire.io allows you to set up “Alerts” based on profile metrics. This is crucial for detecting performance regressions automatically.

Setting up Blackfire Alerts

1. Navigate to Alerts: In your Blackfire.io dashboard, go to the “Alerts” section.

2. Create a New Alert: Define the conditions under which an alert should be triggered. Common metrics to monitor for Magento 2 include:

  • HTTP Request Time: Set a threshold for average or p95/p99 request times.
  • PHP Execution Time: Monitor the time spent in PHP code.
  • Memory Usage: Track peak memory consumption.
  • Number of SQL Queries: Identify excessive database calls.
  • Number of Cache Misses: Detect issues with Magento’s caching mechanisms.

3. Configure Notification Channels: Integrate Blackfire alerts with your existing alerting system, such as Slack, PagerDuty, or email. This ensures that your DevOps team is notified immediately when performance degrades.

Example Alert Configuration (Conceptual):

You might set an alert for the page.response.time metric. If the average response time for any page exceeds 2 seconds for more than 5 minutes, trigger a PagerDuty incident.

Google Cloud Monitoring for Infrastructure Health

Beyond application-specific monitoring, Google Cloud’s native monitoring tools are essential for tracking the health of your underlying infrastructure: Compute Engine instances, Kubernetes Engine clusters, Load Balancers, and Cloud SQL instances.

Key Google Cloud Monitoring Metrics to Track

For your Magento 2 and MongoDB setup, focus on these metrics:

  • Compute Engine (VMs):
    • compute.googleapis.com/instance/cpu/utilization: CPU usage. High utilization can indicate bottlenecks.
    • compute.googleapis.com/instance/memory/usage: Memory usage. Crucial for preventing OOM errors.
    • compute.googleapis.com/instance/disk/read_ops_count and write_ops_count: Disk I/O operations.
    • compute.googleapis.com/instance/network/received_bytes_count and transmitted_bytes_count: Network traffic.
  • Cloud SQL (for any auxiliary databases or if not using dedicated MongoDB VMs):
    • cloudsql.googleapis.com/database/cpu/utilization
    • cloudsql.googleapis.com/database/memory/utilization
    • cloudsql.googleapis.com/database/disk/utilization
    • cloudsql.googleapis.com/database/network/received_bytes_count and transmitted_bytes_count
    • cloudsql.googleapis.com/database/replication/lag (if applicable)
  • Load Balancing:
    • loadbalancing.googleapis.com/https/request_count
    • loadbalancing.googleapis.com/https/backend_response_time: End-to-end latency.
    • loadbalancing.googleapis.com/https/backend_connection_close_count: Indicates backend health.
    • loadbalancing.googleapis.com/https/backend_health_check_failed: Critical for identifying unhealthy backends.
  • Kubernetes Engine (GKE):
    • Pod CPU/Memory utilization.
    • Node CPU/Memory utilization.
    • Network I/O per pod/node.
    • container.googleapis.com/container/cpu/request_cores, container.googleapis.com/container/memory/request_bytes: Resource requests vs. limits.

Setting up Google Cloud Monitoring Alerts

Google Cloud’s Monitoring (formerly Stackdriver) allows you to create custom dashboards and set up alerting policies based on these metrics.

1. Navigate to Monitoring: In the Google Cloud Console, go to “Monitoring”.

2. Create Alerting Policies:

# Example: Alerting on high CPU utilization for Magento VMs
# In Cloud Console: Monitoring -> Alerting -> Create Policy

# Condition:
# Metric: Compute Engine VM Instance -> CPU utilization
# Filter: Instance name contains 'magento'
# Aggregator: mean
# For: 5 minutes
# Threshold: > 85%

# Notification:
# Choose your notification channel (e.g., Email, Slack, PagerDuty)

3. Configure Notification Channels: Set up integrations with your incident management tools. This ensures that alerts are routed to the correct teams.

Correlating Metrics for Root Cause Analysis

The true power of a comprehensive monitoring strategy lies in the ability to correlate metrics from different sources. When an alert fires, you need to quickly understand the root cause.

Scenario: Magento 2 frontend is slow.

  • Initial Alert: Blackfire.io triggers an alert for high HTTP request time on the homepage.
  • Investigate Blackfire: Drill down into the Blackfire profile for the homepage. You might see a specific controller action or plugin taking too long.
  • Check MongoDB: Simultaneously, check your Prometheus dashboard for MongoDB metrics. Are there high mongodb_qrw (queue read/write) or mongodb_lock_time values? Is the mongodb_query rate unusually high? This could indicate slow MongoDB queries impacting Magento.
  • Check GCP Infrastructure: Look at Google Cloud Monitoring. Are the Magento Compute Engine instances experiencing high CPU or memory usage? Is the network traffic spiking?
  • Correlate Timestamps: Align the timestamps of the Blackfire alert, MongoDB spikes, and GCP metric anomalies. This correlation is key to pinpointing whether the issue originated in the application code, the database, or the underlying infrastructure.

By integrating Blackfire.io for application performance, Prometheus/Grafana for MongoDB, and Google Cloud Monitoring for infrastructure, you create a layered defense system. This allows for rapid detection, diagnosis, and resolution of issues, ensuring your Magento 2 application remains performant and available.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • How to securely integrate Twilio SMS Gateway endpoints into WordPress custom plugins using Block Patterns API
  • Implementing automated compliance reporting for custom internal server status logs ledgers using native TCP printing streams
  • How to securely integrate Google Analytics v4 REST endpoints into WordPress custom plugins using Metadata API (add_post_meta)
  • Implementing automated compliance reporting for custom affiliate click tracking logs ledgers using dompdf library
  • How to securely integrate Stripe Payment webhook endpoints into WordPress custom plugins using WordPress Database Class ($wpdb)

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (586)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (810)
  • PHP (5)
  • PHP Development (23)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (548)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (23)
  • WordPress Theme Development (357)

Recent Posts

  • How to securely integrate Twilio SMS Gateway endpoints into WordPress custom plugins using Block Patterns API
  • Implementing automated compliance reporting for custom internal server status logs ledgers using native TCP printing streams
  • How to securely integrate Google Analytics v4 REST endpoints into WordPress custom plugins using Metadata API (add_post_meta)

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (810)
  • Debugging & Troubleshooting (586)
  • Security & Compliance (548)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala