liveness probe for microservice without http

6/4/2020

I have a microservice that is not a webservice.

It is a Spring Boot (1.5) CommandLineRunner app that does not have a need to expose an API or do anything with http.

However, I need to give it a liveness probe for Kubernetes.

Can this be accomplished without refactoring it into a webservice app?

I have this configuration added to enable Spring's info endpoint

management:
  endpoint:
    health:
      enabled: true
    info:
      enabled: true

# https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-endpoints
info:
  app:
    name: foo-parser
    description: parses binary files from S3 and updates the database

I implemented this health check class

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;

@Component
public class HealthCheck extends AbstractHealthIndicator {

  Logger log = LoggerFactory.getLogger("jsonLogger");

  private final MySQLAccessor mySQLAccessor;
  private static final String FOO_TABLE = "foo";

  @Autowired
  public HealthCheck(final MySQLAccessor mySQLAccessor) {
    this.mySQLAccessor = mySQLAccessor;
  }

  @Override
  protected void doHealthCheck(Builder builder) throws Exception {
    boolean result = mySQLAccessor.healthCheck(FOO_TABLE);
    if (result) {
      log.info("HELLO! the health check is good!");
      builder.up().withDetail("test", "good");
    }
    else {
      log.info("HELLO! OH NOES the health check is ungood!");
      builder.down().withDetail("test", "bad");
    }
  }
}

Can this idea work? Or do I have to refactor it to serve web requests?

Thank you for any clues

-- slashdottir
health-check
java
kubernetes
spring-boot

2 Answers

6/4/2020

you can expose actuator endpoint details including the healthcheck using JMX.

example application.yml

management:
  endpoints:
    jmx:
      exposure:
        include: health,info,metrics,mappings

Then define the liveness probe to run a script (or java program) to call the JMX endpoint and answer the healthcheck:

example k8s config

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: liveness
    image: my-app 
    livenessProbe:
      exec:
        command:
        - /bin/sh
        - test_app_with_jmx.sh 
      initialDelaySeconds: 5
      periodSeconds: 5
-- stringy05
Source: StackOverflow

6/4/2020

Trying out stringy05's idea...

enabled jmx endpoints:

management:
  endpoints:
    jmx:
      exposure:
        include: "*"
        exclude:
  endpoint:
    health:
      enabled: true
    info:
      enabled: true

Using this answer:

https://stackoverflow.com/questions/1751130/calling-jmx-mbean-method-from-a-shell-script#answer-47869966

I tried this:

import javax.management.*;
import javax.management.remote.*;

public class JMXInvoker {

  public static void main(String... args) throws Exception {
    Object result = JMXConnectorFactory.connect(new JMXServiceURL(args[0]))
        .getMBeanServerConnection().invoke(new ObjectName(args[1]), args[2], new Object[]{}, new String[]{});
    String status = "" + result;
    String state = status.substring(8,10);
    Boolean ok = state.compareTo("UP") == 0;
    if (!ok)
      System.exit(1);
  }
}

Added some JMX related beans to the main app class

@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class FooParserApplication implements CommandLineRunner {

  Logger log = LoggerFactory.getLogger("jsonLogger");
  @Autowired
  private FooStuff fooStuff;

  @Bean
  public RmiRegistryFactoryBean rmi() {
    RmiRegistryFactoryBean rmi = new RmiRegistryFactoryBean();
    rmi.setPort(5555);
    return rmi;
  }
  
  @Bean
  public ConnectorServerFactoryBean server() throws Exception {
    ConnectorServerFactoryBean fb = new ConnectorServerFactoryBean();
    fb.setObjectName("connector:name=rmi");
    fb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:5555/myconnector");
    return fb;
  }

  public static void main(final String[] args) {
    final SpringApplication springApplication = new SpringApplication(ECHParserApplication.class);
    springApplication.run(args);
  }

  @Override
  public void run(final String... args) {
     fooStuff.doIt()
  }
}

calling it from bash:

java -cp foo-parser.jar -Dloader.main=com.foo.JMXInvoker org.springframework.boot.loader.PropertiesLauncher service:jmx:rmi://localhost/jndi/rmi://localhost:5555/myconnector org.springframework.boot:type=Endpoint,name=Health health

Now I just need to put it in a bash script for Kubernetes.

Thanks!

-- slashdottir
Source: StackOverflow