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.
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...
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
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
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.
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.
Something ain't quite right with the path rewrite and paths rules. Any suggestions?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With