Configuring Java version in a Maven based app

There’s a couple of different ways to configure the Java version for an app in your Maven pom.xml file.

The simplest is with the following properties:

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

If you’re configuring a Spring Boot app with Maven, you can also use this single property (only works for a Spring Boot app using the starter parent spring-boot-starter-parent) to override the Spring Boot default:

<properties>
<java.version>17</java.version>
</properties>

Deploying a Spring Boot app to AWS Elastic Beanstalk with the eb cli

Deploying a Spring Boot app to AWS Elastic Beanstalk is relatively easy using the eb cli tool, if you’re prepared to accept some defaults as part of the deploy. It’s probably possible to configure/customize IAM roles, Security Groups etc, but accepting the defaults is an easy way to deploy during development.

To initialize a new Elastic Beanstalk deployment, run in the root of your Spring Boot source folder:

eb init

Before creating the environment and deploying your app, edit the .elasticbeanstalk/config.yml that the previous step creates, to configure the built app jar to be deployed, by adding this section:

deploy:
artifact: target/your-app-jar-0.0.1-SNAPSHOT.jar

To create a single dev/test env with no load balancing:

eb create --single

To tear down the deployed ec2 instance running your Spring Boot app:

eb terminate

Service Port Configuration

By default, Beanstalk services are deployed on port 5000. Since Spring Boot apps are on port 8080 by default, the quickest way to configure Spring Boot to accept incoming requests on port 5000 is to edit the ‘Updates, monitoring and logging’ section and add a new env var for Spring Boot to reconfigure to use port 5000 instead (this is described here)

Added new env var SERVER_PORT=5000:

Building a Maven based Spring Boot app with multiple app classes with main() methods

I have a Maven based Spring Boot project with 2 Application classes. One is my main Spring Boot app, the other is a standalone example app. When attempting to build with ‘mvn package’ I get this error:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.6.4:repackage (repackage) on project xample-client: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.6.4:repackage failed: Unable to find a single main class from the following candidates [kh.aprs.botexample.APRSISBotExampleApplication, kh.aprs.clientexample.APRSISClientExampleApplication]

The 2 apps listed are my 2 apps, but only APRSISBotExampleApplication is the one I want to get packaged, so I need to tell Maven which is the the main app. This is configured with the <start-class> property, configured in a properties section like this:

<properties>
 <start-class>your.package.YourApplication</start-class>
</properties>

This is described here.

Kubernetes Rolling Updates: implementing a Readiness Probe with Spring Boot Actuator

During a Rolling Update on Kubernetes, if a service has a Readiness Probe defined, Kubernetes will use the results of calling this heathcheck to determine when updated pods are ready to start accepting traffic.

Kubernetes supports two probes to determine the health of a pod:

  • the Readiness Probe: used to determine if a pod is able to accept traffic
  • the Liveliness Probe: used to determine if a pod is appropriately responding, and if not, it will be killed and a new pod restarted

Spring Boot’s Actuator default healthcheck to indicate if a service is up and ready for traffic can be used for a Kubernetes Readiness Probe. To include in an existing Spring Boot service, add the Actuator maven dependency:

<dependency>
<groupId>org.springframework.boot</groupId
<artifactId>spring-boot-starter-actuator</artifactId
</dependency>

This adds the default healthcheck accessible by /actuator/health, and returns a 200 (and json response { “status” : “up”} ) if the service is up and running. 

To configure Kubernetes to call this Actuator healthcheck to determine the health of a pod, add a readinessProbe section to the container spec section for your deployment.yaml:

spec:
containers:
- name: exampleservice-b
image: kevinhooke/examplespringboot-b:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /example-b/v1/actuator/health
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 5

Kubernetes will call this endpoint to check when the pod is deployed and ready for traffic. During a rolling update, as new pods are created with an updated image, you’ll see their status go from 0/1 available to 1/1 available as soon as the Spring Boot service has completed startup and the healthcheck is responding.

The gif below shows deployment of an update image to a pod. Notice how as new pods are created, they move from 0/1 to 1/1 and then when they are ready, the old pods are destroyed: