Engineering

Monitoring and Alerting with Prometheus and Grafana: A Practical Setup

Learn how to set up Prometheus and Grafana for effective monitoring and alerting. Practical examples and insights for building a robust monitoring stack. Ensure system reliability and performance!

· Founder & Engineer · · 9 min read
Grafana dashboard displaying Prometheus metrics for monitoring system performance and alerting.

Effective monitoring and alerting are crucial for maintaining the reliability and performance of any complex system. At MisuJob, where we process 1M+ job listings and rely on AI-powered job matching, we’ve built a robust monitoring stack using Prometheus and Grafana. This post details our practical setup, providing insights and code examples you can adapt for your own infrastructure.

The Importance of Monitoring and Alerting

Monitoring and alerting aren’t just about knowing when something breaks; they’re about understanding system behavior, identifying bottlenecks, and proactively preventing issues. A well-configured system can provide invaluable insights into performance trends, resource utilization, and potential security threats. For MisuJob, this means ensuring our job matching algorithms are performing optimally, our API response times are consistently low, and our users have a seamless experience. A slight degradation in performance can impact thousands of users across Europe, so early detection is paramount.

Why Prometheus and Grafana?

We chose Prometheus and Grafana for their powerful capabilities and open-source nature. Prometheus excels at collecting and storing time-series data, while Grafana provides a flexible and visually appealing interface for querying, visualizing, and alerting on that data. Their integration is seamless and they are widely adopted in the industry.

  • Prometheus: A time-series database built for operational monitoring. Its pull-based model, powerful query language (PromQL), and alert management capabilities make it a natural fit for our needs.
  • Grafana: A data visualization tool that supports a wide range of data sources, including Prometheus. It allows us to create dashboards, set up alerts, and collaborate on monitoring strategies.

Setting Up Prometheus

The core of our monitoring system is Prometheus. Its primary job is to collect metrics from our various services and store them efficiently.

Installing and Configuring Prometheus

The first step is to install Prometheus. We typically use Docker for ease of deployment:

docker run -d --name prometheus -p 9090:9090 \
  -v prometheus_data:/prometheus \
  -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:latest

This command starts a Prometheus container, maps port 9090 to your host, and mounts a configuration file. The prometheus.yml file is where you define your monitoring targets. Here’s a simplified example:

global:
  scrape_interval:     15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'api_server'
    static_configs:
      - targets: ['api.misujob.com:8080']

This configuration tells Prometheus to collect metrics from itself and from our API server, which exposes metrics on port 8080. The scrape_interval specifies how often Prometheus collects metrics.

Exposing Metrics from Your Application

To monitor your applications, you need to expose metrics in a format that Prometheus understands. The most common format is the Prometheus exposition format. Most languages have libraries to help with this. For example, in Python:

from prometheus_client import start_http_server, Summary
import random
import time

# Create a metric to track time spent and requests made.
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')

# Decorate function with metric.
@REQUEST_TIME.time()
def process_request(t):
    """A dummy function that takes some time."""
    time.sleep(t)

if __name__ == '__main__':
    # Start up the server to expose the metrics.
    start_http_server(8000)
    # Generate some requests.
    while True:
        process_request(random.random())

This code creates a simple HTTP server that exposes metrics at the /metrics endpoint. Prometheus can then be configured to collect these metrics.

Setting Up Grafana

Grafana is our visualization and alerting platform. It allows us to create dashboards that provide real-time insights into our system’s performance.

Installing and Configuring Grafana

Similarly to Prometheus, we use Docker for Grafana:

docker run -d --name grafana -p 3000:3000 \
  -v grafana_data:/var/lib/grafana \
  grafana/grafana:latest

This command starts a Grafana container and maps port 3000 to your host. Once Grafana is running, you can access it in your browser at http://localhost:3000. The default credentials are admin/admin.

Connecting Grafana to Prometheus

