A few weeks ago, I’ve covered how you can properly create Docker images for your Spring boot application. Last time, we’ve also seen how to set up Kubernetes locally with Minikube. Now, it’s time to combine the two, and deploy a Spring boot application on Kubernetes.
Creating a deployment
The first step, when trying to deploy an application with Kubernetes is to describe the deployment/pods itself within a YAML file. To do this, I’m going to use the following YAML configuration:
apiVersion: apps/v1 kind: Deployment metadata: name: movie-quote-service-deployment labels: app: movie-quote-app tier: backend spec: selector: matchLabels: app: movie-quote-app tier: backend template: metadata: labels: app: movie-quote-app tier: backend spec: containers: - name: movie-quote-service image: g00glen00b/movie-quote-service:0.0.1 ports: - containerPort: 8080 name: service-port
We start by defining some labels, such as app=movie-quote-app and tier=backend. We can then use these labels to define which containers should be deployed.
In this case, I have a Docker image called g00glen00b/movie-quote-service, which will expose port 8080, so I added that as well.
Creating a secret
The application we’re planning to deploy connects to a database, and thus, we’ll use Kubernetes secrets to configure the passwords properly. In this example, I’ll store these secrets within my YAML configuration file.
By default, secrets have to be base64 encoded first. So, let’s generate those base64 strings first:
echo -n "dbuser" | base64 echo -n "dbpass" | base64 echo -n "P@\$\$w0rd" | base64
Please note, you have to add the
-n flag, otherwise a newline will be appended to the end, and that will be encoded as well.
Now you can add the following configuration to your YAML file:
--- apiVersion: v1 kind: Secret metadata: name: mysql-login data: rootPassword: UEAkJHcwcmQK username: ZGJ1c2VyCg== password: ZGJwYXNzCg==
Obviously, if you want to expose your database somehow, you probably don’t want to publish these credentials.
Using a secret
Once you’ve setup your secret, you can use it within environment variables by using the
secretKeyRef property, for example:
name: movie-quote-service-deployment image: g00glen00b/movie-quote-service:0.0.1 # ports: ... env: - name: SPRING_DATASOURCE_URL value: "jdbc:mysql://localhost:3306/quotes?useSSL=false&allowPublicKeyRetrieval=true" - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: name: mysql-login key: username - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: mysql-login key: password
As you can see, we’re configuring both
SPRING_DATASOURCE_PASSWORD by referring to a secret. You don’t have to use secrets though, as seen within
If you want to limit the resources that are available to your container, you can use the
resources property within your YAML configuration file for your container, for example:
name: movie-quote-service-deployment image: g00glen00b/movie-quote-service:0.0.1 # env: ... # ports: ... resources: requests: memory: "128Mi" cpu: "250m" limits: memory: "256Mi" cpu: "1000m"
In this example, we limited the memory to 256Mb of RAM, and 1 CPU. The “m” for the CPU stands for “milli”, and thus 1000m is 1 CPU.
Configuring health checks
In Kubernetes, we can configure two types of health checks:
- The liveness probe can be used to configure when an application is actually down when it has been running fine for a while. Possible causes could be memory issues, connections going stale, running out of disk space and so on.
- The readiness probe can be used to configure when an application is still starting up. In some cases, applications might require some additional time to load initial datasets and so on. With the readiness probe, we tell Kubernetes when an application is ready to accept requests.
With Spring boot, we could use the health actuator to actually configure the liveness probe. When the application has stale connections, or it has other issues, the status will go from UP to DOWN, and by default, it will return a HTTP status 503 in stead of 200. This means that the liveness check would fail, and the application would be restarted, which is exactly what we want.
To configure this, we can use the following configuration:
name: movie-quote-service-deployment image: g00glen00b/movie-quote-service:0.0.1 ports: - containerPort: 8080 name: service-port # env: ... # resources: ... livenessProbe: httpGet: port: service-port path: /actuator/health initialDelaySeconds: 30 timeoutSeconds: 10
So, we’re using the /actuator/health endpoint, configured the application with an initial delay of 30 seconds to guarantee that the application has started, and with a timeout of 10 seconds in case the application goes unresponsive.
We also have to define on which port the liveness probe could be found. The nice thing is that we can just refer to the service-port which we defined earlier in the
Make sure that the initial delay covers the startup time of your application. If your application requires about 20 seconds to start up, you could add another 10 seconds and configure an initial delay of 30 seconds. Otherwise, your application could go in a restart loop if your application wasn’t able to start up in time.
Configuring the readiness probe
Now, similar to the liveness check, we could configure the readiness check using any endpoint. When the application is ready, any endpoint should work, including the /actuator/health endpoint. So, if you’re lazy, you could use the same endpoint, or you could configure it to be something else, like /actuator/info or a custom endpoint.
name: movie-quote-service-deployment image: g00glen00b/movie-quote-service:0.0.1 ports: - containerPort: 8080 name: service-port # env: ... # resources: ... # livenessProbe: ... readinessProbe: httpGet: port: service-port path: /actuator/health initialDelaySeconds: 20 timeoutSeconds: 5
As you can see, both probes have similar configuration. It doesn’t have to be though, and you could configure different endpoints. Additionally, you can use other types of probes as well, such as a TCP probe, a command that has to be executed, … .
Defining a service
To expose your applications to the outer world, we define a service. Much like a proxy, they route traffic to the actual container. There are several kinds of services, some of which could provide load balancing and such. However, when running Kubernetes locally, we have only access to the NodePort service.
The NodePort service requires two ports to be configured. First, you have to configure which port it should forward to. Second, you have to configure on which port you want to expose the application.
NodePort services can only be exposed to ports within the 30000-32767 range:
--- apiVersion: v1 kind: Service metadata: name: movie-quote-service labels: app: movie-quote-app tier: backend spec: ports: - name: http nodePort: 30080 port: 8080 type: NodePort selector: app: movie-quote-app tier: backend
Just like before, we use the labels to properly configure the NodePort. In this case, our application will be available within the Kubernetes cluster on port 30080.
Running on Minikube
Now that we defined both the secret, deployment and the service, we can start deploying them on Minikube.
First of all, make sure that Minikubeb is up and running:
If this command doesn’t return a proper IP address, you can start Minikube using the following command:
Once Minikube is up and running, you can deploy the containers using kubectl:
kubectl apply -f kubefiles/movie-quote-service.yml
Now you can verify if the application is running by using the following commands:
kubectl get pods kubectl get svc
The “movie-quote-service” pod should have the status “Running”, and the services should contain a service called “movie-quote-service” active on port 30080.
Additionally, you could use the Minikube dashboard to see the status of your pods:
Opening your application
Now that our application is running on Kubernetes, you can test it out. First you have to find out on which IP address Minikube is running. If you used a virtual machine (eg. virtualbox) as the driver, this won’t be your local IP address.
Now that you know the IP address, you should be able to visit your application on port 30080.
Alternatively, you can also forward the port to your local machine, by using the port-forward command:
kubectl port-forward svc/movie-quote-service 8080:8080
While this is running, you should be able to visit your application on
So there you have it, you now have your Spring boot application up and running on Kubernetes.