Admin

DevOps

Featured

Boost Kubernetes Monitoring: Prometheus Custom Exporters & Recording Rules

Master Kubernetes monitoring with Prometheus. Learn to create custom exporters & recording rules for powerful insights, custom metrics & advanced alerting.

By Sujay SinghPublished: June 26, 202617 min read2 views✓ Fact Checked
Boost Kubernetes Monitoring: Prometheus Custom Exporters & Recording Rules
Boost Kubernetes Monitoring: Prometheus Custom Exporters & Recording Rules

Overview

As enterprises increasingly adopt Kubernetes for orchestrating containerized applications, the complexity of monitoring these dynamic environments grows exponentially. While Kubernetes provides a robust API for managing resources, understanding the health and performance of the applications *within* the clusters requires a specialized approach. Prometheus, an open-source monitoring system, has become the de facto standard for Kubernetes monitoring due to its powerful multi-dimensional data model, flexible query language (PromQL), and efficient pull-based architecture. However, Prometheus alone isn't a silver bullet. To achieve comprehensive observability, especially for custom applications or specific business logic, we often need to extend its capabilities. This is where **custom exporters** and **recording rules** come into play. **Custom exporters** bridge the gap between your applications and Prometheus. They are small services that expose application-specific metrics in a format Prometheus can scrape. This allows you to monitor internal application states, business-level KPIs, or integrate with third-party systems that don't natively expose Prometheus metrics. Imagine tracking the number of failed login attempts for your authentication service or the processing time for a specific microservice's critical path – custom exporters make this possible. **Recording rules**, on the other hand, are an optimization strategy within Prometheus. They allow you to pre-compute frequently needed or computationally expensive PromQL queries and store their results as new time series. This significantly improves query performance for dashboards and alerting, especially when dealing with large datasets or complex aggregations. Instead of recalculating a `sum by (namespace, job) (rate(http_requests_total[5m]))` every time you load a dashboard, a recording rule can do it once every minute, making your Prometheus UI and Grafana dashboards snappier and more responsive. Together, custom exporters and recording rules empower DevOps teams to achieve granular visibility into their Kubernetes applications and optimize their monitoring infrastructure for scale and efficiency. This article will walk you through the practical steps of developing and deploying custom exporters and implementing recording rules within a Kubernetes environment.

Prerequisites

Before we dive into the implementation, ensure you have the following ready: * **A Kubernetes Cluster**: Any Kubernetes cluster (e.g., Minikube, Kind, GKE, EKS, AKS) will work. For this guide, we'll assume a running cluster. * **`kubectl`**: The Kubernetes command-line tool, configured to connect to your cluster. * **Helm**: The Kubernetes package manager, version 3.x. We'll use Helm to install the Prometheus Operator. * **Prometheus Operator**: This powerful tool simplifies the deployment and management of Prometheus, Alertmanager, and related components in Kubernetes. We'll assume it's installed and running. If not, you can install it via Helm:

    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    helm repo update
    kubectl create namespace prometheus
    helm install prometheus prometheus-community/kube-prometheus-stack --namespace prometheus
    
This will deploy Prometheus, Grafana, Alertmanager, and the necessary CRDs (Custom Resource Definitions) like `ServiceMonitor` and `PrometheusRule`. * **Docker**: For building and pushing your custom exporter's container image. * **Python 3.x**: Basic familiarity with Python will be helpful for the custom exporter example, along with `pip` for package management. * **Git**: For version control (optional, but highly recommended for all configurations).

Step-by-step Implementation

This section will guide you through the process of creating a custom exporter, deploying it to Kubernetes, and then defining Prometheus recording rules.

Understanding Prometheus Exporters

Prometheus uses a "pull" model, meaning it scrapes metrics endpoints from configured targets. An exporter is essentially an HTTP server that exposes metrics in a specific text-based format on an `/metrics` endpoint. These metrics typically follow a structure like this:

