Enable liveness and readiness by default

Closes gh-22825
This commit is contained in:
Stéphane Nicoll 2025-09-15 10:46:35 +02:00
parent 8f0d87a964
commit 83ad15ba3b
6 changed files with 18 additions and 80 deletions

View File

@ -672,7 +672,7 @@ TIP: The `ssl` javadoc:org.springframework.boot.actuate.health.HealthIndicator[]
If an SSL certificate will be invalid within the time span defined by this threshold, the javadoc:org.springframework.boot.actuate.health.HealthIndicator[] will warn you but it will still return HTTP 200 to not disrupt the application.
You can use this threshold to give yourself enough lead time to rotate the soon to be expired certificate.
Additional javadoc:org.springframework.boot.actuate.health.HealthIndicator[] beans are available but are not enabled by default:
Additional javadoc:org.springframework.boot.actuate.health.HealthIndicator[] beans are enabled by default:
[cols="3,4,6"]
|===
@ -687,6 +687,8 @@ Additional javadoc:org.springframework.boot.actuate.health.HealthIndicator[] bea
| Exposes the "`Readiness`" application availability state.
|===
These can be disabled by using the configprop:management.endpoint.health.probes.enabled[] configuration property.
[[actuator.endpoints.health.writing-custom-health-indicators]]
@ -952,8 +954,8 @@ readinessProbe:
NOTE: `<actuator-port>` should be set to the port that the actuator endpoints are available on.
It could be the main web server port or a separate management port if the `"management.server.port"` property has been set.
These health groups are automatically enabled only if the application xref:how-to:deployment/cloud.adoc#howto.deployment.cloud.kubernetes[runs in a Kubernetes environment].
You can enable them in any environment by using the configprop:management.endpoint.health.probes.enabled[] configuration property.
These health groups are automatically enabled.
You can disable them by using the configprop:management.endpoint.health.probes.enabled[] configuration property.
NOTE: If an application takes longer to start than the configured liveness period, Kubernetes mentions the `"startupProbe"` as a possible solution.
Generally speaking, the `"startupProbe"` is not necessarily needed here, as the `"readinessProbe"` fails until all startup tasks are done.

View File

@ -16,26 +16,18 @@
package org.springframework.boot.actuate.autoconfigure.availability;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.availability.LivenessStateHealthIndicator;
import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.health.contributor.Health;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* {@link EnableAutoConfiguration Auto-configuration} for availability probes.
@ -47,7 +39,7 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
@AutoConfiguration(after = { AvailabilityHealthContributorAutoConfiguration.class,
ApplicationAvailabilityAutoConfiguration.class })
@ConditionalOnClass(Health.class)
@Conditional(AvailabilityProbesAutoConfiguration.ProbesCondition.class)
@ConditionalOnBooleanProperty(name = "management.endpoint.health.probes.enabled", matchIfMissing = true)
public final class AvailabilityProbesAutoConfiguration {
@Bean
@ -68,49 +60,4 @@ public final class AvailabilityProbesAutoConfiguration {
return new AvailabilityProbesHealthEndpointGroupsPostProcessor(environment);
}
/**
* {@link SpringBootCondition} to enable or disable probes.
* <p>
* Probes are enabled if the dedicated configuration property is enabled or if the
* Kubernetes cloud environment is detected/enforced.
*/
static class ProbesCondition extends SpringBootCondition {
private static final String ENABLED_PROPERTY = "management.endpoint.health.probes.enabled";
private static final String DEPRECATED_ENABLED_PROPERTY = "management.health.probes.enabled";
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
ConditionMessage.Builder message = ConditionMessage.forCondition("Probes availability");
ConditionOutcome outcome = onProperty(environment, message, ENABLED_PROPERTY);
if (outcome != null) {
return outcome;
}
outcome = onProperty(environment, message, DEPRECATED_ENABLED_PROPERTY);
if (outcome != null) {
return outcome;
}
if (CloudPlatform.getActive(environment) == CloudPlatform.KUBERNETES) {
return ConditionOutcome.match(message.because("running on Kubernetes"));
}
if (CloudPlatform.getActive(environment) == CloudPlatform.CLOUD_FOUNDRY) {
return ConditionOutcome.match(message.because("running on Cloud Foundry"));
}
return ConditionOutcome.noMatch(message.because("not running on a supported cloud platform"));
}
private @Nullable ConditionOutcome onProperty(Environment environment, ConditionMessage.Builder message,
String propertyName) {
String enabled = environment.getProperty(propertyName);
if (enabled != null) {
boolean match = !"false".equalsIgnoreCase(enabled);
return new ConditionOutcome(match, message.because("'" + propertyName + "' set to '" + enabled + "'"));
}
return null;
}
}
}

