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. 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.
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
spec:
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.
apiVersion: maistra.io/v2
kind: ServiceMeshControlPlane
metadata:
name: my-istio-controllplane
namespace: my-istio-namespace
spec:
gateways:
ingress: #1
enabled: true
service:
type: LoadBalancer
loadBalancerIP: a.b.c.d #2
externalTrafficPolicy: Local
openshiftRoute:
enabled: false #3
tracing:
type: Jaeger
sampling: 10000
proxy:
accessLogging:
file:
name: /dev/stdout #4
networking:
trafficControl:
outbound:
policy: REGISTRY_ONLY #5
addons:
jaeger:
name: jaeger
install:
storage:
type: Memory
memory:
maxTraces: 100000 #6
kiali:
enabled: true
name: kiali
grafana:
enabled: true
Let’s go through this a little bit.
- 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.
- 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.
- For reasons not important for this article we didn’t want to go through the router.
- 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.
- 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.
- 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:
apiVersion: maistra.io/v1
kind: ServiceMeshMemberRoll
metadata:
name: default
namespace: my-istio-namespace
spec:
members:
- 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.
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
namespace: my-istio-namespace
spec:
selector:
istio: ingressgateway
servers:
- port: #1
number: 80
name: http
protocol: HTTP
hosts:
- my.external.hostname.example.com
- my-second.external.hostname.example.com
- my-other.external.hostname.example.com
tls:
httpsRedirect: true # sends 301 redirect for http requests
- port: #2
number: 443
name: https-443
protocol: HTTPS
hosts: #3
- my.external.hostname.example.com
- my-second.external.hostname.example.com
tls:
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
hosts:
- my-other.external.hostname.example.com
tls:
mode: SIMPLE # enables HTTPS on this port
credentialName: my-istio-ingress-other-secret #7
Here we do a couple of things
- We could optionally listen to port 80 automatically redirecting to https. All hosts that should be available for redirect should be listed.
- We configure the first https port specifying the port number and giving it a name.
- We list all hosts that share the same certificate.
- We point to the secret name that contains the certificates
- If we have more hosts or ports we could specify another object.
- The port number may be repeated, but the name of the port must be unique.
- 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 my.external.hostname.example.com/my-app.
We would need to provide a VirtualService for the application.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
spec:
gateways:
- my-istio-namespace/my-istio-gateway #1
hosts:
- my.external.hostname.example.com #2
http:
- match: #3
- uri:
regex: "/my-app/(rest)/?.*"
- uri:
prefix: "/my-app/health"
route:
- destination:
host: my-app #4
corsPolicy: #5
allowOrigins:
- regex: "https?://(.*).example.com"
- regex: "https?://localhost.*"
allowMethods:
- GET
- POST
- PUT
- PATCH
- DELETE
allowHeaders:
- '*'
Some explanations.
- We name the gateway that we want to use with namespace and name.
- We specify the hostname that we want to use.
- 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.
- The name of the destination service object.
- 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 “google.com”, that would be denied. The only thing we need to do to allow it is to add a ServiceEntry. Like this.
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: google
spec:
hosts:
- www.google.com #1
ports:
- number: 443 #2
name: https-port
protocol: HTTPS
resolution: DNS
location: MESH_EXTERNAL
exportTo:
- my-application-namespace #3
- The host(s) that should be allowed
- The port(s) that should be allowed
- 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.
Conclusions
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?
Author:
Daniel Oldgren
Solution Architect