Merging VirtualServices in an Istio Sidecar


We hit an issue with one of our customer deployments. They use path-based routing between Microservices. In their VM infrastructure, all Ingress traffic and traffic between Applications themselves were routed via central LoadBalancer which was directing traffic based on URL path.
As part of the migration to Kubernetes and Istio, the role of LoadBalancer naturally falls to Istio Ingress Gateway. The deployment model uses Helm. Deployment of each application creates its own Service and VirtualService. Each VirtualService contains all URL Paths for an Application and bounds to Ingress Gateway to route external traffic into the Cluster. When Applications inside the Cluster call each other – they still need to communicate via Ingress Gateway since they are unaware of the routing mechanism. This makes Ingress Gateway a very hot spot.
Fortunately, Istio offers the very nice feature of an “internal” gateway that can be declared on VirtualService using the keyword “mesh” https://istio.io/latest/docs/reference/config/networking/virtual-service/. This functionality allows to route traffic between pods directly Pod to Pod by creating necessary Egress Proxy configuration on Envoy sidecars.
Unfortunately “mesh” gateway does not support the merging of VirtualServices and only a single VirtualService can be used for Envoy Egress configuration. There is a feature request to enable this functionality https://github.com/istio/istio/issues/22997
To address the problem without the Istio feature we needed to either create our own way of merging VirtualServices on “mesh” Gateway or declare one overarching VirtualService which will include all individual Application VirtualServices.
A very neat workaround is provided in the same thread https://github.com/monimesl/istio-virtualservice-merger. Even though the code is very straightforward we couldn’t use this solution due to the policy of not taking any code which doesn’t come from the vendor or another reputable source.
We dismissed the option of restructuring the deployments pipeline to configure a single VirtualService for all Applications as it would create too much repetition and custom scripting.
An alternative approach we have found by using the “lookup” function of Helm is to create an overarching VirtualService on the fly. The only requirement from the pipeline was to make sure that deployment of an overarching VirtualService will happen last when all services are already deployed.
A very simple Helm Chart with a single template will let us do it
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: internal-routing
namespace: {{ .Release.Namespace }}
spec:
gateways:
- mesh
hosts:
- {{ .Values.internalIngressGateway}}
http:
{{ range $service := (lookup "networking.istio.io/v1beta1" "VirtualService" .Release.Namespace "").items }}
{{ if ne $service.metadata.name "internal-routing" }} #don't forget to exclude itself
{{ toYaml $service.spec.http |nindent 8 }}
{{ end }}
{{ end }}
This way each Application continues to be deployed on its own and creates VirtualService to route external traffic. The overarching VirtualService will be bound to “mesh” Gateway and collect all existing VirtualServices routes. As long as this Helm Chart is deployed last, it will pick up all existing VirtualServices and enable service to service communications in the namespace.
- What is TCP Proxy Protocol and why do you need to know about it? - March 30, 2023
- Highlights of OpenUK Conference in London - February 13, 2023
- Applied Observability - January 25, 2023