Search code examples
url-rewritingurl-routingistio

How to rewrite in Istio the right way and avoid 404 error?


Scenario-

I have 2 deployments deployment-1 with label- version:v1 and deployment-2 with label- version:v2 both are hosted under a nodeport service- test-1. I have created a virtual service with two match conditions as follows

  - match:
    - uri:
        exact: /v1
    rewrite:
      uri: /
    route:
      - destination:
          host: test-1
          port:
            number: 80
          subset: v1
  - match:
    - uri:
        exact: /v2
    rewrite:
      uri: /
    route:
      - destination:
          host: test-1
          port:
            number: 80
          subset: v2

The code file can be found here

Problem-

When I try to visit this Ingress Gateway IP at http://ingress-gateway-ip/v1/favicon.ico, I encounter a 404 error in the console saying http://ingress-gateway-ip/favicon.ico not found (because this has been re-written to "/") also the stylings and js are absent at this route. But when I try to visit http://ingress-gateway-ip/v1/favicon.ico I can see the favicon icon along with all the js and stylings.

Please find the screenshots of the problem here

Expectation-

How can I access these two services using a prefix routing in the url, meaning when I navigate to /v1, only the V1 version should come up without 404, and when I navigate to /v2, only the V2 version should come up?

EDIT-1:

  1. Added code snippet from the original code
  2. Added code file link

EDIT-2:

  1. Added screenshot of the problem
  2. Modified problem statement for clear understanding

Solution

  • How can I access these two services using a prefix routing in the url, meaning when I navigate to /v1, only the V1 version should come up without 404, and when I navigate to /v2, only the V2 version should come up

    I assume your issue is your DestinationRule, in the v2 name your label is version: v1 and it should be version: v2, that's why your requests from /v1 and /v2 went only to v1 version of your pod.

    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: test-destinationrule
    spec:
      host: test-1
      subsets:
      - name: v1
        labels:
          version: v1
      - name: v2
        labels:
          version: v1 <--- 
    

    It should be

    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: test-destinationrule
    spec:
      host: test-1
      subsets:
      - name: v1
        labels:
          version: v1
      - name: v2
        labels:
          version: v2
    

    When I try to visit this Ingress Gateway IP, I encounter a 404 error in the console saying http://ingress-gateway-ip/favicon.ico

    It's working as designed, you haven't specified path for /, just for /v1 and /v2.

    If you want to be able to access then you would have to add another match for /

    - match:
      - uri:
          prefix: /
      route:
        - destination:
            host: test-1
    

    There is working example with 2 nginx pods, take a look.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-v1
    spec:
      selector:
        matchLabels:
          version: v1
      replicas: 1
      template:
        metadata:
          labels:
            app: frontend
            version: v1
        spec:
          containers:
          - name: nginx1
            image: nginx
            ports:
            - containerPort: 80
            lifecycle:
              postStart:
                exec:
                  command: ["/bin/sh", "-c", "echo Hello nginx1 > /usr/share/nginx/html/index.html"]
    
    ---
    
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-v2
    spec:
      selector:
        matchLabels:
          version: v2
      replicas: 1
      template:
        metadata:
          labels:
            app: frontend
            version: v2
        spec:
          containers:
          - name: nginx2
            image: nginx
            ports:
            - containerPort: 80
            lifecycle:
              postStart:
                exec:
                  command: ["/bin/sh", "-c", "echo Hello nginx2 > /usr/share/nginx/html/index.html"]
    
    ---
    
    apiVersion: v1
    kind: Service
    metadata:
      name: test-1
      labels:
        app: frontend
    spec:
      ports:
      - name: http-front
        port: 80
        protocol: TCP
      selector:
        app: frontend
    
    ---
    
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: simpleexample
    spec:
      selector:
        istio: ingressgateway
      servers:
      - hosts:
        - '*'
        port:
          name: http
          number: 80
          protocol: HTTP
    
    ---
    
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: test-virtualservice
    spec:
      gateways:
      - simpleexample
      hosts:
      - '*' 
      http:
      - match:
        - uri:
            prefix: /v1
        rewrite:
          uri: /
        route:
          - destination:
              host: test-1
              port:
                number: 80
              subset: v1
      - match:
        - uri:
            prefix: /v2
        rewrite:
          uri: /
        route:
          - destination:
              host: test-1
              port:
                number: 80
              subset: v2
    
      
    ---
    
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: test-destinationrule
    spec:
      host: test-1
      subsets:
      - name: v1
        labels:
          version: v1
      - name: v2
        labels:
          version: v2
    

    Results from curl:

    curl -v ingress-gateway-ip/  
    404 Not Found 
    there is no path specified for that in virtual service 
    
    curl -v ingress-gateway-ip/v1  
    HTTP/1.1 200 OK 
    Hello nginx1
    
    curl -v ingress-gateway-ip/v2 
    HTTP/1.1 200 OK 
    Hello nginx2
    

    EDIT

    the problem is that all the stylings and js are not readable by the browser at "/" when they are being re-written

    It was already explained by @Rinor here

    I would add this Istio in practise tutorial here, it explains well a way of dealing with that problem, which is to add more paths for your dependencies(js,css,etc).

    Let’s break down the requests that should be routed to Frontend:

    Exact path / should be routed to Frontend to get the Index.html

    Prefix path /static/* should be routed to Frontend to get any static files needed by the frontend, like Cascading Style Sheets and JavaScript files.

    Paths matching the regex ^.*.(ico|png|jpg)$ should be routed to Frontend as it is an image, that the page needs to show.

    http:
      - match:
        - uri:
            exact: /
        - uri:
            exact: /callback
        - uri:
            prefix: /static
        - uri:
            regex: '^.*\.(ico|png|jpg)$'
        route:
        - destination:
            host: frontend             
            port:
              number: 80
    

    Let me know if you have any more questions.