
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: October 15, 2024
We might want to create a Spring Boot application with Kotlin and deploy it as a container in a Kubernetes cluster using Docker.
In this tutorial, we’ll look at deploying a simple Kotlin application to Kubernetes (K8s) using Docker and Docker Hub.
Before we proceed with deploying to Kubernetes, let’s ensure that we have a few prerequisites installed and basic knowledge of the technologies involved:
Let’s create a simple Spring Boot application we’ll containerize later:
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
Let’s also create a simple endpoint:
@RestController
@RequestMapping("/api")
class HelloController {
@GetMapping("/hello")
fun hello(): ResponseEntity<String> {
return ResponseEntity.ok("Hello From Docker and Kubernetes!")
}
}
We’ll use the endpoint to check that our container is running in the K8s cluster.
Once we have our main Kotlin application, we need to create an entry point to start it. We’ll build an executable with a docker-k8s Maven profile with the shade plugin or other plugins available to create an executable JAR. Therefore, we need to update the pom.xml file:
<profiles>
<profile>
<id>docker-k8s</id>
<build>
<finalName>kotlin-app</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.baeldung.dockerkubernetes.ApplicationKt</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Now, let’s create our JAR file:
$ mvn clean package -Pdocker-k8s
The Maven command will create the kotlin-app.jar file in the /target directory.
Next, let’s create a Docker image using the Dockerfile:
# Use an official OpenJDK runtime as a parent image
FROM openjdk:17-jdk-slim
# Set the working directory in the container
WORKDIR /app
# Copy the JAR file into the container
COPY target/kotlin-app.jar /app/kotlin-app.jar
# Run the application
CMD ["java", "-jar", "/app/kotlin-app.jar"]
Finally, let’s build our image:
$ docker build -t kotlin-app .
If we run the docker images command, we should see the new image listed:
REPOSITORY TAG IMAGE ID CREATED SIZE
kotlin-app latest 99630fc39439 3 days ago 447MB
Let’s push the Docker image to Docker Hub and deploy it to K8s.
To push our image to the Docker Hub, we need to log in:
$ docker login
We authenticate by providing our Docker Hub credential or with a browser authentication through a provided code.
Then, we need to provide a tag for the image. We’ll set a USERNAME environment variable to reuse throughout the process. For example, let’s say we have authenticated with the baeldung user:
$ export USERNAME=baeldung
Finally, let’s tag the image and push it to the repository:
$ docker tag kotlin-app:latest ${USERNAME}/kotlin-app:latest \
&& docker push ${USERNAME}/kotlin-app:latest
This command will pull all the Docker image layers and push the tagged image to the container repository. Notably, every container registry has its domain — in this case, it’s docker.io/, and we should use it as a prefix to identify our image.
We’ll use Minikube to deploy our Dockerized Spring Boot app to K8s locally. After installing Minikube, let’s start a local K8s cluster simply by running:
$ minikube start
The Minikube single-node K8s cluster will run as a Docker container, as we can see by running the docker ps -a command:
CONTAINER ID NAMES STATUS IMAGE PORTS
a7d43b6ac800 minikube Up 3 minutes gcr.io/k8s-minikube/kicbase:v0.0.45 127.0.0.1:32768->22/tcp ...
We need to create a K8s deployment to run our containerized Kotlin app inside pods. Let’s define a deployment.yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kotlin-app-deployment
spec:
replicas: 1
selector:
matchLabels:
app: kotlin-app
template:
metadata:
labels:
app: kotlin-app
spec:
containers:
- name: kotlin-app-container
image: DOCKER_IMAGE
ports:
- containerPort: 8080
Kubernetes doesn’t allow dynamic variable substitution. Therefore, we can manually replace DOCKER_IMAGE with the image we created earlier and apply the deployment:
$ kubectl apply -f deployment.yaml
Otherwise, we can export an environment variable:
$ export DOCKER_IMAGE="docker.io/${USERNAME}/kotlin-app:latest"
Then, we pipe cat with the sed command (to replace the variable name) and kubectl apply -f to create the deployment:
$ cat deployment.yaml | sed "s|DOCKER_IMAGE|$DOCKER_IMAGE|g" | kubectl apply -f -
Either way, let’s now check that we’ve deployed our pod:
$ kubectl get pods -l app=kotlin-app
We should also see our pod up and running:
NAME READY STATUS RESTARTS AGE
kotlin-app-deployment-664d69c57c-gszj6 1/1 Running 0 3m20s
Finally, we need to expose our deployment through a service. Let’s define a service.yaml file:
apiVersion: v1
kind: Service
metadata:
name: kotlin-app
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 30000
selector:
app: kotlin-app
Let’s create the service:
$ kubectl apply -f service.yaml
We also want to check the service creation:
$ kubectl get service kotlin-app
We should now see the service up and running:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kotlin-app NodePort 10.96.229.29 <none> 8080:30000/TCP 3m40s
As a Minikube alternative, we might consider another lightweight K8s distribution or deploy directly to the Cloud, for instance, with EKS or AKS.
Minikube runs Kubernetes clusters locally, but services are not automatically accessible through a public IP as they would be in a cloud environment. To access a service, Minikube will perform a port-forwarding:
$ minikube service kotlin-app
The output will show the exposed URL:
|-----------|------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|---------------------------|
| default | kotlin-app | 8080 | http://192.168.49.2:30000 |
|-----------|------------|-------------|---------------------------|
Opening service default/kotlin-app in default browser...
As we can see, a web browser opens the URL for the service by forwarding the service’s port from the Minikube VM to our local machine. This allows us to access the service as if it were running on a public IP but locally.
If we now curl or go to http://192.168.49.2:30000/api/hello URL in the browser, we’ll access the Kotlin app running inside the pod in the K8s cluster and get the response:
Hello From Docker and Kubernetes!
In this article, we saw how to deploy a Spring Boot Kotlin app in a K8s cluster. To achieve this, we first containerized a Spring Book app and pushed a Docker image to the Docker Hub.
We then used Minikube to start a local K8s cluster, created a deployment, and exposed a service through port-forwarding. Finally, we successfully got a response from the endpoint we exposed in the Kotlin app.