# HELP http_requests_total Total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",path="/api/v1/users"} 1234
http_requests_total{method="get",path="/api/v1/status"} 5678

# HELP app_queue_size Current size of the application queue.
# TYPE app_queue_size gauge
app_queue_size 15

# HELP app_request_duration_seconds Histogram of request durations.
# TYPE app_request_duration_seconds histogram
app_request_duration_seconds_bucket{le="0.1"} 100
app_request_duration_seconds_bucket{le="0.5"} 150
app_request_duration_seconds_bucket{le="1.0"} 170
app_request_duration_seconds_bucket{le="+Inf"} 180
app_request_duration_seconds_sum 90.5
app_request_duration_seconds_count 180
Prometheus client libraries are available for various languages (Go, Python, Java, Ruby, Node.js, etc.) to simplify the creation of these exporters. They handle the HTTP server, metric registration, and formatting.

Developing a Custom Exporter (Python Example)

Let's create a simple Python exporter that simulates tracking some application-specific metrics: the total number of processed tasks and the current number of active workers. 1. **Create a project directory**:

    mkdir my-custom-exporter
    cd my-custom-exporter
    
2. **`requirements.txt`**:

    prometheus_client==0.19.0
    
3. **`app.py` (Custom Exporter Logic)**: This Python script will use the `prometheus_client` library to expose two metrics: * `app_processed_tasks_total`: A `Counter` to track the cumulative number of tasks processed. * `app_active_workers`: A `Gauge` to track the current number of active workers. It will also simulate some activity to make the metrics change over time.

    from prometheus_client import start_http_server, Counter, Gauge
    import random
    import time
    import os

    # Define metrics
    PROCESSED_TASKS_TOTAL = Counter(
        'app_processed_tasks_total',
        'Total number of tasks processed by the application.'
    )
    ACTIVE_WORKERS = Gauge(
        'app_active_workers',
        'Current number of active workers in the application.'
    )

    def process_task():
        """Simulates processing a task and increments the counter."""
        time.sleep(random.uniform(0.1, 0.5)) # Simulate work
        PROCESSED_TASKS_TOTAL.inc()
        print(f"Task processed. Total: {PROCESSED_TASKS_TOTAL._value}")

    def update_workers():
        """Simulates changes in active workers."""
        ACTIVE_WORKERS.set(random.randint(1, 10))
        print(f"Active workers updated: {ACTIVE_WORKERS._value}")

    if __name__ == '__main__':
        # Exporter will listen on port 8000
        exporter_port = int(os.environ.get("EXPORTER_PORT", 8000))
        start_http_server(exporter_port)
        print(f"Prometheus exporter listening on port {exporter_port}")

        # Main loop to simulate application activity
        while True:
            process_task()
            if random.random() < 0.3: # Update workers occasionally
                update_workers()
            time.sleep(1) # Wait before next iteration
    
4. **`Dockerfile`**: To containerize our exporter for Kubernetes deployment.

    # Use an official Python runtime as a parent image
    FROM python:3.9-slim-buster

    # Set the working directory in the container
    WORKDIR /app

    # Copy the current directory contents into the container at /app
    COPY requirements.txt .
    COPY app.py .

    # Install any needed packages specified in requirements.txt
    RUN pip install --no-cache-dir -r requirements.txt

    # Make port 8000 available to the world outside this container
    EXPOSE 8000

    # Run app.py when the container launches
    CMD ["python", "app.py"]
    
5. **Build and Push Docker Image**: Replace `your-dockerhub-username` with your actual Docker Hub username or your private registry's path.

    docker build -t your-dockerhub-username/my-custom-exporter:v1.0.0 .
    docker push your-dockerhub-username/my-custom-exporter:v1.0.0
    

Deploying the Custom Exporter to Kubernetes

Now, let's deploy our `my-custom-exporter` to the Kubernetes cluster and configure Prometheus to scrape its metrics. We'll use a `Deployment`, a `Service`, and a `ServiceMonitor` (a Prometheus Operator CRD). 1. **`exporter-deployment.yaml`**: This defines our application deployment.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-custom-exporter
      labels:
        app: my-custom-exporter
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: my-custom-exporter
      template:
        metadata:
          labels:
            app: my-custom-exporter
        spec:
          containers:
          - name: exporter
            image: your-dockerhub-username/my-custom-exporter:v1.0.0 # Replace with your image
            ports:
            - containerPort: 8000
              name: http-metrics
            env:
            - name: EXPORTER_PORT
              value: "8000"
    
2. **`exporter-service.yaml`**: This exposes our exporter within the cluster.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-custom-exporter
      labels:
        app: my-custom-exporter
    spec:
      selector:
        app: my-custom-exporter
      ports:
        - name: http-metrics
          protocol: TCP
          port: 8000
          targetPort: 8000
    
3. **`exporter-servicemonitor.yaml`**: This is crucial for Prometheus Operator. It tells Prometheus *how* to discover and scrape metrics from our service. Note that `namespaceSelector` is important if your exporter is in a different namespace than Prometheus. Here, we assume both are in the `default` namespace for simplicity, but Prometheus is typically in `prometheus` namespace. If Prometheus is in `prometheus` namespace and your app in `default`, you'd set `namespaceSelector.matchNames: ["default"]`.

    apiVersion: monitoring.coreos.com/v1
    kind: ServiceMonitor
    metadata:
      name: my-custom-exporter
      labels:
        app: my-custom-exporter
        release: prometheus # This label links to the Prometheus instance installed by kube-prometheus-stack
    spec:
      selector:
        matchLabels:
          app: my-custom-exporter # Selects the Service with this label
      endpoints:
      - port: http-metrics # Refers to the named port in the Service
        path: /metrics
        interval: 15s
      namespaceSelector:
        matchNames:
          - default # Ensure Prometheus looks for services in the 'default' namespace
    
**Important:** The `release: prometheus` label in the `ServiceMonitor`'s metadata is critical. The `kube-prometheus-stack` Helm chart configures the Prometheus instance to discover `ServiceMonitor` resources only if they have this specific label (or whatever label you configured your Prometheus instance to use via `prometheus.prometheusSpec.serviceMonitorSelector.matchLabels` and `prometheus.prometheusSpec.serviceMonitorNamespaceSelector`). 4. **Deploy to Kubernetes**:

    kubectl apply -f exporter-deployment.yaml
    kubectl apply -f exporter-service.yaml
    kubectl apply -f exporter-servicemonitor.yaml
    
5. **Verify Exporter and Metrics**: * Check if the pod is running:

        kubectl get pods -l app=my-custom-exporter
        
* Forward the service port to your local machine to check the `/metrics` endpoint:

        kubectl port-forward svc/my-custom-exporter 8000:8000
        
Then, open your browser or `curl` `http://localhost:8000/metrics`. You should see the `app_processed_tasks_total` and `app_active_workers` metrics. * Access the Prometheus UI. If you installed with `kube-prometheus-stack`, you can forward its service:

        kubectl port-forward svc/prometheus-kube-prometheus-prometheus 9090:9090 -n prometheus
        
Navigate to `http://localhost:9090` in your browser. Go to "Status" -> "Targets". You should see your `my-custom-exporter` listed and in a "UP" state. You can then query `app_processed_tasks_total` or `app_active_workers` in the Prometheus graph interface.

Understanding Prometheus Recording Rules

Recording rules allow you to define new time series based on existing ones. They are especially useful for: * **Pre-aggregating data**: Combining metrics from multiple sources or instances into a single, summary metric. * **Simplifying complex queries**: Storing the result of a long or frequently used PromQL expression under a simpler name. * **Improving dashboard and alert performance**: Dashboards and alerts can query the pre-computed series instead of executing expensive on-the-fly calculations. A recording rule has two main parts: * `record`: The name of the new metric series. It should follow Prometheus naming conventions (`::`). * `expr`: The PromQL expression whose result will be stored as the new series. For example, if you have `http_requests_total` and want to know the 5-minute rate of requests per application, you might use: `record: app:http_requests_rate5m:sum` `expr: sum by (app) (rate(http_requests_total[5m]))`

