Microservices with Kotlin, Kubernetes, and Linkerd
Tags: kubernetes, cloud, kotlin, microservices
Published: 2020-11-28 17:17:58



> KHipster is a development platform to generate, develop and deploy Spring Boot + Angular/React/Vue Fullstack Web applications and Spring microservices. KHipster is built on top of JHipster.

In this post, we will do the following: 

* Generate microservices with KHipster using JHipster Description Language (JDL)
* Generate Kubernetes configuration using JDL
* Generate code using KHipster
* Create a Kubernetes cluster in GCP
* Install Linkerd CLI and deploy Linkerd in Kubernetes cluster
* Deploy ingress controller in Kubernetes
* Deploy applications in Kubernetes cluster
* Configure traffic to application

Let us get started.

## Generate Microservices with KHipster

### Step 1: Install KHipster

KHipster is available as an NPM package. To install it run:

```
λ npm i -g generator-jhipster-kotlin
```

> Do not have Node installed? Check out `nvm` (Node Version Manager) that makes it easy to setup Node - [here](https://github.com/nvm-sh/nvm/blob/master/README.md).

Now, verify the installation by running:

```
λ khipster 
```

### Step 2: Create JDL file

> The JDL is a JHipster-specific domain language.

JDL provides a DSL to describe the applications, deployments, entities and their relationships. `Applications` refer to the service as a whole, the application can be a monolith or microservice. The `deployments` refer to the way to deploy the application it can be `kubernetes`. The `entities` straightaway maps to the database table in your application. The `relationships` refers to the entity relationship `one-to-one`, `one-to-many`, etc.,. JDL simplifies the process of creating the microservices applications and entities.

```
// Gateway application
application {
    config {
        applicationType         gateway
        authenticationType      jwt
        baseName                gateway
        buildTool               gradle
        clientFramework         react
        databaseType            sql
        devDatabaseType         h2Disk
        prodDatabaseType        mysql
        packageName             com.sendilkumarn
        serverPort              8080
        serviceDiscoveryType    no
    }
    entities *
} 

// Microservice application
application {
    config {
        applicationType         microservice
        authenticationType      jwt
        baseName                service
        buildTool               gradle
        databaseType            sql
        devDatabaseType         h2Disk
        prodDatabaseType        mysql
        packageName             com.sendilkumarn
        serverPort              8081
        serviceDiscoveryType    no
    }
    entities product
}

// Product entity 
entity product  {
    name    String  required
    price   Long    required
}
```

We have defined 2 applications: `gateway` and `service`, and an entity: `product`. The major difference between `gateway` and `service`:

* `gateway` uses `clientFramework` as `react`, `applicationType` as `gateway`
* `service` uses `applicationType` as `microservice`

> Refer we have used `serviceDiscoveryType no`, this implies that we are not using serviceDiscovery in the microservices. We will use Linkerd for servicediscovery.

> Did you know? JHipster supports `sql` and `noSql` database and it has many configuration options for generating the applications. Check the entire list of application configuration options [here](https://www.jhipster.tech/jdl/applications#available-application-configuration-options).

## Generate Kubernetes configuration

KHipster provides `kubernetes` option to generate the Kubernetes configuration file for the `JHipster` generated applications. Interested to explore more about Kubernetes check out [here](https://sendilkumarn.com/blog/kubernetes-for-everyone). The `jdl` accepts `deployment` block to define the Kubernetes deployment. 

```
deployment {
  deploymentType         kubernetes
  appsFolders            [gateway, service]
  dockerRepositoryName   "sendilkumarn"
  serviceDiscoveryType   no
  istio                  false
  kubernetesNamespace    khipster
  kubernetesServiceType  Ingress
}
```

> Note: It checks whether you have installed Docker since we need Docker for creating and pushing the images and use them later. 

The Kubernetes definition is made inside the `deployment` block with `deploymentType` as `kubernetes`. The `appsFolders` points to the folders of the generated applications. We define the `dockerRepositoryName`, this refers to docker user name here. Similar to applications, we define the `serviceDiscoveryType` as `no` (since we are going to use `Linkerd` for service discovery). KHipster provides `istio` out of the box, since we are going to use `Linkerd` for that. We define the Kubernetes namespace using `kubernetesNamespace` property. All the deployments & services use `khipster` namespace to deploy the application. 

Finally, We use `Ingress` as the `KubernetesServiceType` configuration. The smallest deployment unit of Kubernetes is a `pod`. A pod is a group of Docker containers. Refer here for more information about the [pods](https://sendilkumarn.com/blog/kubernetes-pods). Each pod gets IP address during its creation. If we have to access the application we have to connect to the pod's IP address (with port if applicable). In the Kubernetes cluster, pods are created and destroyed automatically based on various factors. So when a pod spawns newly, IP address referring them might change.In order to connect to running applications in the pods, we use services. Services define a set of pods and a way to access them. The services is like a loadbalancer or service discoverer that helps to identify the pods with a name to it. 

Kubernetes provide three types of service type:

- Cluster IP  - This exposes service to an cluster internal IP 
- Nodeport  - This exposes the service to an port 
- Load Balancer - This exposes the service to an external IP

With [Kube-DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/), Kubernetes also maps the service to an external name. Refer [here](https://kubernetes.io/docs/concepts/services-networking/service/) for more infomration.

Ingress is not a service type, but with Ingress you can expose the service. The ingress acts as an entry point for your cluster. It is ideal for exposing multiple services under the same IP address.

> We need `ingress-controller` to expose services via ingress IP, we will set up ingress-controller below.

## Generate code using KHipster

The `khipster import-jdl` imports the `JDL` file and generates the application and the deployment configuration files.

```
λ khipster import-jdl app.jdl

Gateway application generated successfully.
Service application generated successfully.
Kubernetes configuration generated successfully.
```

Login the docker using `docker login`. Now go and build each services in their respective folders:

```
λ ./gradlew bootJar -Pprod jib -Djib.to.image=<docker-username>:<app-name>
```

## Create a Kubernetes cluster in GCP

> Install gcloud [here](https://cloud.google.com/sdk/docs/install).

```
λ gcloud beta container --project <project-name> clusters create "khipster-cluster" --machine-type "c2-standard-4" --num-nodes "2" --zone "us-central1-c"
```

We create a cluster named `khipster-cluster` in the given project. We define the `machine-type` with `c2-standard-4` with 2 nodes in the `us-central1-c` zone. This will take a while to complete, since it has to create a cluster from the scratch.

> Explore more about K8s installation in the GCP [here](https://cloud.google.com/kubernetes-engine/docs/quickstart).

## Install Linkerd CLI and deploy Linkerd in Kubernetes cluster

> Linkerd is a `service mesh` for Kubernetes. Linkerd makes running services easier and safer by giving you runtime debugging, observability, reliability, and security—all without requiring any changes to your code. - [Linkerd website](https://linkerd.io/2/overview/)

Install the CLI manually in your system and set up the path, run:

```
λ curl -sL https://run.linkerd.io/install | sh
Linkerd stable-2.9.0 was successfully installed 🎉

λ export PATH=$PATH:$HOME/.linkerd2/bin
```

Check the installation by running:

```
λ linkerd version
Client version: stable-2.9.0
Server version: unavailable
```

Once the installation is completed. Before installing the Linkerd in the Kubernetes cluster, we wil check whether the Kubernetes cluster is configured correctly. We run:

```
λ linkerd check --pre
....
Status check results are √
```

> The above command checks whether the kubernetes cluster is configured correctly and ready to install the Linkerd control plane.

Note: The bare Kubernetes configuration might not have the required cluster admin role. To create the `clusterrolebinding` with `cluster-admin` role for the selected account.

```
λ kubectl create clusterrolebinding cluster-admin-binding \
  --clusterrole=cluster-admin \
  --user="$(gcloud config get-value core/account)"

clusterrolebinding.rbac.authorization.k8s.io/cluster-admin-binding created
```

To install the Linkerd on the Kubernetes cluster, run the following:

```
λ linkerd install | kubectl apply -f -
```

The `linkerd install` command creates the kubernetes configuration files. We pipe the configuration files using `kubectl apply -f ` command on those generated files. This will create the complete `linkerd` in the kubernetes cluster. Once the installation is complete refer the installation is complete using 

```
λ linkerd check
```

## Deploy ingress controller in Kubernetes

> Ingress exposes `HTTP` and `HTTPS` routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.

Ingress provides an external IP with which we connect to the services running inside the Kubernetes cluster. The Ingress also provides load balancing, SSL / TLS, and others. We will install a vanilla version Nginx ingress controller:

```
λ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/cloud/deploy.yaml
```

Once the ingress-controller is installed in the Kubernetes cluster. Get the IP address by running the following command:

```
λ kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
xxx.xxx.xxx.xxx
```

Our application will be accessible via the above IP address.

## Deploy applications in Kubernetes cluster

To deploy applications in to the Kubernetes cluster, go into the Kubernetes folder and run the following command:

```
λ ./kubectl-apply.sh apply -f
```

All the deployments and services will be installed in the K8s cluster. Now we need to auto inject Linkerd into the `khipster` namespace for all the services in that particular namespace. 

Next, let's add Linkerd to `khipster` namespace by running:

```
λ kubectl get -n khipster deploy -o yaml \
  | linkerd inject - \
  | kubectl apply -f -
```

> You can also inject proxy automatically. Refer [here](https://linkerd.io/2/features/proxy-injection/) for more information.

Now the Kubernetes cluster is ready with all the necessary configuration.

## Configure traffic to application 

Now the final step is to map the ingress service to the services. Note the `annotation` in the `metadata` section, the Nginx uses the proxy header to proxy the HTTP traffic. Refer [here](https://linkerd.io/2/tasks/using-ingress/) for more information.

```
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: web-ingress
  namespace: khipster
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;

spec:
  rules:
  - host: <ip-address>.nip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: gateway
          servicePort: 80
      - path: /service
        backend:
          serviceName: service
          servicePort: 8081
```

> <ip-address>.nip.io refer the nip.io at the end. nip.io helps to redirect check [here](https://nip.io/)

We have also defined the `spec` section. The spec section provides the rules for redirection. We provide the host and map the path to the service with their name. Viola! Now the application is ready! Go to `<ip-address>.nip.io` to check your microservices running. 

Linkerd comes with `dashboard` & `grafana` to monitor the services, traffic, and everything. You can check them out using `linkerd dashboard`.

---

[🐦 Twitter](https://twitter.com/sendilkumarn) // [💻 GitHub](https://github.com/sendilkumarn) // [✍️ Blog](https://sendilkumarn.com/blog)