Deploying Kotlin on Kubernetes: A Practical Basic Guide
Kubernetes (K8s) has become the standard for orchestrating containerized applications. If you develop server-side applications in Kotlin (using Ktor, Spring Boot, or http4k), sooner or later you will face the question of deploying them in a cluster. In this guide, we will walk through the process from packaging a Kotlin application into a Docker image to running it in Kubernetes using ConfigMap and Ingress. All examples are working and ready for adaptation.
1. Preparing the Kotlin Application and Docker Image
For the example, let's take a simple application on Ktor (can be replaced with any other framework). Our application will listen on port 8080 and return a JSON with the version.
// src/main/kotlin/com/example/Application.ktimport io.ktor.server.engine.*import io.ktor.server.netty.*import io.ktor.server.routing.*import io.ktor.server.response.*
fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respond(mapOf("app" to "kotlin-on-k8s", "version" to "1.0.0")) } } }.start(wait = true)}Let's build the application into a fat JAR using Gradle (the shadow plugin). After the build, create a Dockerfile:
# DockerfileFROM openjdk:17-jdk-slimWORKDIR /appCOPY build/libs/app-all.jar app.jarEXPOSE 8080CMD ["java", "-jar", "app.jar"]Build the image and push it to a registry (e.g., Docker Hub):
docker build -t youruser/kotlin-app:1.0.0 .docker push youruser/kotlin-app:1.0.02. Creating Kubernetes Manifests
Now let's describe the resources for deployment. Create three files: deployment.yaml, service.yaml, and configmap.yaml.
2.1. ConfigMap for Configuration
Let's externalize application settings (e.g., logging level or connection string) into a ConfigMap:
# configmap.yamlapiVersion: v1kind: ConfigMapmetadata: name: app-configdata: LOG_LEVEL: "DEBUG" APP_MODE: "production"2.2. Deployment
In the Deployment manifest, specify the image, port, replicas, and inject variables from the ConfigMap:
# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: kotlin-appspec: replicas: 2 selector: matchLabels: app: kotlin-app template: metadata: labels: app: kotlin-app spec: containers: - name: app image: youruser/kotlin-app:1.0.0 ports: - containerPort: 8080 envFrom: - configMapRef: name: app-config livenessProbe: httpGet: path: / port: 8080 initialDelaySeconds: 10 periodSeconds: 52.3. Service
The Service will balance traffic between pods:
# service.yamlapiVersion: v1kind: Servicemetadata: name: kotlin-app-servicespec: selector: app: kotlin-app ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP3. Deploying to the Cluster and Configuring Ingress
Apply the manifests:
kubectl apply -f configmap.yamlkubectl apply -f deployment.yamlkubectl apply -f service.yamlCheck that the pods are running:
kubectl get podskubectl logs <pod-name>Now make the application accessible from outside. Create an Ingress (assuming an Ingress Controller, e.g., NGINX, is installed in the cluster):
# ingress.yamlapiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: kotlin-app-ingressspec: rules: - host: kotlin.example.com http: paths: