Nov 29, 2020

Microservices with Kotlin, Kubernetes, and Istio

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 Istio CLI and deploy Istio in Kubernetes cluster
  • Deploy applications in Kubernetes cluster

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 Istio 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                  true
  kubernetesNamespace    khipster
  kubernetesServiceType  Ingress
  ingressDomain          "<ip-address>.nip.io"
}

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 Istio for service discovery). KHipster provides istio support out of the box, we set the value to true to use the istio configuration. 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.

Istio comes with an ingress-controller and exposes IP, we will use that to access our services. We provide the IP address using ingressDomain property.

Note: .nip.io refer the nip.io at the end. nip.io helps to redirect the URL- Refer here for more information.

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>

KHipster also uses maven as a build tool.

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 Istio CLI and deploy Istio in Kubernetes cluster

Istio is a service mesh for Kubernetes. Istio acts as a service discovery (connect), authenticate and authorize communication between services (secure), apply policies to services (control), collect telemetry and monitor services (observe) - Istio website

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

λ curl -L https://istio.io/downloadIstio | sh -

λ cd istio-1.8.0

λ export PATH=$PWD/bin:$PATH

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 Istio on the Kubernetes cluster, run the following:

λ istioctl install --set profile=demo -y

✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete

Istio supports various configuration profiles for installation. Check out their differences here. We are using the demo profile.

Once the installation is complete refer the installation is complete using:

λ kubectl get ns istio-system

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. Istio comes with an ingress gateway. Get the IP address by running the following command:

λ kubectl get svc istio-ingressgateway -n istio-system -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

The KHipster generates a set of configruation files for different services. They are as follows:

.
├── README.md
├── service
│   ├── service-deployment.yml
│   ├── service-destination-rule.yml
│   ├── service-mysql.yml
│   ├── service-service.yml
│   ├── service-virtual-service.yml
│   └── jwt-secret.yml
├── gateway
│   ├── gateway-deployment.yml
│   ├── gateway-destination-rule.yml
│   ├── gateway-gateway.yml
│   ├── gateway-mysql.yml
│   ├── gateway-service.yml
│   ├── gateway-virtual-service.yml
│   └── jwt-secret.yml
├── istio
│   ├── grafana-gateway.yml
│   ├── jaeger-gateway.yml
│   └── kiali-gateway.yml
└── kubectl-apply.sh

Each service has a separate folder and all the related configurations are placed inside the folder. For example:

The gateway service has a gateway-deployment.yml. The deployment file describes the desired state of the deployment, like where is the actual Docker image, how many replicas to run, when to scale the services, etc.,. The Deployment controller in the Kubernetes takes care of ensuring all the conditions defined in the deployment file is met.

The gateway-destination-rule.yml contains the network configuration related to Load Balancing, other connection related parameters, detect and evict unhealthy hosts using outlierDetection.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: gateway-destinationrule
  namespace: default
spec:
  host: gateway
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
    connectionPool:
      tcp:
        maxConnections: 30
        connectTimeout: 100ms
      http:
        http1MaxPendingRequests: 10
        http2MaxRequests: 100
        maxRequestsPerConnection: 10
        maxRetries: 5
    outlierDetection:
      consecutiveErrors: 5
      interval: 30s
      baseEjectionTime: 60s
  subsets:
    - name: v1
      labels:
        version: 'v1'

Inside the yml file, the spec contains all the necessary information. The host is the gateway service.

Next we define the traffic policy. The traffic polocy starts with the load balancer configuration. The load balancer can either be simple or consistent. Simple consists of a set of pre-configured load balancing configuration. By default simple supports:

  • RoundRobin
  • Least Conn
  • Random
  • Pass through

We have chosen Random here. The Istio team suggests that Random is better than the RoundRobin if we don't have any health configuration. It checks for the services that are healthy and routes the request to it. For more information on this — Check here for more information about these configuration.

Following the load balancer configuration, we have defined the connection pool. The connection pool instructs istio what is the time out, maximum pending requests, maximum number of requests, retries for failures etc.,. Finally we also specify the outlierDetection, this helps istio to mark a service as unhealthy when it gives 5 errors consecutively in an interval of 30s.

Check out here for various options that you can define inside the destination-rule here.

The gateway-gateway.yml contains configuration for Istio’s Ingress gateway here. The gateway sit at the edge of the service mesh created by the Istio. It configures exposed ports and protocols and helps to connect to the underlying services. In our case the gateway exposes port 80 for http and http2 connections.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: gateway-gateway
  namespace: default
  labels:
    gateway: gateway-gateway
    istio: ingressgateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - gateway.khipster.<ingress-ip>.nip.io
    - port:
        number: 80
        name: http2
        protocol: HTTP2
      hosts:
        - gateway.khipster.<ingress-ip>.nip.io

Refer here for more information and options about the gateway configuration.

Note the host name, it consists of the service name, kubernetes namespace where we will deploy them, followed by nip.io for redirection.

In addition to defining the gateway, we also added in a Virtual Service that exposes the gateway based on the routing.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: gateway-gw-virtualservice
  namespace: default
  labels:
    service: gateway-gw-virtualservice
spec:
  hosts:
    - gateway.khipster.<ingress-ip>.nip.io
  gateways:
    - gateway-gateway
  http:
    - match:
        - uri:
            prefix: /application1/
      route:
        - destination:
            host: application1
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: gateway

Refer here for more information and configuration options available for Virtual Service.

The gateway-mysql.yml includes all the necessary MySQL database configuration. The gateway-service.yml will abstract the gateway application as a service. The gateway-virtual-service.yml as the name indicates, is a virtual service for the gateway-service. The jwt-secret.yml consist of JSON Web Token secret for the application. We will have the same set of configuration for all the services for which we are generating the Kubernetes configuration.

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

./kubectl-apply.sh apply -f

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

Viola! Now the application is ready! Go to <ip-address>.nip.io to check your microservices running.

Istio comes with kiali, jaeger & grafana. Kiali helps to visualize the service mesh refer here for more information about Kiali. Refer here for what you can achieve with Kiali and Istio. Jaeger is the distributed tracing system, that helps to identify the your service architecture and its bottle necks. Refer here for more information. Finally grafana to visualize everything.