Implementing Recording Rules in Kubernetes

With the Prometheus Operator, recording rules are defined using `PrometheusRule` Custom Resources. These resources are typically deployed in the same namespace as your Prometheus instance. Let's create a recording rule for our custom exporter's metrics. We'll calculate the 5-minute rate of processed tasks. 1. **`app-recording-rules.yaml`**: This file defines a `PrometheusRule` resource. We'll define a rule to calculate the rate of `app_processed_tasks_total` over 5 minutes.

    apiVersion: monitoring.coreos.com/v1
    kind: PrometheusRule
    metadata:
      name: my-app-recording-rules
      namespace: prometheus # Deploy in the same namespace as Prometheus
      labels:
        release: prometheus # Link to the Prometheus instance
    spec:
      groups:
      - name: my-custom-app-rules
        interval: 30s # How often to evaluate these rules
        rules:
        - record: app:processed_tasks_rate5m:irate # New metric name
          expr: |
            irate(app_processed_tasks_total[5m]) # PromQL expression
        - record: app:active_workers:avg
          expr: |
            avg_over_time(app_active_workers[5m]) # Average active workers over 5m
    
**Note:** `irate` is used here instead of `rate` because `irate` detects only the instantaneous rate of increase, which is better for rapidly changing counters like `app_processed_tasks_total`. `rate` averages over the entire range. For slowly changing counters or for general trend analysis, `rate` is often preferred. `interval` defines how often Prometheus evaluates these rules and stores the results. 2. **Deploy the Recording Rules**:

    kubectl apply -f app-recording-rules.yaml
    
3. **Verify Recording Rules**: * Wait a few minutes for Prometheus to evaluate the new rules. * Forward the Prometheus UI again if needed:

        kubectl port-forward svc/prometheus-kube-prometheus-prometheus 9090:9090 -n prometheus
        
* Go to `http://localhost:9090` and navigate to "Status" -> "Rules". You should see your `my-custom-app-rules` group listed, along with the `app:processed_tasks_rate5m:irate` and `app:active_workers:avg` rules. * Go to the "Graph" interface and query `app:processed_tasks_rate5m:irate` or `app:active_workers:avg`. You should see the pre-computed time series. This setup provides a robust foundation for monitoring your custom applications with granular detail and optimized performance.

Security Considerations

When extending your monitoring capabilities with custom exporters and recording rules, it's crucial to address security implications: * **Metrics Endpoint Exposure**: * **Network Access**: By default, Prometheus exporters expose metrics on a plain HTTP endpoint. Ensure that network access to these endpoints is restricted to your Prometheus scraping agents only. In Kubernetes, `ServiceMonitor` targets internal cluster IPs, which is generally safer than exposing them externally. For critical applications, consider using network policies to strictly control ingress to exporter ports. * **Authentication/Authorization**: For highly sensitive metrics, consider adding basic authentication or mTLS to your exporter endpoint. However, this adds complexity as Prometheus also needs to be configured with credentials to scrape them. Most internal metrics are not considered sensitive enough to warrant this, but it's a critical decision based on your data. * **Sensitive Data in Metrics**: Never expose Personally Identifiable Information (PII), secrets, or other highly sensitive data through your metrics endpoints. Metrics should be aggregations or counts, not raw data. For instance, `failed_login_attempts_total` is fine; `last_failed_login_username` is not. * **Exporter Vulnerabilities**: * **Dependency Management**: Keep your exporter's dependencies (e.g., Python `prometheus_client` library) updated to patch known vulnerabilities. Use minimal base images in your `Dockerfile`. * **Resource Limits**: Implement resource limits (`requests` and `limits` in Kubernetes Deployment) for your exporter pods to prevent them from consuming excessive CPU or memory, which could be exploited in a DoS attack. * **Prometheus Access**: * **RBAC**: Ensure that access to the Prometheus UI and API is controlled via Kubernetes RBAC and potentially external authentication if exposed outside the cluster. * **Prometheus Configuration**: Restrict who can modify `ServiceMonitor` and `PrometheusRule` resources. Only authorized personnel should be able to define new scrape targets or recording rules. * **Recording Rule Impact**: While recording rules are for optimization, poorly written or excessively complex rules can still impact Prometheus's performance. Test new rules in a staging environment before deploying to production.
**Security Best Practice:** Adopt a "least privilege" approach for all components. Your exporter should only have the permissions it needs, and its metrics endpoint should only be accessible by Prometheus.

