Intro to OpenShift Service Mesh

Intro to OpenShift Service Mesh

OpenShift 4 has introduced official support for service mesh based on the Istio framework. This support is built on top of Maistra operator. Maistra is an opinionated distribution of Istio designed to work with Openshift. It combines Kiali, Jaeger, and Prometheus into a platform managed by the operator. The current version of OpenShift Service Mesh is 1.1.5. According to the documentation, this version of the service mesh supports Istio 1.4.8. Also, for creating this tutorial I was using OpenShift 4.4 installed on Azure.

In this article, I will not explain the basics of Istio framework. If you do not have experience in using Istio for building service mesh on Kubernetes you may refer to my article Service Mesh on Kubernetes with Istio and Spring Boot.

1. Install OpenShift Service Mesh operators

OpenShift 4 provides extensive support for Kubernetes operators. You can install them using OpenShift Console. To do that you must navigate to Operators -> Operator Hub, and then find Red Hat OpenShift Service Mesh. You can install some other operators to enable integration between the service mesh and additional components like Jeager, Prometheus, or Kiali. In this tutorial, we are going to discuss the Kiali component. That’s why we have to search and install Kiali Operator.

kiali

2. OpenShift Service mesh configuration

By default, Istio is always installed inside istio-system namespace. Although you can install it on OpenShift in any project, we will use istio-system – according to the best practices. Once the operator is installed inside istio-system namespace we may create Service Mesh Control Plane. The only component that will be enabled is Kiali.

istio-controlplane

In the next step, we are going to create Service Mesh Member Roll and Service Mesh Member components. This time we have to prepare the YAML manifest. It is important to start from a ServiceMeshMemberRoll object. In this object, we should define a list of projects being a part of our service mesh. Currently, there is the only project – microservices.

apiVersion: maistra.io/v1
kind: ServiceMeshMemberRoll
metadata:
  name: default
  namespace: istio-system
spec:
  members:
    - microservices

In the ServiceMeshMember definition, it is important to set the right namespace for the control plane. In our case that is istio-system namespace.

apiVersion: maistra.io/v1
kind: ServiceMeshMember
metadata:
  name: default
spec:
  controlPlaneRef:
    name: basic-install
    namespace: istio-system

Finally, we can take a look at the configuration of OpenShift Service Mesh Operator inside istio-system project.

openshift-service-mesh-configuration

Here’s the list of deployments inside istio-system namespace after installation of OpenShift Service Mesh.

openshift-service-mesh-deployments

3. Deploy applications on OpenShift

Let’s switch to the microservices namespace. We will deploy our example microservices that communicate with each other. Each application would be deployed in two versions. We are using the same codebase, so each version would be distinguished based on the label version. Labels may be injected into the container using DownwardAPI.
The most important thing in the following Deployment definition is annotation sidecar.istio.io/inject. It is responsible for enabling Istio sidecar injection for the application. Other applications and their versions have a similar deployment manifest structure.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: department-deployment-v1
spec:
  selector:
    matchLabels:
      app: department
      version: v1
  template:
    metadata:
      labels:
        app: department
        version: v1
	  annotations:
        sidecar.istio.io/inject: "true"
    spec:
      containers:
      - name: department
        image: piomin/department-service
        ports:
        - containerPort: 8080
        volumeMounts:
          - mountPath: /etc/podinfo
            name: podinfo
      volumes:
        - name: podinfo
          downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels

The sample system consists of three microservices: employee-service, department-service, and organization-service. The source code of those applications is available on GitHub within the repository https://github.com/piomin/course-kubernetes-microservices/tree/openshift/simple-microservices. Each application is built on top of Spring Boot, and uses the H2 database as an in-memory data store. You can build and deploy them on OpenShift using Skaffold (by executing command skaffold dev), which manifest is configured inside the repository (skaffold.yaml).

I won’t describe the implementation details about example applications. They are written in Kotlin, and use OpenJDK as a base image. If you are interested in more detailed pieces of information you may watch two parts of my online course Microservices on Kubernetes: Inter-communication & gateway, and Microservices on Kubernetes: Service mesh.

Here’s a list of applications deployed inside project microservices.

openshift-service-mesh-apps

4. Istio configuration

After running all the sample applications we may proceed to the Istio configuration. Because each application is deployed in two versions, we will define DestinationRule component that defines a list of two subsets per application based on the value of the version label. Here’s the example DestinationRule for employee-service.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: employee-service-destination
spec:
  host: employee-service.microservices.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

Now, we may proceed to the definition of VirtualService. Routing between different versions of the application will be based on the value of HTTP header X-Version.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: employee-service-route
spec:
  hosts:
    - employee-service.microservices.svc.cluster.local
  http:
    - match:
        - headers:
            X-Version:
              exact: v1
      route:
        - destination:
            host: employee-service.microservices.svc.cluster.local
            subset: v1
    - match:
        - headers:
            X-Version:
              exact: v2
      route:
        - destination:
            host: employee-service.microservices.svc.cluster.local
            subset: v2
    - route:
        - destination:
            host: employee-service.microservices.svc.cluster.local
            subset: v1

The similar YAML manifests will be prepared for other microservices: department-service and organization-service.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: department-service-destination
spec:
  host: department-service.microservices.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

And VirtualService for department-service.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: department-service-route
spec:
  hosts:
    - department-service.microservices.svc.cluster.local
  http:
    - match:
        - headers:
            X-Version:
              exact: v1
      route:
        - destination:
            host: department-service.microservices.svc.cluster.local
            subset: v1
    - match:
        - headers:
            X-Version:
              exact: v2
      route:
        - destination:
            host: department-service.microservices.svc.cluster.local
            subset: v2
    - route:
        - destination:
            host: department-service.microservices.svc.cluster.local
            subset: v1

The whole configuration created until now was responsible for internal communication. Now, we will expose our application outside the OpenShift cluster. To do that we need to create Istio Gateway. It is referencing the Service called ingressgateway available in the namespace istio-system. That service is exposed outside the cluster using OpenShift Route.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: microservices-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"

Let’s take a look at the list of available routes. Besides Istio Gateway we can also access Kiali console outside the OpenShift cluster.

mesh-routes

The last thing we need to do is to create Istio virtual services responsible for routing from Istio gateway to the applications. The configuration is pretty similar to the internal virtual services. The difference is that it is referencing the Istio Gateway and performs routing to the downstream services basing on path prefix. If the path starts with /employee the request is forwarded toemployee-service etc. Here’s the configuration for employee-service. The similar configuration has been prepared for both department-service, and organization-service.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: employee-service-gateway-route
spec:
  hosts:
    - "*"
  gateways:
    - microservices-gateway
  http:
    - match:
        - headers:
            X-Version:
              exact: v1
          uri:
            prefix: "/employee"
      rewrite:
        uri: " "
      route:
        - destination:
            host: employee-service.microservices.svc.cluster.local
            subset: v1
    - match:
        - uri:
            prefix: "/employee"
          headers:
            X-Version:
              exact: v2
      rewrite:
        uri: " "
      route:
        - destination:
            host: employee-service.microservices.svc.cluster.local
            subset: v2

5. Test requests

The default hostname for my OpenShift cluster is istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io. To add some test data I’m sending the following requests.

$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/department/departments -d "{\"name\":\"Test1\"}" -H "Content-Type: application/json" -H "X-Version:v1"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/department/departments -d "{\"name\":\"Test1\"}" -H "Content-Type: application/json" -H "X-Version:v2"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/organization/organizations -d "{\"name\":\"Test1\"}" -H "Content-Type: application/json" -H "X-Version:v1"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/organization/organizations -d "{\"name\":\"Test1\"}" -H "Content-Type: application/json" -H "X-Version:v2"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/employee/employees -d "{\"firstName\":\"John\",\"lastName\":\"Smith\",\"position\":\"director\",\"organizationId\":1,\"departmentId\":1}" -H "Content-Type: application/json" -H "X-Version:v1"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/employee/employees -d "{\"firstName\":\"Paul\",\"lastName\":\"Walker\",\"position\":\"architect\",\"organizationId\":1,\"departmentId\":1}" -H "Content-Type: application/json" -H "X-Version:v1"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/employee/employees -d "{\"firstName\":\"John\",\"lastName\":\"Smith\",\"position\":\"director\",\"organizationId\":1,\"departmentId\":1}" -H "Content-Type: application/json" -H "X-Version:v2"
$ curl -X POST http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/employee/employees -d "{\"firstName\":\"Paul\",\"lastName\":\"Walker\",\"position\":\"architect\",\"organizationId\":1,\"departmentId\":1}" -H "Content-Type: application/json" -H "X-Version:v2"

Now, we can test internal communication between microservices. The following requests verifies communication between department-service, and employee-service.

$ curl http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/department/departments/1/with-employees -H "X-Version:v1"
$ curl http://istio-ingressgateway-istio-system.apps.np9zir0r.westeurope.aroapp.io/department/departments/1/with-employees -H "X-Version:v2"

6. Kiali

Finally, we access Kiali to take a look at the communication diagram. We had to generate some test traffic before.

openshift-service-mesh-kiali

Kiali allows us to verify Istio configuration. For example we may see the list of virtual services per project.

openshift-service-mesh-kiali-services

We may also take a look on the details of each VirtualService.

openshift-service-mesh-kiali-service

Leave a Reply