Prometheus and Grafana: Gathering Metrics from Spring Boot on Kubernetes

Brian McClain

If you read the guide on how to run Prometheus and Grafana on Kubernetes, you might be wondering: How do I add metrics from my application? Spring Boot developers are used to making metrics available from their application using Spring Boot Actuator and Micrometer, but Prometheus expects metrics to be in a specific format. In this guide, you’ll learn how to expose both standard and custom metrics in your Spring Boot application, gather them using Prometheus, and visualize them in Grafana.

All of the code for this guide can be found on GitHub.

Export Metrics for Prometheus from Spring Boot

Thanks to Spring Boot Actuator there are only a couple of steps you need to take to start exporting basic metrics from your application that Prometheus can gather. By default, Spring Boot Actuator provides a lot of metrics out of the box, including insight into the JVM, the machine the application is running on, and the web server that’s backing the application. To enable these insights, you can include the Spring Boot Actuator dependency in your pom.xml file, as well as the dependency that will provide them for Prometheus:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    <version>1.5.4</version>
</dependency>

Internally, including these dependencies makes an additional metrics endpoint available at /actuator/prometheus, but by default this endpoint isn’t reachable by outside services. You can expose the new endpoint by explicitly enabling it in your application.yml file, alongside the default health and metrics endpoints. You’ll also want to provide an application tag to your metrics so Grafana knows how to organize the metrics. Below, the application tag is set to match the name of the application, defined in the spring.application.name property:

spring:
  application:
    name: spring-prometheus-demo
management:
  endpoints:
    web:
      exposure:
        include: health, metrics, prometheus
  metrics:
    tags:
      application: ${spring.application.name}

That’s it! Upon running your application, you’ll see a new endpoint available that you can point Prometheus to. As with any Spring Boot application, you can start it with the following command:

./mvnw spring-boot:run

Once started, you’ll find a huge list of metrics made available at http://localhost:8080/actuator/prometheus!

Adding a Custom Metric

Insight into the machine running your application and the JVM it’s on is a great start. But what if you want to track custom metrics? If your application is running a website, maybe you want to track how many page hits certain endpoints are receiving, or how many times a resource with a specific ID is requested.

Since these metrics are being taken care of by Spring Boot Actuator, any custom metrics you generate are picked up by Prometheus as well! Consider a scenario in which you want to track how many times the main page of the website receives a request. For this, you’d use a simple counter, increasing it every time the page is requested. Take a look at the following RestController, which sets up a counter called visitCounter and adds it to the default MeterRegistry. Additionally, the visitCounter is incremented every time the / endpoint is requested, fulfilled by the index() method:

@RestController
public class WebController {

    Counter visitCounter;

    public WebController(MeterRegistry registry) {
        visitCounter = Counter.builder("visit_counter")
            .description("Number of visits to the site")
            .register(registry);
    }

    @GetMapping("/")
    public String index() {
        visitCounter.increment();
        return "Hello World!";
    }    
}

Restart the application and visit it running at http://localhost:8080/ a few times to increase the counter. You can then see the value of this counter by visiting http://localhost:8080/actuator/prometheus:

visit_counter_total 5.0

This same method can be used for timers and gauges as well.

Deploying the Application in Kubernetes

This guide assumes you’ve already deployed Prometheus to the Kubernetes cluster that you’re deploying your application to, but if not, be sure to check out the Prometheus and Grafana: Gathering Metrics from Kubernetes guide. You’ll also need to first build a container for your application, which you can do using the Maven spring-boot:build-image command as described in this guide on the Spring website. Once built and pushed to the container repository of your choice, you’re ready to deploy to Kubernetes!

You’ll need to deploy three things: a Deployment to run the application, a Service to access the application, and a ServiceMonitor to tell Prometheus how to gather metrics from your application. You can find the full deployment YAML here. First, take a look at the Deployment and the Service:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-prometheus-demo
  labels:
    app: spring-prometheus-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-prometheus-demo
  template:
    metadata:
      labels:
        app: spring-prometheus-demo
    spec:
      containers:
      - name: spring-prometheus-demo
        image: brianmmcclain/spring-prometheus-demo:0.0.1-SNAPSHOT
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

---
apiVersion: v1
kind: Service
metadata:
  name: spring-prometheus-demo-service
  labels:
    app: spring-prometheus-demo
spec:
  selector:
    app: spring-prometheus-demo
  ports:
    - protocol: TCP
      name: http-traffic
      port: 8080
      targetPort: 8080

The above creates a Deployment from the container image of the application, in this case pointing to my personal build at brianmmcclain/spring-prometheus-demo:0.0.1-SNAPSHOT. It also gives it the label of app: spring-prometheus-demo. The Service then uses that label as a selector to know how to attach to the application. The Service also receives a label of app: spring-prometheus-demo, which the ServiceMonitor will use to find the Service. One thing to note is that the port receives a name of http-traffic, which you can see the ServiceMonitor reference below:

---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: spring-prometheus-demo-service-monitor
spec:
  selector:
    matchLabels:
      app: spring-prometheus-demo
  endpoints:
  - port: http-traffic
    path: "/actuator/prometheus"

Here a ServiceMonitor is created, which looks for a Service with the label app: spring-prometheus-demo. It then defines an endpoint to use to scrape metrics, referring to the port named http-traffic and the path /actuator/prometheus, which as you saw is where Spring Boot exposes the Prometheus-formatted metrics.

You can verify that Prometheus is scraping the new endpoint by checking the targets it has registered, found under “Status” drop-down menu:

Spring application scrape information in Prometheus

Visualizing the JVM Metrics

The Grafana website maintains a great repository of community-made dashboards, including one for visualizing JVM metrics from Micrometer. Even better, Grafana makes it very easy to import existing dashboards, as shown in this guide. While it may take a few minutes for Prometheus to pick up some of the metrics, upon importing the dashboard, you’ll see a whole plethora of metrics start to be populated.

JVM metrics in Grafana

But what about the custom metric added to the code? For this, you can create a new panel by clicking the graph with the plus sign symbol on the top of the dashboard. You’ll be presented with a screen with a number of options. What you’re interested in is the query, however just entering the name of the visits metric (visit_counter_total) will show the cumulative total number of visits over time rather than a pattern of spikes and dips. This is where we need the help of the rate function, which “calculates the per-second average rate of increase of the time series in the range vector.” Put in simpler terms, it turns the accumulative counter into a time-series measurement. One-second intervals can also inject a lot of noise into the graph, so you can further modify the query to look at it in 5-minute intervals as well. In all, this turns the query into:

rate(visit_counter_total[5m])

Adding a panel for the custom metric

Apply your changes, and you’ll see the new graph added to the dashboard!

New panel added to the Grafana dashboard

What’s Next?

Hopefully this gave you an idea of how to start gathering metrics, both standard and custom, from your Spring Boot applications using Prometheus. Micrometer provides a huge array of tools you can take advantage of, all of which are automatically gathered by Prometheus. Grafana also provides a rich set of features for building your own queries, which may look intimidating at first, but is made easier thanks to great documentation and examples. If you’re setting up Prometheus and Grafana for the first time, we have a guide on how you can get started on your own!