Best Practices

To maximize the effectiveness and maintainability of your Prometheus custom exporters and recording rules, follow these best practices:

For Custom Exporters:

1. **Metric Naming Conventions**: Adhere to Prometheus naming conventions (`snake_case` for names, `_total` for counters, `_bucket` for histograms). Use clear, descriptive names and labels. 2. **Use Appropriate Metric Types**: * `Counter`: For values that only ever increase (e.g., total requests, errors). * `Gauge`: For values that can go up or down (e.g., current queue size, active users). * `Histogram`/`Summary`: For observing distributions of values (e.g., request durations). Histograms are generally preferred due to their aggregability. 3. **Keep Exporters Lightweight**: Exporters should be simple, efficient, and fast. Their primary job is to expose metrics, not to perform heavy computations or complex business logic. 4. **Statelessness**: Ideally, exporters should be stateless. If they need to maintain state, ensure it's minimal and resilient. 5. **Error Handling**: Implement robust error handling within your exporter logic. Failed metric collection should not crash the exporter. 6. **Avoid High Cardinality**: Be mindful of the number of unique label combinations (cardinality) you expose. High cardinality can significantly increase Prometheus's resource consumption and degrade query performance. For example, avoid labels like `user_id` or `session_id`. 7. **Documentation**: Clearly document what each metric represents, its units, and any important labels. 8. **Graceful Shutdown**: Ensure your exporter handles `SIGTERM` signals gracefully to allow Kubernetes to shut down pods cleanly.

For Recording Rules:

1. **Clear Naming**: Use descriptive names for your recorded metrics, often following the `level:metric:aggregation` pattern (e.g., `namespace:http_requests_total:rate5m`). 2. **Focus on Performance and Simplicity**: Create recording rules for: * Frequently queried, complex expressions. * Aggregations across many series. * Base metrics for alerts that need consistent evaluation. 3. **Avoid Excessive Cardinality**: Just like with raw metrics, recording rules can also introduce high cardinality if not careful. Aggregate over relevant labels and drop unnecessary ones. 4. **Version Control**: Store your `PrometheusRule` YAML files in a version control system (like Git) alongside your application code. This enables tracking changes, auditing, and easier deployment. 5. **Test Thoroughly**: Before deploying recording rules to production, test them in a staging environment. Verify that the `expr` produces the expected results and that the `interval` is appropriate. 6. **Choose the Right Interval**: The `interval` for recording rules should be a multiple of your scrape interval. A common practice is `1m` or `30s` for rules, while scrape intervals are often `15s` or `30s`. 7. **Monitor Prometheus Performance**: Keep an eye on Prometheus's own metrics (e.g., `prometheus_tsdb_head_chunks_created_total`, `prometheus_engine_query_duration_seconds`) to ensure that new recording rules aren't negatively impacting its performance.

FAQ

1. Can I use custom exporters for non-Kubernetes applications?

Absolutely! Prometheus exporters are designed to run anywhere. While this article focuses on Kubernetes deployment, the core Python exporter code and `Dockerfile` would be identical. You would simply run the Docker container on a VM, bare metal, or any other environment, and configure your Prometheus server (which could also be outside Kubernetes) to scrape its `/metrics` endpoint by specifying its IP address and port in the `scrape_configs` section of `prometheus.yml`.

