se.guides.health.adoc Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2019, 2024 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
= Helidon SE Health Check Guide
:description: Helidon health checks
:keywords: helidon, health check, health check, health, check
:rootdir: {docdir}/../..
include::{rootdir}/includes/se.adoc[]
This guide describes how to create a sample Helidon SE project
that can be used to run some basic examples using both built-in and custom health checks.
== What You Need
For this 15 minute tutorial, you will need the following:
include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites]
=== Create a Sample SE Project
Generate the project sources using the Helidon SE Maven archetype.
The result is a simple project that can be used for the examples in this guide.
[source,bash,subs="attributes+"]
.Run the Maven archetype:
----
mvn -U archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion={helidon-version} \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.se
----
=== Using the Built-In Health Checks
Helidon has a set of built-in health checks:
* deadlock detection
* available disk space
* available heap memory
The following example shows how to use the built-in health checks. These examples are all executed
from the root directory of your project (helidon-quickstart-se).
Notice that the `pom.xml` file in the generated project already contains dependencies for Helidon's health component and for the
built-in health checks.
[source,xml]
.Generated dependencies related to health
----
io.helidon.webserver.observe
helidon-webserver-observe-health
io.helidon.health
helidon-health-checks
----
Handling health checks is part of Helidon's observability support.
By default, when you add the dependency for the built-in health checks, Helidon automatically registers the built-in checks.
[source,bash]
.Build and run the project
----
mvn clean package
java -jar target/helidon-quickstart-se.jar
----
In another window, access the application's health endpoint.
[source,bash]
.Access the health endpoint
----
curl -v http://localhost:8080/observe/health
----
The verbose `curl` output reports the HTTP status:
[source,text]
----
< HTTP/1.1 204 No Content
----
The successful status means all health checks reported `UP`.
To see the details about each health check, add the following `features` configuration fragment in the `server` section of the
`application.yaml`.
Make sure the `features` key is at the same level as `port` and `host` that are already in the file.
[source,yaml]
.Configuration fragment to include details in the health output (nested under `server`)
----
server:
port: 8080
host: 0.0.0.0
features: # <1>
observe:
observers:
health:
details: true
----
<1> Added `features` config section.
Press ^C to stop the running server, rebuild it, and rerun it.
[source,bash]
.Stop, rebuild, and rerun the server
----
^C
mvn clean package
java -jar target/helidon-quickstart-se.jar
----
In the other window access the health endpoint again.
[source,bash]
.Access the health endpoint
----
curl -v http://localhost:8080/observe/health
----
This time the `curl` output shows not only the HTTP status--as 200 instead of 204 because the response now contains data--but also the detailed output for all health checks.
[source,hocon]
.Health check details
----
{
"status": "UP", // <1>
"checks": [ // <2>
{
"name": "diskSpace",
"status": "UP",
"data": {
"total": "465.63 GB",
"percentFree": "14.10%",
"totalBytes": 499963174912,
"free": "65.67 GB",
"freeBytes": 70513274880
}
},
{
"name": "heapMemory",
"status": "UP",
"data": {
"total": "516.00 MB",
"percentFree": "99.82%",
"max": "8.00 GB",
"totalBytes": 541065216,
"maxBytes": 8589934592,
"free": "500.87 MB",
"freeBytes": 525201320
}
},
{
"name": "deadlock",
"status": "UP"
}
]
}
----
<1> Overall application health status
<2> List of individual health checks.
=== Adding Custom Health Checks
You can add your own custom health checks. These typically assess the conditions in and around your application and report whether the service should be considered started, live, and/or ready.
The following trivial but illustrative example adds a custom start-up check that reports `DOWN` until the server has been running for eight seconds and reports `UP` thereafter. Note the two main steps in the example code:
1. Create an explicit instance of `ObserveFeature` which contains a custom `HealthObserver` with the custom check.
2. Add that `ObserveFeature` instance to the `WebServerConfig.Builder` as a feature.
[source,java]
.Updated `Main#main`, augmenting the creation of `WebServer` instance with a custom health check
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_1, indent=0]
----
<1> Declare a variable for holding the server start-up time. (This is set later in the code.)
<2> Begin preparing the custom `HealthObserver` according to this app's specific needs.
<3> Turn on detailed output in HTTP responses to the health endpoint.
<4> Add a custom start-up health check:
* Compute the status for the response according to whether the server has been up for at least eight seconds.
* Add a detail to the response reporting the time at which the health check was queried.
* Set the health check type as `STARTUP`.
* Set the health check name to `"warmedUp"`.
<5> Find and apply configuration for observability observers _other_ than health (because we are about to create our own custom `HealthObserver`).
<6> Add the `HealthObserver` to the `ObserveFeature`.
<7> Add the `ObserveFeature` instance as a feature to the webserver.
<8> Record when the server has actually started.
Note that the health check type and name are fixed, whereas the health check recomputes the value of the response every time Helidon queries it.
NOTE: For the next step, be ready to access the health endpoint very quickly after you restart the server!
[source,bash]
.Stop, rebuild, and rerun the application
----
^C
mvn package
java -jar target/helidon-quickstart-se.jar
----
[source,bash]
.Access the health endpoint **quickly**
----
curl -v http://localhost:8080/observe/health
----
If you access the health endpoint before the server has been up for eight seconds, `curl` reports the response status as `503 Service Unavailable` and displays output similar to the following:
[source,json]
.Health response shortly after server restart (partial)
----
{
"status": "DOWN",
"checks": [
{
"name": "warmedUp",
"status": "DOWN",
"data": {
"time": 1702068978353
}
}
]
}
----
The built-in health checks (not shown in the example output) all report `UP` but the new custom start-up health check reports `DOWN` because the server has been up only a short time.
Access the health endpoint again, after the server has been up at least eight seconds.
[source,bash]
.Access the health endpoint again **after 8 seconds**
----
curl -v http://localhost:8080/observe/health
----
This time, `curl` reports `200 OK` for the response status and displays different output for the custom health check.
[source,json]
.Health response after the server has been running a while (partial)
----
{
"status": "UP",
"checks": [
{
"name": "warmedUp",
"status": "UP",
"data": {
"time": 1702069379717
}
}
]
}
----
The example code includes the built-in health checks in Helidon's overall health assessment of the application. To exclude them invoke the `HealthObserver.Builder` `useSystemServices` method (for example, just after invoking `details` on the builder).
[source,java]
.Disable all built-in health checks
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_2, indent=0]
----
Alternatively, you could instead remove the dependency on the `helidon-health-checks` component from the `pom.xml` file.
=== Accessing Specific Health Check Types
You can choose which category of health check to retrieve when you access the health endpoint by adding the health check type as an additional part of the resource path:
* liveness only - http://localhost:8080/observe/health/live
* readiness only - http://localhost:8080/observe/health/ready
* startup only - http://localhost:8080/observe/health/started
* all - http://localhost:8080/observe/health
[source,bash]
.Get only start-up health checks
----
curl http://localhost:8080/observe/started
----
[source,json]
.JSON response:
----
{
"status": "UP",
"checks": [
{
"name": "warmedUp",
"status": "UP",
"data": {
"time": 1702069835172
}
}
]
}
----
=== Applying Configuration to a Custom Health Observer: Customizing the URL path
Earlier examples showed how to add custom health checks by building a custom `HealthObserver` in which the code set up the behavior of the health subsystem explicitly. Recall that the example code invoked the `HealthObserver.Builder` `details` method to turn on detailed output.
Once it creates a custom health observer, your code has full responsibility for determining the observer's behavior; Helidon does not automatically apply configuration to a custom observer. But your code can easily do so.
The next example customizes the URL path for the health endpoint, first explicitly in the code and then via configuration.
==== Customizing the endpoint path in the code
Customize the URL path for health checks by invoking the `endpoint` method on the `HealthObserver.Builder`.
[source,java]
.Set a custom endpoint path
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_3, indent=0]
----
<1> Changes the health endpoint path to `/myhealth`.
[source,bash]
.Build and run the application, then verify that the health check endpoint responds at `/myhealth`:
----
curl http://localhost:8080/myhealth
----
Earlier you added health config to the `application.yaml` config file to turn on detailed output. If you want to run an experiment, change that `details` setting in the config file to `false` and stop, rebuild, and rerun the application. Now access the health endpoint (at `/myhealth`, remember).
The output _remains_ detailed because your code--which has full responsibility for determining the custom health observer's behavior--does not apply configuration to the custom observer's builder.
==== Adding configuration to a custom observer
In addition to preparing the health observer builder with hard-coded settings, your code can also apply configuration for health. This allows someone who deploys your application to control the behavior of the health subsystem using configuration without requiring source code changes to your application.
The generated `Main` class in the application already creates a `Config` object for the top-level config node. Using the following code to create the observe feature also applies any health-related configuration settings to the custom health observer. Notice the added line just before the `HealthObserver.Build` `build()` invocation near the end of the example code.
[source,java]
.Apply health configuration to your custom health observer
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_4, indent=0]
----
<1> Find and apply any health-related settings from configuration at the `server.features.observe.observers.health` config key.
Your code decides what config key to use for retrieving the configuration. Recall earlier, before adding custom health checks, you added a config section for health--to set `details` to `true`--at `server.features.observe.observers.health`. Helidon used that configuration to set up the health observer _it_ created automatically. To be consistent for anyone preparing the configuration file, it's a good idea for your application code--as it prepares a custom `HealthObserver`--to look in the same place Helidon does for health config.
Order is important. Here, the code first sets `details` to `true` explicitly and later applies configuration. If your end user sets `details` in the `server.features.observe.observers.health` config to `false`, that setting overrides the hard-coded `true` setting in the code _because of where in the code you apply the configuration_. Try changing the `details` value to `false` in the config file and then stop, rebuild, and rerun the application. Access the health endpoint and notice that the output is no longer detailed.
In general, most applications should apply settings from config _after_ assigning any settings in the code so users have the final say, but there might be exceptions in your particular case.
=== Using Liveness, Readiness, and Startup Health Checks with Kubernetes
The following example shows how to integrate the Helidon health API in an application that implements
health endpoints for the Kubernetes liveness, readiness, and startup probes.
[source,java]
.Add a `readyTime` variable to the `Main` class:
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_5, indent=0]
----
[source,java]
.Change the `HealthObserver` builder in the `Main#main` method to use new built-in liveness checks and custom liveness, readiness, and startup checks:
----
include::{sourcedir}/se/guides/HealthSnippets.java[tag=snippet_6, indent=0]
----
<1> Add built-in health checks.
<2> Add a custom readiness check.
<3> Add a custom start-up check.
<4> Add a custom liveness check.
[source,bash]
.Build and run the application, then verify the liveness, readiness, and started endpoints:
----
curl http://localhost:8080/health/live
curl http://localhost:8080/health/ready
curl http://localhost:8080/health/started
----
[source,bash]
.Stop the application and build the docker image:
----
docker build -t helidon-quickstart-se .
----
[source,yaml]
.Create the Kubernetes YAML specification, named `health.yaml`, with the following content:
----
kind: Service
apiVersion: v1
metadata:
name: helidon-health # <1>
labels:
app: helidon-health
spec:
type: NodePort
selector:
app: helidon-health
ports:
- port: 8080
targetPort: 8080
name: http
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: helidon-health # <2>
spec:
replicas: 1
selector:
matchLabels:
app: helidon-health
template:
metadata:
labels:
app: helidon-health
version: v1
spec:
containers:
- name: helidon-health
image: helidon-quickstart-se
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health/live # <3>
port: 8080
initialDelaySeconds: 5 # <4>
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready # <5>
port: 8080
initialDelaySeconds: 5 # <6>
periodSeconds: 2
timeoutSeconds: 3
startupProbe:
httpGet:
path: /health/started # <7>
port: 8080
initialDelaySeconds: 8 # <8>
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
---
----
<1> A service of type `NodePort` that serves the default routes on port `8080`.
<2> A deployment with one replica of a pod.
<3> The HTTP endpoint for the liveness probe.
<4> The liveness probe configuration.
<5> The HTTP endpoint for the readiness probe.
<6> The readiness probe configuration.
<7> The HTTP endpoint for the startup probe.
<8> The startup probe configuration.
[source,bash]
.Create and deploy the application into Kubernetes:
----
kubectl apply -f ./health.yaml
----
[source,bash]
.Get the service information:
----
kubectl get service/helidon-health
----
[source,bash]
----
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helidon-health NodePort 10.107.226.62 8080:30116/TCP 4s # <1>
----
<1> A service of type `NodePort` that serves the default routes on port `30116`.
[source,bash]
.Verify the health endpoints using port '30116', your port may be different:
----
curl http://localhost:30116/health
----
[source,bash]
.Delete the application, cleaning up Kubernetes resources:
----
kubectl delete -f ./health.yaml
----
=== Summary
This guide demonstrates how to use health checks in a Helidon SE application as follows:
* Access the default health checks
* Create and use custom readiness, liveness, and startup checks
* Customize the health check root path
* Integrate Helidon health check with Kubernetes
Refer to the following reference for additional information:
* link:{javadoc-base-url}/index.html?overview-summary.html[Helidon Javadoc]
© 2015 - 2025 Weber Informatics LLC | Privacy Policy