Service Mesh by example – how we did it

In this post we will look at a simple way to test Istio out. This was our first step towards implementing Istio. If you wonder why we chose to try it out, have a look at the previous post, Why we chose to try a Service Mesh on Openshift.

Talking about Istio Service Mesh here, we are actually talking about a product called Maistra, or as it is called in RedHat Openshift, OpenShift Service Mesh. Maistra is Istio with slight adjustments to better fit with OpenShift. That could mean that some features are not equal to their real Istio counterparts. It still has the same CRDs for configuring the applications although the control plane setup has its own CRDs and differs a little bit. For more details, please look at this link: From now on, we will mostly use Istio as the name for simplicity.

Setting up a basic Istio Service Mesh controlplane on OpenShift

First step is to install the OpenShift Service Mesh operator. Could look something like this.

kind: Subscription
  channel: stable
  installPlanApproval: Automatic
  name: servicemeshoperator
  source: redhat-operators
  sourceNamespace: openshift-marketplace

Next is the Service Mesh Control Plane. This is where we configured it.

kind: ServiceMeshControlPlane
  name: my-istio-controllplane
  namespace: my-istio-namespace
    ingress: #1
      enabled: true
        type: LoadBalancer
        loadBalancerIP: a.b.c.d #2
        externalTrafficPolicy: Local
      enabled: false #3
    type: Jaeger
    sampling: 10000
        name: /dev/stdout #4
          policy: REGISTRY_ONLY #5
      name: jaeger
          type: Memory
            maxTraces: 100000 #6
      enabled: true
      name: kiali
      enabled: true

Let’s go through this a little bit.

  1. Here we specify that we want an ingress gateway object. The configuration of the gateway comes later, but this part includes the service annotations needed for creating a LoadBalancer resource. In case we wanted to use a specific egress-gateway to control outbound source-ip we would do it after this section.
  2. We have been running this on Google Cloud and there we needed to create an IP resource first. Then when using this configuration, a cloud load balancer resource will be created and linked to the gateway.
  3. For reasons not important for this article we didn’t want to go through the router.
  4. By specifying this access log config. All istio sidecars produce access logs to stdout which can normally be picked up by the log aggregator automatically.
  5. This setting is good if you want to control your outbound traffic. By setting this, no traffic is allowed out of the Service Mesh without first being registered. We will see an example of this later.
  6. We chose to install Istio with a simple all-in-one ephemeral solution for Jaeger tracing that keeps a number of traces in memory. If tracing is a vital part of troubleshooting far back in time, or if there is a lot of traffic, a better option is to use a persistent backend for traces.

Adding OpenShift namespaces to the Service Mesh

The Istio components are usually installed in their own namespace. For other namespaces to be able to participate in the Service Mesh, they must be made members. This can be done in two ways. Either by a resource in the istio-namespace called ServiceMeshMemberRoll that names all included namespaces. Or as a ServiceMeshMember object in each member namespace that names the Service Mesh. The first option can look something like this:

kind: ServiceMeshMemberRoll
name: default
namespace: my-istio-namespace
- my-application-namespace

This creates the necessary network policies to allow my-application-namespace to join the service mesh.

Configuring the Service Mesh Gateway in OpenShift

Now it is time to create the ingress gateway. It could look something like this.

kind: Gateway
  name: my-gateway
  namespace: my-istio-namespace
    istio: ingressgateway
  - port: #1
      number: 80
      name: http
      protocol: HTTP
      httpsRedirect: true # sends 301 redirect for http requests
  - port: #2
      number: 443
      name: https-443
      protocol: HTTPS
    hosts: #3
      mode: SIMPLE # enables HTTPS on this port
      credentialName: my-istio-ingress-cert-secret #4
  - port: #5
      number: 443 #6
      name: https-other-443
      protocol: HTTPS
      mode: SIMPLE # enables HTTPS on this port
      credentialName: my-istio-ingress-other-secret #7

Here we do a couple of things

  1. We could optionally listen to port 80 automatically redirecting to https. All hosts that should be available for redirect should be listed.
  2. We configure the first https port specifying the port number and giving it a name.
  3. We list all hosts that share the same certificate.
  4. We point to the secret name that contains the certificates
  5. If we have more hosts or ports we could specify another object.
  6. The port number may be repeated, but the name of the port must be unique.
  7. We point to the other secret containing the certificate for this hostname.

We used Let’s Encrypt to generate the secret containing the certificates. If using other ways, just provide a secret with the keys “tls.crt” and “tls.key” in pem-format.

This concludes the basic setup of the Mesh itself. Let’s move on to the workload configuration.

Exposing an application through the Service Mesh

Assuming we have an application called my-app running in my-application-namespace. We want that application to be exposed under

We would need to provide a VirtualService for the application.

kind: VirtualService
  name: my-app
  - my-istio-namespace/my-istio-gateway #1
  - #2
  - match: #3
    - uri:
        regex: "/my-app/(rest)/?.*"
    - uri:
        prefix: "/my-app/health"
    - destination:
        host: my-app #4
    corsPolicy: #5
        - regex: "https?://(.*)"
        - regex: "https?://localhost.*"
        - GET
        - POST
        - PUT
        - PATCH
        - DELETE
        - '*'

Some explanations.

  1. We name the gateway that we want to use with namespace and name.
  2. We specify the hostname that we want to use.
  3. Here we specify which matching-rules to use for this service. So /my-app/health or anything under /my-app/rest/ would be matched in this case.
  4. The name of the destination service object.
  5. An optional cors-policy that would automatically be applied without having to implement it in the application.

That’s it, now the application is available.

What if you want the application to use some external service?

Allowing external traffic from the Service Mesh

Since we specified in the Mesh config that we only wanted to allow registered services. If the application was trying to access lets say, that would be denied. The only thing we need to do to allow it is to add a ServiceEntry. Like this.

kind: ServiceEntry
  name: google
  - #1
  - number: 443 #2
    name: https-port
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL
  - my-application-namespace #3
  1. The host(s) that should be allowed
  2. The port(s) that should be allowed
  3. By creating this resource in the my-istio-namespace and exporting it only to namespaces that need to access it. You can further limit who can reach the external address.


Although this is only a very basic setup of the mesh, it is doing its job. We found it to be very simple to choose which services to expose and not, and also be fine-grained about what paths we wanted to expose. The whole setup so far is a rather limited number of objects to keep track of and it felt easy to get going. The hardest parts were related to understanding what actually happens under the hood. Even though it is great when things are really easy to use, we tend to want to know how it works as well.

We have really enjoyed trying Istio out so far, but stay tuned for the last part of this article series where we will write a little about our thoughts after using Istio for some time. Was it worth it?

Daniel Oldgren
Solution Architect