Reputation: 639
I want to implement custom logic to determine readiness for my pod, and I went over this: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.kubernetes-probes.external-state and they mention an example property:
management.endpoint.health.group.readiness.include=readinessState,customCheck
Question is - how do I override customCheck
?
In my case I want to use HTTP probes, so the yaml looks like:
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
httpGet:
path: /actuator/health
port: 12345
So then again - where and how should I apply logic that would determine when the app is ready (just like the link above, i'd like to rely on an external service in order for it to be ready)
Upvotes: 12
Views: 14080
Reputation: 86
customCheck
is a key for your custom HealthIndicator. The key for a given HealthIndicator is the name of the bean without the HealthIndicator suffix
You are defining readinessProbe, so probably hiting /actuator/health/readiness is a better choice.
public class CustomCheckHealthIndicator extends AvailabilityStateHealthIndicator {
private final YourService yourService;
public CustomCheckHealthIndicator(ApplicationAvailability availability, YourService yourService) {
super(availability, ReadinessState.class, (statusMappings) -> {
statusMappings.add(ReadinessState.ACCEPTING_TRAFFIC, Status.UP);
statusMappings.add(ReadinessState.REFUSING_TRAFFIC, Status.OUT_OF_SERVICE);
});
this.yourService = yourService;
}
@Override
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
if (yourService.isInitCompleted()) {
return ReadinessState.ACCEPTING_TRAFFIC;
} else {
return ReadinessState.REFUSING_TRAFFIC;
}
}
}
Upvotes: 5
Reputation: 7495
To expand KrzysztofS's answer:
First, create a custom health indicator like this:
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.stereotype.Component;
@Component
public class MyCustomReadinessIndicator extends ReadinessStateHealthIndicator {
private final AtomicBoolean ready = new AtomicBoolean();
public MyCustomReadinessIndicator(ApplicationAvailability availability) {
super(availability);
}
@Override
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
return ready.get()
? ReadinessState.ACCEPTING_TRAFFIC
: ReadinessState.REFUSING_TRAFFIC;
}
public void markAsReady() {
if (ready.get()) {
throw new IllegalStateException("Already initialized");
}
ready.set(true);
}
}
This must be a bean, or Spring won't be able to discover it.
@Autowire
your component to your service or another component, and call its markAsReady()
function when this indicator should switch into "ready" state.
Next, add the name of the bean1 into "include" block for "readiness" group in your application.yaml file (if you're using application.properties, figure it out yourself).
management:
endpoint:
health:
group:
readiness:
include: readinessState, myCustomReadinessIndicator
show-components: always
show-details: always
probes:
enabled: true
Next, try running your application and opening various Actuator endpoints.
Endpoint | ready state ofyour indicator |
HTTP response code |
JSON response |
---|---|---|---|
/actuator/health |
false |
503 | { "status": "OUT_OF_SERVICE", "components": { "myCustomReadinessIndicator": { "status": "OUT_OF_SERVICE" }, "livenessState": { "status": "UP" }, "readinessState": { "status": "UP" } }, "groups": ["liveness", "readiness"] } |
/actuator/health/liveness |
false |
200 | {"status":"UP"} |
/actuator/health/readiness |
false |
503 | { "status": "OUT_OF_SERVICE", "components": { "myCustomReadinessIndicator": { "status": "OUT_OF_SERVICE" }, "readinessState": { "status": "UP" } } } |
/actuator/health |
true |
200 | { "status": "UP", "components": { "myCustomReadinessIndicator": { "status": "UP" }, "livenessState": { "status": "UP" }, "readinessState": { "status": "UP" } }, "groups": ["liveness", "readiness"] } |
/actuator/health/liveness |
true |
200 | {"status":"UP"} |
/actuator/health/readiness |
true |
200 | { "status": "UP", "components": { "myCustomReadinessIndicator": { "status": "UP" }, "readinessState": { "status": "UP" } } } |
Now, you need to set /actuator/health/readiness
in your readiness probe's path
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
httpGet:
path: /actuator/health/readiness
port: 12345
Related: set liveness probe's path to /actuator/health/liveness
not to /actuator/health
, since /actuator/health
will return 503 if your indicator isn't ready yet, even though livenessState
is "UP".
1 Bean name is usually camel-cased name of the class, starting with lowercase letter, but you can override it by providing name in component annotation: @Component("overriddenName")
Upvotes: 15