2

Question related to service discovery with Spring Boot Actuator, combined with Spring Cloud Kubernetes please.

Currently, I have a web app that has both the actuator and spring boot kubernetes dependencies. I am also using the discovery client provided by kubernetes, things are working fine.

However, when I curl my health endpoint, I do see those strange statements:

discoveryComposite":{"description":"Discovery Client not initialized","status":"UNKNOWN","components":{"discoveryClient":{"description":"Discovery Client not initialized","status":"UNKNOWN"}}

"reactiveDiscoveryClients":{"description":"Discovery Client not initialized","status":"UNKNOWN","components":{"Kubernetes Reactive Discovery Client":{"description":"Discovery Client not initialized","status":"UNKNOWN"}

Simple Reactive Discovery Client":{"description":"Discovery Client not initialized","status":"UNKNOWN"}}}

"readinessState":{"status":"UP"},"refreshScope":{"status":"UP"}},"groups":["liveness","readiness"]}*

May I ask why is it "unknown"? I would have expected at least one of the three here to how up something, and definitely not "Discovery Client not initialized".

Did I forget to initialize something? To register something? To configure something?

Btw, this is really a question regarding discovery with kubernetes. Not related to Eureka, not related to Consul and such.

Many thanks

PatPanda
  • 3,644
  • 9
  • 58
  • 154

2 Answers2

6

Having the same issue, I've noticed that the org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator has its discoveryInitialized field set to false. This is because no one triggered an InstanceRegisteredEvent in the application context. Usually the event should be triggered from the start() method of so called "registration" - a bean responsible for registration of current application instance in service registry, e.g. EurekaAutoServiceRegistration in case of Eureka.

The point is that Kubernetes is not a service registry per se and it doesn't require explicit registration (because every pod within it is "registered" by default due to the nature of orchestration). That is why Spring Cloud Kubernetes does nothing by default to register the application instance with Kubernetes.

However there is org.springframework.cloud.kubernetes.registry.KubernetesAutoServiceRegistration class that can "emulate" the process of auto registration by simply emitting messages to the log. As of v1.1.0 this class is @Deprecated and has no usages in the framework elsewhere. The only profit I see of using it at the moment, is that it can trigger the missing InstanceRegisteredEvent for initializing the DiscoveryClientHealthIndicator. You can use it by adding the following bean declaration to any of your @Configuration classes:

@Bean
public KubernetesAutoServiceRegistration kubernetesAutoServiceRegistration(
         ApplicationContext context, 
         KubernetesServiceRegistry registry,
         KubernetesRegistration registration) {
  return new KubernetesAutoServiceRegistration(context, registry, registration);
}

But please do not rely on it heavily as it will likely be removed in forthcoming versions of the framework.

As an alternative you can emit the InstanceRegisteredEvent by yourself somewhere from your code but make sure you do it when the application is actually ready to work with discovery client.

Toparvion
  • 799
  • 2
  • 9
  • 19
  • This is a great answer, thanks! I just upgraded to the Ilford RC1 just released few hours ago. It seems it needs a bit of change, as they changed on the Kubernetes side. trying – PatPanda Dec 16 '20 at 13:01
  • A first dry run, it seems the new version does not like this – PatPanda Dec 16 '20 at 13:24
  • The bean 'kubernetesHealthIndicator', defined in class path resource [org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration$KubernetesActuatorConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/kubernetes/client/KubernetesClientAutoConfiguration$KubernetesActuatorConfiguration.class] and overriding is disabled. – PatPanda Dec 16 '20 at 13:24
  • I don't see any relations between this problem and my answer so far. However I suppose that this is a typical issue of framework upgrading: the `KubernetesActuatorConfiguration` class has moved to `fabric8` package in "Ilford RC1", but your target directory still contains a .class file for it in `client` package from the previous version. As a result, Spring finds both versions of the class when scanning the classpath and thus fails with the exception. Try to clean the target directory (e.g. `gradle clean`, `mvn clean` or rebuild in IDE) and run the app again. – Toparvion Dec 17 '20 at 01:57
  • 1
    @PatPatPat, BTW I've found the source of the issue you observe - there was a refactoring in Ilford release where the package was renamed. See [this section](https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2020.0-Release-Notes#code-refactoring) of Release Notes. – Toparvion Dec 21 '20 at 10:00
  • Thanks, continuing the trying! – PatPanda Dec 21 '20 at 13:36
2

DiscoveryClientHealthIndicator implements ApplicationListener<InstanceRegisteredEvent<?>> and overrides onApplicationEvent(InstanceRegisteredEvent<?> event) method to listen event and intialize DiscoveryClientHealthIndicator because disoveryInitialize field set to false at the start.

To trigger InstanceRegisteredEvent you need to create publisher of that event. The publisher constructs the event object and publishes it to anyone who's listening.

To publish the event, the publisher can simply inject the ApplicationEventPublisher and use the publishEvent() API. Call its in the methods annotated with @PostConstruct just after the initialization of bean properties, to be sure that application is actually ready to work with discovery client.

@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@PostConstruct
public void postConstruct(){
    String message = "Instance register event triggered";
    publishCustomEvent(message);

}

public void publishCustomEvent(final String message) {
    InstanceRegisteredEvent<?> instanceRegisteredEvent = new InstanceRegisteredEvent<>(this, message);
    applicationEventPublisher.publishEvent(instanceRegisteredEvent);
}
Veljko P.
  • 89
  • 5