Home > other >  Kubernetes Multiple Path Rewrites
Kubernetes Multiple Path Rewrites

Time:01-25

Alright, various permutations of this question have been asked and I feel terrible asking; I'm throwing the towel in and was curious if anyone could point me in the right direction (or point out where I'm wrong). I went ahead and tried a number of examples from the docs, but to no avail (see below).

I'm trying to route traffic to the appropriate location under Kubernetes using an Ingress controller.

Server Setup

I have a server, myserver.com and three services running at:

myserver.com/services/

myserver.com/services/service_1/

myserver.com/services/service_2/

Note that I'm not doing anything (purposefully) to myserver.com/.

At each of the three locations, there's a webapp running. For example, myserver.com/services/service_2 needs to load css files at myserver.com/services/service_2/static/css, etc...

Kubernetes Ingress

To manage the networking, I'm using a Kubernetes Ingress controller, which I've defined below. The CORS annotations aren't super relevant, but I've included them to clear up any confusion.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myServices
  namespace: myServices
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-origin: '$http_origin'
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/use-regex: "true"

spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - myserver.com
  rules:
  - host: myserver.com
    http:
      paths:
      - path: /services
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
      - path: /services/service_1(/|$)
        pathType: Prefix
        backend:
          service:
            name: web-service-1
            port:
              number: 80
      - path: /services/service_2(/|$)
        pathType: Prefix
        backend:
          service:
            name: web-service-2
            port:
              number: 80

Targets

I noticed that one helpful thing to do is give some path examples. From the examples below it looks like the paths aren't that complicated. I think this is what I'm after. Note that I'd like each service to be able to resolve its css and image files.

myserver.com/services -> myserver.com/services
myserver.com/services/xxx/xxx -> myserver.com/services/xxx/xxx
myserver.com/services/service_1 -> myserver.com/services/service_1
myserver.com/services/service_1/xxx/xxx -> myserver.com/services/service_1/xxx/xxx
myserver.com/services/service_2/xxx/xxx -> myserver.com/services/service_2/xxx/xxx


Attempts

I know that this issue has to do a lot with the nginx.ingress.kubernetes.io/rewrite-target rule and its interaction with the paths I've defined.

I know that I don't want nginx.ingress.kubernetes.io/rewrite-target: $1 because that gives a 500 when visiting myserver.com/services

I know that I don't want nginx.ingress.kubernetes.io/rewrite-target: $1/$2 because when I visit myserver.com/services/service_1 I actually get part of the content at myserver.com/services rendered on the page.

SO Attempt 1

I also attempted to replicate the accepted solution from this question.

In this attempt I set

nginx.ingress.kubernetes.io/rewrite-target: "/$1" and one of the service paths to

- path: /(services/service_1(?:/|$).*)

When I visit myserver.com/services/service_1/xyz, the HTML from myserver.com/services/service_1 gets rendered.

Concluding Thoughts

Something ain't quite right with the path rewrite and paths rules. Any suggestions?

CodePudding user response:

The problem you reported in your most recent comment is resolved by looking at the rewrite example in the nginx-ingress documentation.

The rewrite-target annotation configures the ingress such that matching paths will be rewritten to that value. Since you've specified a static value of /, anything matching your ingress rules will get rewritten to /, which is exactly the behavior you're seeing.

The solution is to capture the portion of the path we care about, and then use that in the rewrite-target annotation. For example:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myservices
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-origin: '$http_origin'
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"

spec:
  ingressClassName: nginx
  rules:
  - host: myserver.com
    http:
      paths:
      - path: /services/service_1(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: webservice-service1
            port:
              number: 80
      - path: /services/service_2(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: webservice-service2
            port:
              number: 80
      - path: /services(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: webservice
            port:
              number: 80

Here, we've modified the match expression so that they look like:

      - path: /services/service_1(/|$)(.*)

The second capture group (.*) captures everything after the path portion that matches literally. We then use that capture group ($2, because it's the second group) in the rewrite-target annotation:

    nginx.ingress.kubernetes.io/rewrite-target: /$2

With this configuration in place, a request to /services/service_2 results in:

This is service2.

But a request to /services/service_2/foo/bar results in:

<html><head><title>404 Not Found</title></head><body>
<h1>Not Found</h1>
The URL you requested (/foo/bar) was not found.
<hr>
</body></html>

And looking at the backend server logs, we see:

10.42.0.32 - - [21/Jan/2022:20:33:23  0000] "GET / HTTP/1.1" 200 211 "" "curl/7.79.1"
10.42.0.32 - - [21/Jan/2022:20:33:45  0000] "GET /foo/bar HTTP/1.1" 404 311 "" "curl/7.79.1"

I've updated my example repository to match this configuration.

  •  Tags:  
  • Related