Hello, K8s Spring Boot!
Tags: java, kubernetes
Published: 2020-02-25 08:50:23

Let's try to build the simplest Java Spring application that runs as a pod in a Kubernetes cluster.

The full project can be found on my [GitHub](https://github.com/MarounMaroun/spring-k8s-helloworld).

The project's structure:

```sh
├── Dockerfile
├── README.md
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── k8s
│   └── depl.yaml
├── settings.gradle
└── src
    └── main
        └── java
            └── hello
                ├── App.java
                └── HelloWorldCtrl.java
```

`App.java` is our entry point:

```java
package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}
```

and our controller simply returns "hello world" on the root path:

```java
package hello;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloWorldCtrl {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}
``` 

Now let's write a Dockerfile:

```sh
FROM gradle:jdk10 as builder

COPY --chown=gradle:gradle . /app
WORKDIR /app
RUN gradle build

EXPOSE 8080
WORKDIR /app

CMD java -jar build/libs/gs-spring-boot-0.1.0.jar
```

It uses Gradle in order to build the application, and the `CMD` instruction runs the JAR file.


The K8s deployment is simple. It consists of a [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) and a [service](https://kubernetes.io/docs/concepts/services-networking/service/):

```sh
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hello-world
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: hello-world
        visualize: "true"
    spec:
      containers:
      - name: hello-world-pod
        image: marounbassam/hello-spring
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  labels:
    visualize: "true"
  name: hello-world-service
spec:
  selector:
    app: hello-world
  ports:
  - name: http
    protocol: TCP
    port: 8080
    targetPort: 8080
  type: ClusterIP
```

The deployment defines two replicas of the pod that will be running the container that's built from the image specified in the image attribute.

The service is of type `ClusterIP` (the default Kubernetes service). It gives us a service inside our cluster that other apps can access.

Creating the resources in your cluster:

```sh
kubectl create -f <yaml_file>
```

The resources can be visualized as follows:

```sh
+---------------------+
| hello-world-service |
|                     |
|    10.15.242.210    |
+---------O-----------+
          |
          +-------------O--------------------------O
                        |                          |
              +---------O-----------+    +---------O-----------+
              |        pod 1        |    |        pod 2        |
              |                     |    |                     |
              |     hello-world     |    |     hello-world     |
              +---------------------+    +---------------------+
```

### Inside The Cluster

If you're using minikube for running the cluster locally, you might encounter issues (I really don't know why). But if you're running on a cloud provider, or something more "serious" than minikube, you should be able to do the following:

```sh
$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
hello-world-5bb87c95-6h4kh   1/1       Running   0          7h
hello-world-5bb87c95-bz64v   1/1       Running   0          7h
$ kubectl get svc
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
hello-world-service   ClusterIP   10.15.242.210   <none>        8080/TCP   5s
kubernetes            ClusterIP   10.15.240.1     <none>        443/TCP    7h
$ kubectl exec -it hello-world-5bb87c95-6h4kh bash
$ (inside the pod) curl 10.15.242.210:8080
$ (inside the pod) Greetings from Spring Boot!
```

If you have any questions, please drop a comment.