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.
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.
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.
Here’s the list of deployments inside istio-system
namespace after installation of OpenShift Service Mesh.
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
.
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.
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.
Kiali allows us to verify Istio configuration. For example we may see the list of virtual services per project.
We may also take a look on the details of each VirtualService
.
Leave a Reply