Nov 28, 2020

Microservices with Kotlin, Kubernetes, and Linkerd

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.

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.

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. 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. 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, Kubernetes also maps the service to an external name. Refer here 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.

λ 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.

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

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 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 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

.nip.io refer the nip.io at the end. nip.io helps to redirect check here

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.


Up Next