The first step after logging into Grafana is to add Prometheus as a data source. Go to “Configuration” -> “Data Sources” and select “Prometheus”. Enter the Prometheus URL (e.g., http://prometheus:9090 if they are on the same network).

Creating Dashboards

With Prometheus connected, you can start creating dashboards. Dashboards are collections of panels that visualize your metrics. For example, we have a dashboard that displays the CPU and memory usage of our API servers.

To create a panel, click “Create” -> “Dashboard” -> “Add new panel”. Select Prometheus as the data source and use PromQL to query your metrics. For example, to display the CPU usage of our API server, you might use the following query:

rate(process_cpu_seconds_total{job="api_server"}[5m])

This query calculates the rate of CPU usage over the last 5 minutes for the api_server job. You can customize the panel’s title, axes, and visualization type to best represent your data. We find that using annotations to mark deployments and incidents on the graphs provides valuable context.

Implementing Alerting

Alerting is a critical component of our monitoring system. Prometheus and Grafana together allow us to set up alerts that notify us when certain metrics cross predefined thresholds.

Configuring Prometheus Alerts

Prometheus uses alert rules defined in a configuration file. These rules specify the conditions under which an alert should be triggered. Here’s an example:

groups:
  - name: example
    rules:
      - alert: HighApiLatency
        expr: sum(rate(http_request_duration_seconds_sum{job="api_server"}[5m])) / sum(rate(http_request_duration_seconds_count{job="api_server"}[5m])) > 0.5
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "High API latency"
          description: "API latency is above 0.5 seconds for more than 1 minute."

This rule triggers an alert named HighApiLatency if the average API request latency exceeds 0.5 seconds for more than 1 minute. The expr field contains the PromQL expression that defines the alert condition. The for field specifies how long the condition must be true before the alert is triggered. The labels field allows you to add labels to the alert, such as severity. The annotations field provides additional information about the alert, such as a summary and description.

Integrating Alertmanager

Prometheus doesn’t send alerts directly. It uses Alertmanager to manage and route alerts. Alertmanager handles deduplication, grouping, and routing of alerts to various receivers, such as email, Slack, or PagerDuty.

To configure Alertmanager, you need to create a configuration file. Here’s a simplified example:

route:
  receiver: 'slack-notifications'
  repeat_interval: 5m

receivers:
  - name: 'slack-notifications'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
        channel: '#alerts'
        send_resolved: true

This configuration routes all alerts to a Slack channel. The repeat_interval specifies how often Alertmanager should resend the alert if it’s still active.

Grafana Alerting

Grafana also provides its own alerting capabilities. You can set up alerts directly within Grafana dashboards. While Grafana’s alerting is convenient for simple cases, we generally prefer Prometheus and Alertmanager for more complex scenarios due to their flexibility and scalability.

Practical Examples and Real-World Data

To illustrate the value of our monitoring setup, let’s consider a few practical examples.

Example 1: Detecting API Performance Degradation

Suppose we notice a sudden increase in API latency in our Grafana dashboard. By drilling down into the metrics, we can identify the specific API endpoints that are causing the slowdown. We might discover that a recent code change has introduced a performance bottleneck in one of our job matching algorithms.

Armed with this information, we can quickly revert the code change or implement a fix. Without monitoring and alerting, this performance degradation might have gone unnoticed for hours or even days, impacting thousands of users.

Example 2: Preventing Resource Exhaustion

Our monitoring system also helps us prevent resource exhaustion. For example, we monitor the CPU and memory usage of our database servers. If we see that the CPU usage is consistently high, we can proactively scale up the servers or optimize our database queries. This prevents the database from becoming overloaded and ensures that our API remains responsive.

Example 3: Monitoring Job Data Ingestion

We monitor the rate at which MisuJob aggregates data from multiple sources. A sudden drop in this rate could indicate a problem with our data pipeline. By investigating the issue promptly, we can ensure that our job matching algorithms have access to the latest data.

Salary Data Insights

Our systems ingest and process salary data which we can expose insights about. Here’s a table showing average Data Scientist salaries across several European countries:

CountryAverage Salary (€)
Germany75,000
UK68,000
France62,000
Netherlands70,000
Spain50,000

These are just a few examples of how our monitoring and alerting system helps us maintain the reliability and performance of MisuJob.

Optimizing Prometheus Queries

Efficient PromQL queries are essential for maintaining a responsive monitoring system. Inefficient queries can overload Prometheus and impact its performance. Here are a few tips for optimizing your PromQL queries:

  • Use aggregations: Aggregate data as early as possible in your query. This reduces the amount of data that needs to be processed. For example, instead of querying http_requests_total for each instance and then summing the results, use the sum() aggregator directly in your query: sum(http_requests_total).
  • Use rate() and irate(): The rate() and irate() functions are used to calculate the rate of change of a counter. irate() is generally faster and more accurate for rapidly changing counters. However, it can be susceptible to noise. rate() is more stable but can be slower. Choose the function that best suits your needs.
  • Limit the time range: Avoid querying large time ranges unless absolutely necessary. The larger the time range, the more data Prometheus needs to process.
  • Use labels effectively: Use labels to filter and group your data. This can significantly improve the performance of your queries.

For example, let’s say you have this poorly written query:

sum(http_requests_total{job="my_job", instance=~".*"})

This query is inefficient because it uses a regular expression to match instances. A better approach would be to use labels directly:

sum(http_requests_total{job="my_job"})

If you need to filter by specific instances, add those as explicit labels:

sum(http_requests_total{job="my_job", instance="instance1"}) + sum(http_requests_total{job="my_job", instance="instance2"})

While this is more verbose, it is significantly more performant.

Addressing Common Challenges

Implementing a monitoring and alerting system is not without its challenges. Here are a few common issues we’ve encountered and how we’ve addressed them:

  • Alert fatigue: Too many alerts can desensitize engineers and lead to important issues being missed. To combat alert fatigue, we focus on creating meaningful alerts that are actionable. We also use Alertmanager to deduplicate and group alerts.
  • Noisy metrics: Some metrics can be inherently noisy, causing false positives. To address this, we use techniques such as smoothing and anomaly detection to filter out the noise.
  • Scalability: As our infrastructure grows, our monitoring system needs to scale as well. We use Prometheus’s federation and remote storage capabilities to handle large volumes of data.

Conclusion

Monitoring and alerting are essential for maintaining the reliability and performance of MisuJob. By leveraging Prometheus and Grafana, we’ve built a robust system that provides real-time insights into our infrastructure and allows us to proactively address issues. The specific techniques and configurations we’ve described here provide a solid foundation for building your own monitoring stack.

Key Takeaways

  • Prometheus and Grafana are powerful tools for monitoring and alerting.
  • Effective monitoring requires exposing metrics from your applications.
  • Alertmanager is essential for managing and routing alerts.
  • Optimizing PromQL queries is crucial for maintaining performance.
  • Addressing alert fatigue and noisy metrics is key to a successful monitoring system.
  • Regular review and refinement of alerts are essential.
  • Consider the unique challenges of distributed systems when designing your monitoring strategy.
prometheus grafana monitoring alerting devops infrastructure
Share
P
Pablo Inigo

Founder & Engineer

Building MisuJob - an AI-powered job matching platform processing 1M+ job listings daily.

Engineering updates

Technical deep dives delivered to your inbox.

Find your next role with AI

Upload your CV. Get matched to 50,000+ jobs. Apply to the best fits effortlessly.

Get Started Free

User

Dashboard Profile Subscription