This guide walks you through the process of deploying a Spring Boot application on Kubernetes. There are many choices of how to do things with Spring Boot and Kubernetes — the intention with this guide is to get you up and running as quickly as possible, not to discuss all the alternatives or go into all the details of how you get to production (which is, of course, our favourite place to be).
There are some interactive tutorials that complement and extend the content of this guide on Katacoda/springguides. If you follow those tutorials, all the code will be running in the cloud from your browser. Or you can create your own cluster and install all the tools you need locally, then copy paste from the guides.Getting Started with Spring Boot on Kubernetes: the same material as this guide, but running in your browser.Install Kubernetes: a guide to installing Kubernetes locally using Kind. You can use this to get setup on your laptop if you prefer to run the tutorials there.Kubernetes Probes with Spring Boot: a guide to liveness and readiness probes with Spring Boot.
What you’ll build
Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. It groups containers that make up an application into logical units for easy management and discovery. In this guide we will build and deploy a simple Spring boot application.
You will need a Linux or Linux-like command line. Command line examples in this guide work on Linux, a MacOS terminal with a shell, or WSL on Windows.
You will also need a Kubernetes cluster and the command line tool Kubectl. You can create a cluster locally using Kind (on Docker) or Minikube. Or you can use a cloud provider, such as Google Cloud Platform, Amazon Web Services or Microsoft Azure. Before proceeding further, verify you can run kubectl commands from the shell. E.g. (using kind):
$ kubectl cluster-info
Kubernetes master is running at https://127.0.0.1:46253
KubeDNS is running at https://127.0.0.1:46253/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
and
$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 7m13s
Create a Spring Boot Application
The first thing we will do is create a Spring Boot application. If you have one you prefer to use already in github, you could clone it in the terminal (git and java are installed already). Or you can create an application from scratch using start.spring.io:
curl https://start.spring.io/starter.tgz -d dependencies=webflux,actuator | tar -xzvf -
You can then build the application:
./mvnw install
It will take a couple of minutes the first time, but then once the dependencies are all cached it will be fast.
And you can see the result of the build. If the build was successful, you should see a JAR file, something like this:
ls -l target/*.jar
-rw-r--r-- 1 root root 19463334 Nov 15 11:54 target/demo-0.0.1-SNAPSHOT.jar
The JAR is executable:
$ java -jar target/*.jar
The app has some built in HTTP endpoints by virtue of the “actuator” dependency we added when we downloaded the project. So you will see something like this in the logs on startup:
...
2019-11-15 12:12:35.333 INFO 13912 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2019-11-15 12:12:36.448 INFO 13912 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
...
So you can curl the endpoints in another terminal:
To complete this step, send Ctrl+C to kill the application.
Containerize the Application
There are multiple options for containerizing a Spring Boot application. For local development and testing it makes sense to start with a Dockerfile as the docker build workflow is generally well known and understood.
If you don’t have docker locally or want to automatically push an image to a registry then Jib would be a good choice. In an enterprise setting, when you need a trusted build service for a CI/CD pipeline, you could look at Cloud Native Buildpacks.
First create a Dockerfile:
FROM openjdk:8-jdk-alpine AS builder
WORKDIR target/dependency
ARG APPJAR=target/*.jar
COPY ${APPJAR} app.jar
RUN jar -xf ./app.jar
FROM openjdk:8-jre-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.demo.DemoApplication"]
Then build the container image, giving it a tag (choose your own ID instead of “springguides” if you are going to push to Dockerhub):
$ docker build -t springguides/demo .
You can run the container locally:
$ docker run -p 8080:8080 springguides/demo
and check that it works in another terminal:
$ curl localhost:8080/actuator/health
Finish off by killing the container.
You won’t be able to push the image unless you authenticate with Dockerhub (docker login), but there’s an image there already that should work. If you were authenticated you could:
$ docker push springguides/demo
In real life the image needs to be pushed to Dockerhub (or some other accessible repository) because Kubernetes pulls the image from inside its Kubelets (nodes), which are not in general connected to the local docker daemon. For the purposes of this scenario you can omit the push and just use the image that is already there.
Just for testing, there are workarounds that make docker push work with an insecure local registry, for instance, but that is out of scope for this scenario.
Deploy the Application to Kubernetes
You have a container that runs and exposes port 8080, so all you need to make Kubernetes run it is some YAML. To avoid having to look at or edit YAML, for now, you can ask kubectl to generate it for you. The only thing that might vary here is the --image name. If you deployed your container to your own repository, use its tag instead of this one:
You can take the YAML generated above and edit it if you like, or you can just apply it:
$ kubectl apply -f deployment.yaml
deployment.apps/demo created
service/demo created
Check that the application is running:
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/demo-658b7f4997-qfw9l 1/1 Running 0 146m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 2d18h
service/demo ClusterIP 10.43.138.213 <none> 8080/TCP 21h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo 1/1 1 1 21h
NAME DESIRED CURRENT READY AGE
replicaset.apps/demo-658b7f4997 1 1 1 21h
d
Keep doing kubectl get all until the demo pod shows its status as “Running”.
Now you need to be able to connect to the application, which you have exposed as a Service in Kubernetes. One way to do that, which works great at development time, is to create an SSH tunnel:
$ kubectl port-forward svc/demo 8080:8080
then you can verify that the app is running in another terminal: