The goal of these use cases is to test and compare different service mesh implementations.
To run the different demos, you need to have a running Kubernetes cluster with an access to it via the kubectl
CLI, as well as Open Service Mesh running (OSM) on top of it. You can follow these two documents to get a proper environment:
Let's have Alpha API
an API with two API endpoints:
get /path-01
get /path-02
Let's have client-x
a service calling these two API endpoints.
Let's have service-a
a service implementing the two API endpoints of Alpha API
. More precisely, let's have the version 1.0.0
of service-a
implementing these two API endpoints.
WARNING. The client-x
MUST NOT know the implementation details of the Alhpa API
, and that includes the service which implements it. So, we could add an additional network name like aplha-api
that would represent the API. However, as the API endpoints such as get /path-01
and get /path-02
are distinct, we can have only one unique network name such as acme-api
. If, and only if, we would have a collision about two or more different API endpoints having the same name, then, and only then, we can still use a different network name such as <differentiator>.acme-api
.
The Kubernetes manifests of the above situation can be written as follow:
# uc-02.01.yaml
---
# Deploy 'demo' Namespace
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
# Deploy 'service-a' Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: service-a
namespace: demo
---
# Deploy 'service-a-version-1-0-0-deployment' Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a-version-1-0-0-deployment
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: service-a
version: 1.0.0
api: acme-api
template:
metadata:
labels:
app: service-a
version: 1.0.0
api: acme-api
spec:
serviceAccountName: service-a
containers:
- name: service-a
image: patrice1972/service-a:1.0.0
ports:
- name: service-a-port
protocol: TCP
containerPort: 3000
---
# Deploy 'service-a-version-1-0-0' Service
apiVersion: v1
kind: Service
metadata:
name: service-a-version-1-0-0
namespace: demo
spec:
selector:
app: service-a
version: 1.0.0
ports:
- protocol: TCP
port: 3000
targetPort: 3000
---
# Deploy 'acme-api' Service
apiVersion: v1
kind: Service
metadata:
name: acme-api
namespace: demo
spec:
selector:
api: acme-api
ports:
- protocol: TCP
port: 3000
targetPort: 3000
---
# Deploy 'client-x' Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: client-x
namespace: demo
---
# Deploy 'client-x-version-1-0-1-deployment' Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-x-version-1-0-1-deployment
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: client-x
version: 1.0.1
template:
metadata:
labels:
app: client-x
version: 1.0.1
spec:
serviceAccountName: client-x
containers:
- name: client-x
image: patrice1972/client-x:1.0.1
env:
- name: API_HOST
value: "acme-api.demo"
---
# Deploy 'client-x-version-1-0-1' (dummy) Service [Required by OSM]
apiVersion: v1
kind: Service
metadata:
name: client-x-version-1-0-1
namespace: demo
spec:
selector:
app: client-x
version: 1.0.1
ports:
- protocol: TCP
port: 9999
name: dummy-unused-port
You can deploy them using the following command:
$ kubectl apply -f https://raw.githubusercontent.com/patricekrakow/service-mesh-use-cases/master/uc-02.01.yaml
or
$ kubectl apply -f uc-02.01.yaml
Output of the command
namespace/demo created
serviceaccount/service-a created
deployment.apps/service-a-version-1-0-0-deployment created
service/service-a-version-1-0-0 created
service/acme-api created
serviceaccount/client-x created
deployment.apps/client-x-version-1-0-1-deployment created
service/client-x-version-1-0-1 created
Then, you can verify that the installation of the demo using the following command:
$ kubectl get pods -n demo
Output of the command
NAME READY STATUS RESTARTS AGE
client-x-version-1-0-1-deployment-7db9cf495b-5cmc6 1/1 Running 0 49s
service-a-version-1-0-0-deployment-5d4fbcc598-22pj6 1/1 Running 0 49s
Finally, you can verify that the demo is working properly using the following command:
$ kubectl logs client-x-version-1-0-1-deployment-7db9cf495b-5cmc6 -n demo | tail
Output of the command
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-22pj6
You can see that the client-x
while addressing the network name acme-api
is getting replies from the version 1.0.0.
of the service-a
.
Let's now add the service mesh.
Add the OSM CLI to your path using the following command:
$ export PATH="$HOME/environment/go/src/github.com/openservicemesh/osm/bin:$PATH"
$ osm version
Output of the command
Version: dev; Commit: 930991cbc4a211c43c15195a7a4c7d2c867ddf05; Date: 2020-08-25-09:22
Install the service mesh using the following command:
$ osm install --osm-image-tag 930991cbc4a211c43c15195a7a4c7d2c867ddf05
Output of the command
OSM installed successfully in namespace [osm-system] with mesh name [osm]
We can then check the installation of OSM using the following command:
$ kubectl get pods --namespace osm-system
Output of the command
NAME READY STATUS RESTARTS AGE
jaeger-6864b858c5-mwc24 1/1 Running 0 116s
osm-controller-8554b44bd4-lvvpp 1/1 Running 0 116s
osm-grafana-fdc677699-r8j2x 1/1 Running 0 116s
osm-prometheus-6cdf59c56f-5s5gg 1/1 Running 0 116s
Let's onboard the services of our demo to the service mesh via the Kubernetes namespace using the following command:
$ osm namespace add demo
Output of the command
Namespace [demo] successfully added to mesh [osm]
We also need to delete the pods so they get re-created them with the sidecar proxy injected:
$ kubectl delete pods --all -n demo
Output of the command
pod "client-x-version-1-0-1-deployment-7db9cf495b-5cmc6" deleted
pod "service-a-version-1-0-0-deployment-5d4fbcc598-22pj6" deleted
Looking back at the pods using the following command:
$ kubectl get pods -n demo
Output of the command
NAME READY STATUS RESTARTS AGE
client-x-version-1-0-1-deployment-7db9cf495b-8t7xs 2/2 Running 0 92s
service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv 2/2 Running 0 92s
We can now see that there are two containers per pod, because of the injection of the sidecar proxies.
And, the demo, as expected, does not work anymore:
$ kubectl logs client-x-version-1-0-1-deployment-7db9cf495b-8t7xs client-x -n demo | tail
Output of the command
[INFO] get /path-02 | 404
[INFO] get /path-01 | 404
[INFO] get /path-02 | 404
[INFO] get /path-01 | 404
[INFO] get /path-02 | 404
[INFO] get /path-01 | 404
[INFO] get /path-02 | 404
[INFO] get /path-01 | 404
[INFO] get /path-02 | 404
[INFO] get /path-01 | 404
We need to explicitly allow the traffic from client-x
to service-a
using TrafficTarget
SMI configuration:
# uc-02.01.smi.yaml
---
apiVersion: specs.smi-spec.io/v1alpha3
kind: HTTPRouteGroup
metadata:
name: alpha-api-routes
namespace: demo
spec:
matches:
- name: get-path-01
pathRegex: /path-01
methods:
- GET
- name: get-path-02
pathRegex: /path-02
methods:
- GET
---
# Deploy the 'allow-client-x-to-service-a-through-alpha-api-routes' TrafficTarget
kind: TrafficTarget
apiVersion: access.smi-spec.io/v1alpha2
metadata:
name: allow-client-x-to-service-a-through-alpha-api-routes
namespace: demo
spec:
destination:
kind: ServiceAccount
name: service-a
namespace: demo
port: 3000
rules:
- kind: HTTPRouteGroup
name: alpha-api-routes
matches:
- get-path-01
- get-path-02
sources:
- kind: ServiceAccount
name: client-x
namespace: demo
$ kubectl apply -f uc-02.01.smi.yaml
Output of the command
httproutegroup.specs.smi-spec.io/alpha-api-routes created
traffictarget.access.smi-spec.io/allow-client-x-to-service-a-through-alpha-api-routes created
Let's have a look back at the log of client-x
:
$ kubectl logs client-x-version-1-0-1-deployment-7db9cf495b-8t7xs client-x -n demo | tail
Output of the command
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
We can see that client-x
can reach back service-a
.
Let's have the version 1.0.0
of service-b
implementing the second API endpoint – get /path-02
.
The Kubernetes manifests of the above situation can be written as follow:
# uc-02.02.yaml
---
# Deploy 'service-b' Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: service-b
namespace: demo
---
# Deploy 'service-b-version-1-0-0-deployment' Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b-version-1-0-0-deployment
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: service-b
version: 1.0.0
api: acme-api
template:
metadata:
labels:
app: service-b
version: 1.0.0
api: acme-api
spec:
serviceAccountName: service-b
containers:
- name: service-b
image: patrice1972/service-b:1.0.0
ports:
- name: service-b-port
protocol: TCP
containerPort: 3000
---
# Deploy 'service-b-version-1-0-0' Service
apiVersion: v1
kind: Service
metadata:
name: service-b-version-1-0-0
namespace: demo
spec:
selector:
app: service-b
version: 1.0.0
ports:
- protocol: TCP
port: 3000
targetPort: 3000
You can deploy them using the following command:
$ kubectl apply -f https://raw.githubusercontent.com/patricekrakow/service-mesh-use-cases/master/uc-02.02.yaml
or
$ kubectl apply -f uc-02.02.yaml
Output of the command
serviceaccount/service-b created
deployment.apps/service-b-version-1-0-0-deployment created
service/service-b-version-1-0-0 created
Then, you can verify that the installation of the demo using the following command:
$ kubectl get pods -n demo
Output of the command
NAME READY STATUS RESTARTS AGE
client-x-version-1-0-1-deployment-7db9cf495b-8t7xs 2/2 Running 0 17m
service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv 2/2 Running 0 17m
service-b-version-1-0-0-deployment-5b44559c86-nrmgw 2/2 Running 0 38s
Now, if you look at how the demo is working using the following command:
kubectl logs client-x-version-1-0-1-deployment-7db9cf495b-8t7xs client-x -n demo | tail
Output of the command
[INFO] get /path-01 | 404
[INFO] get /path-02 | Hello from get /path-02 | service-b (1.0.0) | service-b-version-1-0-0-deployment-5b44559c86-nrmgw
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | 404
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | 404
[INFO] get /path-02 | Hello from get /path-02 | service-b (1.0.0) | service-b-version-1-0-0-deployment-5b44559c86-nrmgw
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
I cannot know if the
404
is coming fromservice-b
or from the sidecar proxies. However it's weird to get response fromservice-b
since we don't have anyTrafficTarget
configuration forservice-b
.
You will see that the client-x
does not always get a reply when calling get /path-01
which corresponds to the situation when it get directed to service-b
.
We need to configure the mesh to make sure that the route to get /path-01
only goes to service-a
while the route to get /path-02
can go to both service-a
and service-b
.
# uc-02.02.smi.yaml
---
apiVersion: specs.smi-spec.io/v1alpha3
kind: HTTPRouteGroup
metadata:
name: alpha-api-routes
namespace: demo
spec:
matches:
- name: get-path-01
pathRegex: /path-01
methods:
- GET
- name: get-path-02
pathRegex: /path-02
methods:
- GET
---
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: get-path-01-traffic
namespace: demo
spec:
service: acme-api
matches:
- kind: HTTPRouteGroup
name: get-path-01
backends:
- service: service-a-version-1-0-0
weight: 100
- service: service-b-version-1-0-0
weight: 0
---
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: get-path-02-traffic
namespace: demo
spec:
service: acme-api
matches:
- kind: HTTPRouteGroup
name: get-path-02
backends:
- service: service-a-version-1-0-0
weight: 90
- service: service-b-version-1-0-0
weight: 10
You can deploy them using the following command:
$ kubectl apply -f https://raw.githubusercontent.com/patricekrakow/service-mesh-use-cases/master/uc-02.02.smi.yaml
or
$ kubectl apply -f uc-02.02.smi.yaml
Output of the command
httproutegroup.specs.smi-spec.io/alpha-api-routes unchanged
trafficsplit.split.smi-spec.io/get-path-01-traffic created
trafficsplit.split.smi-spec.io/get-path-02-traffic created
Now, you can verify that the demo is working properly using the following command:
$ kubectl logs client-x-version-1-0-1-deployment-7db9cf495b-8t7xs client-x -n demo | tail
Output of the command
It does NOT always work... yet :-(
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | 503
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | Hello from get /path-01 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv
[INFO] get /path-01 | 503
[INFO] get /path-02 | 503
[INFO] get /path-01 | 503
[INFO] get /path-02 | Hello from get /path-02 | service-a (1.0.0) | service-a-version-1-0-0-deployment-5d4fbcc598-j6jzv