2. What's the difference between recording rules and alerting rules?

Both recording rules and alerting rules are defined in `PrometheusRule` resources (or directly in `prometheus.yml` if not using the Operator). * **Recording Rules**: Pre-compute and store the results of PromQL expressions as new time series. Their purpose is to optimize query performance and simplify complex metrics for dashboards or subsequent rules. They generate *data*. * **Alerting Rules**: Evaluate PromQL expressions and, if the condition is met for a specified duration, fire an alert. Their purpose is to notify operations teams about potential issues. They generate *notifications*. Often, alerting rules are built on top of recording rules to leverage the pre-computed, optimized data, making alerts faster and more reliable.

3. How do I troubleshoot if my custom exporter metrics aren't showing up in Prometheus?

This is a common issue, and here's a checklist: 1. **Exporter Pod Status**: * Is the exporter pod running? `kubectl get pods -l app=my-custom-exporter` * Check pod logs for errors: `kubectl logs ` * Can you `curl` the `/metrics` endpoint directly from *within* the cluster? `kubectl exec -it -- curl localhost:8000/metrics` 2. **Service Connectivity**: * Is the Kubernetes `Service` correctly selecting your pod? `kubectl describe svc my-custom-exporter` * Can you `curl` the service from another pod in the same namespace? `kubectl run -it --rm debug --image=busybox -- /bin/sh` `wget -O - my-custom-exporter:8000/metrics` 3. **ServiceMonitor Configuration**: * Is the `ServiceMonitor` correctly defined? `kubectl describe servicemonitor my-custom-exporter` * Does the `selector` in `ServiceMonitor` match the `labels` of your `Service`? * Does the `port` name in `ServiceMonitor.endpoints` match a `name` in your `Service.ports`? * Is the `namespaceSelector` correct, allowing Prometheus to discover services in your app's namespace? * Does the `ServiceMonitor` have the correct `release: prometheus` label (or whatever label your Prometheus instance is configured to select)? 4. **Prometheus Targets**: * Access the Prometheus UI (`http://localhost:9090/targets` after port-forwarding). Is your exporter target listed? Is it "UP"? If it's "DOWN", check the error message in the UI. * Check Prometheus logs for scraping errors: `kubectl logs -n prometheus prometheus-kube-prometheus-prometheus-0` (replace pod name if different). 5. **Prometheus Configuration**: * If you're not using Prometheus Operator, ensure your `prometheus.yml` `scrape_configs` are correctly configured.

Conclusion

In the dynamic world of Kubernetes, a robust monitoring strategy is non-negotiable. While Prometheus offers a powerful foundation, the ability to extend its reach to custom application metrics and optimize its performance for complex queries is paramount. Custom exporters provide the flexibility to instrument virtually any application or system, exposing critical insights that off-the-shelf solutions might miss. From tracking specific business KPIs to monitoring internal component states, exporters ensure no data point remains in the dark. Coupled with this, Prometheus recording rules act as a crucial performance enhancer. By pre-aggregating and simplifying complex PromQL expressions, they reduce the load on your Prometheus server, accelerate dashboard loading times, and provide a stable foundation for highly responsive alerting. This combination allows DevOps teams to move beyond basic infrastructure monitoring, gaining deep, actionable visibility into their applications' health and performance without compromising on efficiency. Embracing custom exporters and recording rules is a significant step towards achieving comprehensive observability, empowering your teams to proactively identify and resolve issues, ultimately leading to more stable, performant, and reliable Kubernetes deployments.

Written By

Sujay Singh

Technology Expert / Cloud Architect at Virtual Venture covering AI, cloud computing, cybersecurity, and emerging tech trends.

Sources & References

• Official company announcements and press releases

• Industry reports from Gartner, IDC, and Statista

• Peer-reviewed research and technical documentation

• On-record statements from industry experts

Last verified: June 26, 2026

Fact-checked by TechNews Venture editorial team

Leave a Comment

Comments are moderated and will appear after review.