View File

@ -34,7 +34,7 @@
"name": "management.endpoint.health.probes.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable liveness and readiness probes.",
"defaultValue": false
"defaultValue": true
},
{
"name": "management.endpoint.health.status.order",
@ -137,6 +137,7 @@
"description": "Whether to enable liveness and readiness probes.",
"defaultValue": false,
"deprecation": {
"level": "error",
"replacement": "management.endpoint.health.probes.enabled"
}
},

View File

@ -41,18 +41,8 @@ class AvailabilityProbesAutoConfigurationTests {
AvailabilityHealthContributorAutoConfiguration.class, AvailabilityProbesAutoConfiguration.class));
@Test
void probesWhenNotKubernetesAddsNoBeans() {
this.contextRunner.run(this::doesNotHaveProbeBeans);
}
@Test
void probesWhenKubernetesAddsBeans() {
this.contextRunner.withPropertyValues("spring.main.cloud-platform=kubernetes").run(this::hasProbesBeans);
}
@Test
void probesWhenCloudFoundryAddsBeans() {
this.contextRunner.withPropertyValues("spring.main.cloud-platform=cloud_foundry").run(this::hasProbesBeans);
void probesWhenDefaultAddsBeans() {
this.contextRunner.run(this::hasProbesBeans);
}
@Test
@ -62,17 +52,14 @@ class AvailabilityProbesAutoConfigurationTests {
}
@Test
void probesWhenPropertyEnabledButNoHealthDependencyDoesNotAddBeans() {
this.contextRunner.withPropertyValues("management.endpoint.health.probes.enabled=true")
.withClassLoader(new FilteredClassLoader("org.springframework.boot.health"))
void probesWhenNoHealthDependencyDoesNotAddBeans() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.boot.health"))
.run(this::doesNotHaveProbeBeans);
}
@Test
void probesWhenKubernetesAndPropertyDisabledAddsNotBeans() {
this.contextRunner
.withPropertyValues("spring.main.cloud-platform=kubernetes",
"management.endpoint.health.probes.enabled=false")
void probesWhenPropertyDisabledAddsNotBeans() {
this.contextRunner.withPropertyValues("management.endpoint.health.probes.enabled=false")
.run(this::doesNotHaveProbeBeans);
}

View File

@ -67,7 +67,8 @@ abstract class AbstractManagementPortAndPathSampleActuatorApplicationTests {
ResponseEntity<String> entity = new TestRestTemplate().withBasicAuth("user", "password")
.getForEntity("http://localhost:" + this.managementPort + "/admin/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("{\"groups\":[\"comp\",\"live\",\"ready\"],\"status\":\"UP\"}");
assertThat(entity.getBody())
.isEqualTo("{\"groups\":[\"comp\",\"live\",\"liveness\",\"readiness\",\"ready\"],\"status\":\"UP\"}");
}
@Test

View File

@ -87,7 +87,7 @@ class SampleSecureWebFluxApplicationTests {
.header("Authorization", getBasicAuth())
.exchange()
.expectBody(String.class)
.isEqualTo("{\"status\":\"UP\"}");
.isEqualTo("{\"groups\":[\"liveness\",\"readiness\"],\"status\":\"UP\"}");
}
private String getBasicAuth() {