Is there a way to prevent a VirtualService from routing to empty/unhealthy destinations?
For example, consider the following VirtualService and DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpy
spec:
hosts:
- httpy
http:
- route:
- destination:
host: httpy
subset: prod
weight: 90
- destination:
host: httpy
subset: canary
weight: 10
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpy
spec:
host: httpy
subsets:
- name: prod
labels:
isCanary: "false"
- name: canary
labels:
isCanary: "true"
Imagine that there are no healthy pods in the httpy service that have the isCanary: "true" label either because A) zero pods have the isCanary: "true" label or B) all of the canary pods are unready/unhealthy. This will result in 10% of requests to the httpy returning a "no healthy upstream" response.
Is there a good way to avoid this? Is it possible to make the VirtualService "smarter" and have it not route traffic to an obviously unusable destination?
If you want to try this out yourself, apply the following gist then delete the httpy-canary deployment: https://gist.github.com/llamasoft/aaca58fda0ec2e06f2fee4f272691460
Yes, in the destination rule You can configure failover with LocalityLoadBalancerSetting.
According to istio documentation:
If the goal of the operator is not to distribute load across zones and regions but rather to restrict the regionality of failover to meet other operational requirements an operator can set a ‘failover’ policy instead of a ‘distribute’ policy.
The following example sets up a locality failover policy for regions. Assume a service resides in zones within us-east, us-west & eu-west this example specifies that when endpoints within us-east become unhealthy traffic should failover to endpoints in any zone or sub-zone within eu-west and similarly us-west should failover to us-east.
failover:
- from: us-east
to: eu-west
- from: us-west
to: us-east
Failover requires outlier detection to be in place for it to work.
Hope it helps.
Related
CONTEXT:
I'm in the middle of planning a migration of kubernetes services from one cluster to another, the clusters are in separate GCP projects but need to be able to communicate across the clusters until all apps are moved across. The projects have VPC peering enabled to allow internal traffic to an internal load balancer (tested and confirmed that's fine).
We run Anthos service mesh (v1.12) in GKE clusters.
PROBLEM:
I need to find a way to do the following:
PodA needs to be migrated, and references a hostname in its ENV which is simply 'serviceA'
Running in the same cluster this resolves fine as the pod resolves 'serviceA' to 'serviceA.default.svc.cluster.local' (the internal kubernetes FQDN).
However, when I run PodA on the new cluster I need serviceA's hostname to actually resolve back to the internal load balancer on the other cluster, and not on its local cluster (and namespace), seen as serviceA is still running on the old cluster.
I'm using an istio ServiceEntry resource to try and achieve this, as follows:
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: serviceA
namespace: default
spec:
hosts:
- serviceA.default.svc.cluster.local
location: MESH_EXTERNAL
ports:
- number: 50051
name: grpc
protocol: GRPC
resolution: STATIC
endpoints:
- address: 'XX.XX.XX.XX' # IP Redacted
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: resources
namespace: default
spec:
hosts:
- 'serviceA.default.svc.cluster.local'
gateways:
- mesh
http:
- timeout: 5s
route:
- destination:
host: serviceA.default.svc.cluster.local
This doesn't appear to work and I'm getting Error: 14 UNAVAILABLE: upstream request timeout errors on PodA running in the new cluster.
I can confirm that running telnet to the hostname from another pod on the mesh appears to work (i.e. don't get connection timeout or connection refused).
Is there a limitation on what you can use in the hosts on a serviceentry? Does it have to be a .com or .org address?
The only way I've got this to work properly is to use a hostAlias in PodA to add a hostfile entry for the hostname, but I really want to try and avoid doing this as it means making the same change in lots of files, I would rather try and use Istio's serviceentry to try and achieve this.
Any ideas/comments appreciated, thanks.
Fortunately I came across someone with a similar (but not identical) issue, and the answer in this stackoverflow post gave me the outline of what kubernetes (and istio) resources I needed to create.
I was heading in the right direction, just needed to really understand how istio uses Virtual Services and Service Entries.
The end result was this:
apiVersion: v1
kind: Service
metadata:
name: serviceA
namespace: default
spec:
type: ExternalName
externalName: serviceA.example.com
ports:
- name: grpc
protocol: TCP
port: 50051
---
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: serviceA
namespace: default
spec:
hosts:
- serviceA.example.com
location: MESH_EXTERNAL
ports:
- number: 50051
name: grpc
protocol: TCP
resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: serviceA
namespace: default
spec:
hosts:
- serviceA.default.svc.cluster.local
http:
- timeout: 5s
route:
- destination:
host: serviceA.default.svc.cluster.local
rewrite:
authority: serviceA.example.com
I find that the Rewrite feature of my Virtual Service is not working very well. Here is my Virtual Service and DestinationRule yaml file:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: leads-http
namespace: seldon
spec:
gateways:
- istio-system/seldon-gateway
hosts:
- '*'
http:
- match:
- uri:
prefix: /seldon/seldon/leads/
rewrite:
uri: /
route:
- destination:
host: leads-leads
port:
number: 8000
subset: leads
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: leads-leads
namespace: seldon
spec:
host: leads-leads
subsets:
- labels:
version: leads
name: leads
trafficPolicy:
connectionPool:
http:
idleTimeout: 60s
When I send an http request:
curl --location --request POST 'http://localhost/seldon/seldon/leads/v2/models/leads-lgb/versions/v0.1.0/infer'
I find that the istio-proxy service prints 404 not found in the logs:
"POST /seldon/seldon/leads/v2/models/leads-lgb/versions/v0.1.0/infer HTTP/1.1" 404
even though I expect:
POST /v2/models/leads-lgb/versions/v0.1.0/infer HTTP/1.1
I am not sure what's happening. Does anyone have any idea? Thanks!
I think your issue is incorrectly configured DestinationRule or service name coneintion.
DestinationRule:
These rules specify configuration for load balancing, connection pool size from the sidecar, and outlier detection settings to detect and evict unhealthy hosts from the load balancing pool.
Version specific policies can be specified by defining a named subset and overriding the settings specified at the service level.
Note: Policies specified for subsets will not take effect until a route rule explicitly sends traffic to this subset.
DestinationRule-Subset:
It seems to me that name should go first in the structure. At least I havent seen/met another examples.
So in your case correct(at least I hope) DR is:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: leads-leads
namespace: seldon
spec:
host: leads-leads
subsets:
- name: leads
labels:
version: leads
However, if that wont help - I encourage you to check this self-resolved question:
Dont you have the same situation with named service port? I mean as per Explicit protocol selection you should add sufix in service name...
name: <protocol>[-<suffix>]
Can any body explain istio Subsets and destination rules in a a simple manner and explain the problem they are trying to solve by introducing the subsets.
DestinationRule is a resource that adds additional routing policies after routing happens to a Service, for example say that you have the following service:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: my-service
ports:
- name: http
protocol: TCP
port: 80
This Service can route to multiple resources, it picks up any pod which contains label app: my-service, which means you can have, for example, different versions of the same service running in parallel using one deployment for each.
Now, with a DestinationRule you can add additional routing policies on top of that, a subset means part of your pods which you can identify through labels, for example:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-service-ab
spec:
host: my-service.default.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: LEAST_CONN
subsets:
- name: a-test
labels:
version: v3
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
This DestinationRule uses a round robin load balancing policy for all traffic going to a subset named a-test that is composed of endpoints (e.g., pods) with labels (version:v3). This can be useful for scenarios such as A/B testing, or to keep multiple versions of you service running in parallel.
Also, you can specify custom TrafficPolicies for a subset that will override TrafficPolicies defined at a Service level.
I have a confusion between Virtual Service and Destinationrule on which one is executed first?
Let’s say I have below configs,
Destinationrule -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: movies
namespace: aio
spec:
host: movies
subsets:
- labels:
version: v1
name: version-v1
- labels:
version: v2
name: version-v2
---
VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: movies
namespace: aio
spec:
hosts:
- movies
http:
- route:
- destination:
host: movies
subset: version-v1
weight: 10
- destination:
host: movies
subset: version-v2
weight: 90
---
I read somewhere that,
A VirtualService defines a set of traffic routing rules to apply when a host is addressed.
DestinationRule defines policies that apply to traffic intended for service after routing has occurred.
Does this mean Destinationrules are invoked after Virtualservices?
I have a small diagram, is my understanding correct?
Yes,
According to istio documentation about DestinationRule:
DestinationRule defines policies that apply to traffic intended for a service after routing has occurred.
And for VirtualService:
A VirtualService defines a set of traffic routing rules to apply when a host is addressed.
There is an youtube video: Life of a Packet through Istio it explains in detail the order of processes that are applied to a packet going through the istio mesh.
As I understand, Istio VirtualService is kind of abstract thing, which tries to add an interface to the actual implementation like the service in Kubernetes or something similar in Consul.
When use Kubernetes as the underlying platform for Istio, is there any difference between Istio VirtualService and Kubernetes Service or are they the same?
Kubernetes service
Kubernetes service manage a pod's networking. It specifies whether your pods are exposed internally (ClusterIP), externally (NodePort or LoadBalancer) or as a CNAME of other DNS entries (externalName).
As an example this foo-service will expose the pods with label app: foo. Any requests sent to the node on port 30007 will be forwarded to the pod on port 80.
apiVersion: v1
kind: Service
metadata:
name: foo-service
spec:
type: NodePort
selector:
app: foo
ports:
- port: 80
targetPort: 80
nodePort: 30007
Istio virtualservice
Istio virtualservice is one level higher than Kuberenetes service. It can be used to apply traffic routing, fault injection, retries and many other configurations to services.
As an example this foo-retry-virtualservice will retry 3 times with a timeout 2s each for failed requests to foo.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: foo-retry-virtualservice
spec:
hosts:
- foo
http:
- route:
- destination:
host: foo
retries:
attempts: 3
perTryTimeout: 2s
Another example of this foo-delay-virtualservice will apply a 0.5s delay to 0.1% of requests to foo.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: foo-delay-virtualservice
spec:
hosts:
- foo
http:
- fault:
delay:
percentage:
value: 0.1
fixedDelay: 5s
route:
- destination:
host: foo
Ref
https://kubernetes.io/docs/concepts/services-networking/service/
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
https://istio.io/latest/docs/reference/config/networking/virtual-service/
https://istio.io/latest/docs/concepts/traffic-management/#virtual-services
Istio's VirtualServices provides, as every Istio's extensions, some additionals features such as external traffic routing/management (Pod to external communication, HTTPS external communication, routing, url rewriting...).
Take a look at this doc about it for more details : https://istio.io/docs/reference/config/networking/virtual-service
They can be both useful, as you need "classic" Services to manage ingress traffic or service-to-service communication.
Virtual Service:
It defines a set of traffic routing rules to apply to a kubernetes service or subset of service based on the matching criteria. This is something similar to kubernetes Ingress object. It plays a key role on Istio's traffic management flexible and powerful.
Kubernetes Service:
It can be a logical set of pods and defined as an abstraction on top of pods which provides single DNS name or IP.