istio: Multiple virtualservices on the same host do not merge

Describe the bug I did the similar test with deploying 2 applications as reported in #6046 - bookinfo and httpbin. One of the applications always returns 404 in the following cases:

case 1: single namespace bookinfo app - bookinfo-gateway and bookinfo virtualservice httpbin app - httpbin-gateway and httpbin virtualservice

case 2: single namespace with single gateway bookinfo app - bookinfo-gateway and bookinfo virtualservice httpbin app - httpbin virtualservice (with short name bookinfo-gateway defined in gateways list)

case 3: multiple namespaces with single gateway bookinfo app - bookinfo-gateway and bookinfo virtualservice in namespace1 httpbin app - httpbin virtualservice in namespace2 (with fqdn bookinfo-gateway.namespace1.svc.cluster.local defined in gateways list)

the application returns 200 if I removed the other virtualservice file in above case. here are the yaml files of the gateway and virtualservices for case 2:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"networking.istio.io/v1alpha3","kind":"Gateway","metadata":{"annotations":{},"name":"bookinfo-gateway","namespace":"default"},"spec":{"selector":{"istio":"ingressgateway"},"servers":[{"hosts":["*"],"port":{"name":"http","number":80,"protocol":"HTTP"}}]}}
  clusterName: ""
  creationTimestamp: 2018-08-03T18:35:36Z
  generation: 1
  name: bookinfo-gateway
  namespace: default
  resourceVersion: "10845"
  selfLink: /apis/networking.istio.io/v1alpha3/namespaces/default/gateways/bookinfo-gateway
  uid: 039d51b8-974c-11e8-a97b-00505680c68c
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"networking.istio.io/v1alpha3","kind":"VirtualService","metadata":{"annotations":{},"name":"bookinfo","namespace":"default"},"spec":{"gateways":["bookinfo-gateway"],"hosts":["*"],"http":[{"match":[{"uri":{"exact":"/productpage"}},{"uri":{"exact":"/login"}},{"uri":{"exact":"/logout"}},{"uri":{"prefix":"/api/v1/products"}}],"route":[{"destination":{"host":"productpage","port":{"number":9080}}}]}]}}
  clusterName: ""
  creationTimestamp: 2018-08-03T19:10:19Z
  generation: 1
  name: bookinfo
  namespace: default
  resourceVersion: "10535"
  selfLink: /apis/networking.istio.io/v1alpha3/namespaces/default/virtualservices/bookinfo
  uid: dce03829-9750-11e8-a97b-00505680c68c
spec:
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"networking.istio.io/v1alpha3","kind":"VirtualService","metadata":{"annotations":{},"name":"httpbin","namespace":"default"},"spec":{"gateways":["bookinfo-gateway"],"hosts":["*"],"http":[{"match":[{"uri":{"prefix":"/status"}},{"uri":{"prefix":"/delay"}},{"uri":{"prefix":"/headers"}}],"route":[{"destination":{"host":"httpbin","port":{"number":8000}}}]}]}}
  clusterName: ""
  creationTimestamp: 2018-08-03T18:57:54Z
  generation: 1
  name: httpbin
  namespace: default
  resourceVersion: "10157"
  selfLink: /apis/networking.istio.io/v1alpha3/namespaces/default/virtualservices/httpbin
  uid: 20d0e173-974f-11e8-a97b-00505680c68c
spec:
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    - uri:
        prefix: /headers
    route:
    - destination:
        host: httpbin
        port:
          number: 8000

