Monitoring your microservices with MicroProfile Health Check and Metrics
In a microservices architecture, microservices work together similarly to how independent members on a team work together toward a goal even when assigned specialized and unique tasks. This distribution of responsibility makes the application more modular, comprehensive, and resilient. However, as your application continues to grow and expand, you may eventually wonder about what tools MicroProfile provides to help manage and monitor your microservices. In comes Health Check and Metrics. Designed to help you detect and diagnose issues, Health Check and Metrics track system statistics and keep your deployments optimized when working together with smart container orchestrations. With new and powerful technologies (like Kubernetes) that automatically manage your deployments becoming more mainstream in the modern age of containerized cloud-native applications, effectively configuring deployment environments have never been more critical. This blog post seeks to show you IBM’s solution to this need and how the experience has been for me as a newcomer to Spring and MicroProfile.
This blog represents one in a series, with our previous blog post covering how to easily document your APIs with OpenAPI. We cover a wide variety of topics and technologies regarding microservices, so check them out if they interest you.
To demonstrate how to implement Health Check and Metrics into your app and why you should, we will use IBM’s BlueCompute storefront application on GitHub. This repository hosts a Spring Boot implementation and, more recently, a MicroProfile version. Walk with me and let’s explore the work needed to utilize MicroProfile Health Check and Metrics like Spring Boot’s Actuator.
Spring Boot: Monitoring your microservices
Both Health Check and Metrics come part of a collection of features that is Spring Boot Actuator. As a simple dependency added to your build tool of choice, it enables a developer to monitor apps, gather metrics, and obtain general info without much effort. The endpoints for both features we are covering today are
/metrics, and the Actuator allows for additional configuration.
Although you could leave the
/health endpoint configured by default, the code snippet above demonstrates a terribly simple way of how we could implement some sort of logic into our health check. I’ve simplified the logic portion down to an “isHealthy” method, but it would ideally contain all the necessary logic you need to make sure your application holds up to your working standards. Additionally, it would be better practice to write different error responses and reverse the status logic, where you would return an “up” status after your series of checks pass, but let’s suspend our disbelief together for the sake of retaining simplicity.
In the BlueCompute store, you’ll find that
/health was left default with an additional
/check endpoint included.
Metrics also comes with defaults, such as obtaining basic system properties like memory usage and uptime, but you can also add custom counters. In the BlueCompute store, it’s been left untouched. Here’s a demonstration on how to create a counter for reference and comparison later:
How cute and sweet is that? We create a little counter using Spring’s CounterService, but this object only comes with increment, decrement, and reset.
To implement your own custom Metrics with Spring Boot—and specifically Spring Boot 2’s new Micrometer—you’ll be using MeterRegistry’s API for gauging, timing, counting, etc., and using MeterRegistryCustomizer to tag your metrics. Spring’s documentation includes some basic examples on how to use these. With the exception of adding bits of code to associate tags with your beans, the Spring implementation doesn’t look too far from the code snippet.
MicroProfile: Monitoring your microservices
The core structure of the method in both the MicroProfile and Spring implementations echo each other for the sake of simplicity. The only real journey we made here falls in line with some of the other MicroProfile features in terms of creating an interface, as annotated by the
@Health header. For the most part, both libraries achieve very similar things and in very similar ways.
You can have the
/health endpoint serve you further when used in conjunction with certain deployment tools. Kubernetes provide two powerful health tools to manage your containers: the readiness probe and the liveness probe. The readiness probe, as the name would imply, checks an endpoint to notify Kubernetes when an application has booted successfully or becomes ready. The liveness probe, on the other hand, serves as a more active check to see if your application has run into a bug or deadlock. If either probe returns a failure for some reason, Kubernetes will attempt to redeploy the application as a form of self-healing. In the BlueCompute store app, both the readiness probe and liveness probe use HTTP GET requests to monitor the app.
Configuring a liveness probe in your deployment to check the return code of a
/health endpoint is as simple as providing a URL to test and doing so allows a developer to organize and keep their health logic confined within the application. For example, if for any reason we’d need to refactor our health logic, it would involve changing the appropriate class and then rebuilding the container. The appeal of the
/health endpoint comes from the convenience of being a one-stop check that we can apply to any application.
The statistics retrieved by MicroProfile and Spring by default don’t wander too far from each other—they both retrieve general stats like memory and heap usages. We start to see differences when implementing custom metrics. Where Spring seems to revolve around one class able to measure what you need when you need them, MicroProfile allows you to prepend your methods with what you need as you need them. Allow me to rephrase: Spring provides the general tool that you customize or specialize what it measures, and MicroProfile asks you to assemble the instruments you want and it handles the rest.
Here, we annotated our getMetrics method with three metrics, each of them returning a useful statistic. When it comes down basic measurements of an operation, the first two a developer thinks of are:
How fast/slow/long was the call?
How many times do we call it?
@Metered answer these exact two questions. Naturally, the
@Timed annotation will return you how long the operation took from invocation until the end of its execution.
@Metered both measure how many times the call was invoked.
The ease of use in simply typing in your desired header and filling the required parameters is amazing. It’s quick, accessible, and gets going on its own. MicroProfile’s friendly approach makes setting up metrics widely available to experienced and new developers alike but may drive off those very hands-on developers who prefer the ability to tweak everything they have. Unfortunately, this snapshot also shows how stacking headers can easily lead your code to a verbose style when descriptions and text intersperse your code. Overall, there are clear tradeoffs Spring and MicroProfile made to achieve their respective design philosophies, and I think it boils down to personal preference.
When you hit the
/metrics endpoint, you’ll run into a giant block of text and numbers. Admittedly, it’s not the most appealing form to digest your data. So, to better visualize our data, the store app deploys Prometheus alongside to scrape and graph counters and timers of our choice.
As an example, this chart measures the time taken by Inventory to complete a task. In this graph, the y-axis measures time the operation took while the x-axis tells you when it occurred (in the logs). The complexity and creativity of your graphs will revolve around which metrics you measure and how you layer them. For example, you could graph your application’s performance over time and track key operations to get a better understanding of its efficiency.
Conclusion with MicroProfile: My experiences and impressions
We’ve covered a lot of material, but Health Check and Metrics only represent two of the many features MicroProfile provides. We coupled these features together since they work fantastically as a pair. We have Metrics retrieving important system properties that we can view as raw text or as a graph with Prometheus, and these feed directly into implementing a server’s health logic. A lot of automation comes into play when deploying with Kubernetes using liveness probes, and once set up, this allows for amazingly quick upward scaling to whatever you need. And, of course, for those who require hands-on experience to better consume this content, the BlueCompute store app provides a great reference sample to entry for all the technologies mentioned here. When picking up this material, I found the OpenLiberty guides a powerful supplement, and I would recommend them to seasoned programmers or newcomers alike.
TL;DR: Both Spring and MicroProfile are powerful and perform similarly. The key differences are how you approach them. Customization is about the same, but Spring is more hands-on whereas MicroProfile is more developer friendly.
This blog only covers a couple of features in the MicroProfile suite, so be sure to visit the other blog posts that cover more sections. The code snippets shown were simplified versions of the code, available on GitHub. Check out how you can also host this app onto IBM Cloud and IBM Cloud Private.