Expected behavior I assume both case 2 and case 3 (#6483 should have fixed the issue) should work in 1.0.

Version Version: 1.0.0 GitRevision: 3a136c90ec5e308f236e0d7ebb5c4c5e405217f4 User: root@71a9470ea93c Hub: gcr.io/istio-release GolangVersion: go1.10.1 BuildStatus: Clean

Client Version: version.Info{Major:“1”, Minor:“10”, GitVersion:“v1.10.2”, GitCommit:“81753b10df112992bf51bbc2c2f85208aad78335”, GitTreeState:“clean”, BuildDate:“2018-04-27T09:22:21Z”, GoVersion:“go1.9.3”, Compiler:“gc”, Platform:“linux/amd64”} Server Version: version.Info{Major:“1”, Minor:“10”, GitVersion:“v1.10.6”, GitCommit:“a21fdbd78dde8f5447f5f6c331f7eb6f80bd684e”, GitTreeState:“clean”, BuildDate:“2018-07-26T10:04:08Z”, GoVersion:“go1.9.3”, Compiler:“gc”, Platform:“linux/amd64”}

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 26 (11 by maintainers)

Most upvoted comments

One option that we recommend is to use some form out out-of-band composition of these files into a single VirtualService resource which is then pushed. This requires you to roll your own composition today but this is something people do a lot with CI/CD systems using pretty much any text templating system.

@louiscryan this is not acceptable, and a clear regression of virtual services from the ingress resources that existed in 0.6. What is the complication of just supporting what was supported in 0.6? Doing this kind of post processing especially in dynamic systems causes a ton of edge cases for the post processor specifically around the removal and updating of routes.

This should be fixed ASAP, to be in parity with the way it worked in 0.6.

@ZackButcher ^^

cc @rshriram @louiscryan This is an issue for a customer. The customer uses helm to manage multiple services. These services share the host but use different URI matches on two virtual services. What should be the advice for people who use helm to manage ingress-type resources?

I understand the concern, but this argument is a clear strawman. The possibility of ambiguities is problematic, I will concede that.

The problem we are facing is not with ambiguous matches. We are currently blocked because Istio does not support merging routes at all. Give us enough rope to hang ourselves. That’s all I’m asking.

@nrjpoddar Well, I do not agree to the path of having to change files as services get installed. if that were the case, would just update the virtual service to include all needed URIs. My opinion is to give more thought and treat multiple files as pure conf files which can be merged.

Even if the matches were constrained to path segments and not other properties of the traffic you can still have ordering issues when merging.

Really? Does that mean merging the following examples would be nondeterministic?

  • for host example.com match path /hello and route to X
  • for host example.com match path /world and route to Y

@rshriram @vadimeisenbergibm Hostname per service seems a lot to ask customers to setup/configure if we have more than say 10 services to be exposed. Here is a scenario where a standard nginx has worked well for me in the past

  1. Create a server nginx conf such that it can listen on just one host name
  2. Have a bunch of config files included which can be used for redirection. The key is that each service just brings it’s own configuration for redirection but the access to service is always using one host name.

Wondering how this can be done using Istio ingress-gateway? i.e something like do not treat virtualservice with same host as error but really treat as a merge. This way each service can bring their own match parameters and not need to keep modifying the same Virtual service using something centralized. May be these last few statements are based on me not knowing if there is a way to achieve this already.

@louiscryan thanks for your response! I’ve provided a relatively contrived but as simple example as I can provide to help with context below.

Just a couple of comments before I describe the scenario:

  1. The host * is not part of our specific requirements.
  2. Order is not a concern because we’re not submitting nested routes.
  3. If the user is going to submit nested routes where absolute ordering is necessary then that’s up to them to do that properly not the opinion of Istio because envoy supports this behavior.

The scenario is you have serviceA and serviceB each belong behind a single subdomain subdomain1 and are routed to unique input channels. The input channels cannot be added to the host because they would violate RFC-1035 size limits as we don’t have control over what they are.

---
- apiVersion: networking.istio.io/v1alpha3
  kind: VirtualService
  metadata:
     ...
    name: serviceA
    ...
  spec:
    gateways:
    - istio-gateway
    hosts:
    - subdomain1.some.host.com 
    http:
    - match:
      - uri:
          exact: /run/before/input-channel-6351547589003002
      - uri:
          exact: /ping/run/before/input-channel-6351547589003002
      route:
      - destination:
          host: serviceA.common-ns.svc.cluster.local
          port:
            number: 8080
---
- apiVersion: networking.istio.io/v1alpha3
  kind: VirtualService
  metadata:
    ...
    name: serviceB
    ...
  spec:
    gateways:
    - istio-gateway
    hosts:
    - subdomain1.some.host.com 
    http:
    - match:
      - uri:
          exact: /run/before/input-channel-5361011480850776
      - uri:
          exact: /ping/run/before/input-channel-5361011480850776
      route:
      - destination:
          host: serviceB.common-ns.svc.cluster.local
          port:
            number: 8080

I would constrain scope here, leverage reasonable assumptions to get the job done. We’re/were EEP so I’m happy to come to MV or SF office and work with ya’ll.