Per-path rewrite in an Kubernetes NGINX reverse proxy ingress controller - nginx

I'm setting up a Kubernetes cluster which contains three applications, each running within their own respective pod and service. The web frontends of these applications should be accesible at ports 80, 9000 and 15672. There are also a number of Backend-APIs running in their own pods and services, which should be accesible at their respective ports. The cluster is accessed through a NGINX reverse proxy with the following ingress-definition:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
namespace: my-namespace
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- myapp.com
secretName: my-certificat
rules:
- host: myapp.com
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 8092
path: /api/someroute/(.*)
- backend:
serviceName: my-service-2
servicePort: 30003
path: /api/someotherroute/(.*)
- backend:
serviceName: my-other-frontend
servicePort: 9000
path: /other/(.*)
- backend:
serviceName: my-yetanother-frontend
servicePort: 15672
path: /yetanother/(.*)
- backend:
serviceName: my-main-frontend
servicePort: 80
path: /(.*)
This works for the api-services, but not for the frontends. Was I to enter a URI like myapp.com/other/ in my browser it would make a call to my-other-frontend:9000/other/, instead of my-other-frontend:9000/. This can of course be solved with a rewrite annotation like nginx.ingress.kubernetes.io/rewrite-target: /$1, the problem is, that this would also apply to the api-services, and those actually need the complete route in their call, so a general rewrite rule would break them.
So my question is: Is it possible to define rewrite rules that only apply to specific paths?

I would advise to split your ingress into 2 based on your different annotations.
1) First one for api-services, called my-api-ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-api-ingress
namespace: my-namespace
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- myapp.com
secretName: my-certificat
rules:
- host: myapp.com
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 8092
path: /api/someroute/(.*)
- backend:
serviceName: my-service-2
servicePort: 30003
path: /api/someotherroute/(.*)
2) And second one, lets say my-front-ingress with nginx.ingress.kubernetes.io/rewrite-target: /$1 annotation for rest of your frontends. Based on your question I believe you dont need the detailed instruction for ingress creation with rewrite annotation.
EDIT1:
Yes, its possible to create multiple ingresses with the same host. But please pay attention which exactly nginx ingress. Refer to #LazerBass answer with his investigations.
In short, based on nginx ingress comparation table,
The nginxinc controller does not support merging Ingress rules with the same host. You can use it only with Mergeable Ingresses
The easiest way is to use regular kubernetes/ingress-nginx
Hope that helps.

Related

Kubernetes ingress nginx route to same path (different service; different port)

I have some simple deployments, pods, services and nginx ingress in Kubernetes. I want to use ingress to route to the services (cluster-ip).
However, there are 2 services for 2 pods with the same path (i.e /abc/def). After I applied the ingress.yaml file, I got an error message saying "nginx: [emerg] duplicate location "/abc/def/" in /tmp/nginx/nginx-cfg728288520:2893".
May I know how to let ingress accepts the same paths with different service and different port?
Here is the ingress.yaml file:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
name: ingress-nginx-default
namespace: default
spec:
rules:
- host:
http:
paths:
- path: /abc/def/
pathType: Prefix
backend:
service:
name: service1
port:
number: 8090
- path: /abc/def
backend:
service:
name: service2
port:
number: 8068
pathType: Prefix
ingressClassName: nginx
"nginx: [emerg] duplicate location "/abc/def/" in /tmp/nginx/nginx-cfg728288520:2893". This error indicates the same host with two same paths which is a duplicate location.
You can use simple fanout based on path or name based virtual hosting.
In order to do this you need to have two hosts that need to be mentioned in the ingress.
based on your example you'd most likely want to have something like foo.bar.com and bar.foo.com. Here's the example from the Kubernetes docs:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths: #path_name
- backend:
serviceName: service1
servicePort: 80
- host: bar.foo.com
http:
paths: #path_name
- backend:
serviceName: service2
servicePort: 80

Enable rewrite for a particular host in ingress nginx configuration

I have below ingress-nginx configuration file.
It rewrites request for one.example.com. I have added another domain, but I don't want rewrite to happen for other domain.
I went through doc, but there is now rewrite example for multiple host setup.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
certmanager.k8s.io/cluster-issuer: "letsencrypt-example-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- one.example.com
- two.example.com
secretName: super-secret
rules:
- host: one.example.com
http:
paths:
- path: /customer/?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
host: two.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kube-prometheus-grafana
port:
number: 80
I could create two separate ingress files for each host.
What will happen in that case it will create two load balancers with different dns?
(cluster is hosted on aws)
You can create two separate ingress.
In that case, nothing will change your ingress controller IP would be same only for DNS.
What will happen in that case it will create two load balancers with
different dns?
No, it won't create two load balancers.

Nginx ingress rule for changing path on the fly

I would like to change the path of request coming to ingress-nginx on the fly to match what different backend services expect. For example, imagine these are the two services I have got:
foo service:
http://foo:8080/api/v1
bar service:
http://bar:8080/api/v1
I would like to expose foo service as http://test.com/foo/api and bar service as http://test.com/bar/api. However, I am not sure how I can change the path on the fly to match what the underlying service expects.
Example ingress.yaml file:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
rules:
- host: test.com
http:
paths:
- path: /foo/api/
backend:
serviceName: foo
servicePort: 8080
- path: /bar/api
backend:
serviceName: bar
servicePort: 8080
- path: /
backend:
serviceName: ui
servicePort: 80
You can try with rewrite annotations.
Look at the example provided in this documentation:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something(/|$)(.*)
In this ingress definition, any characters captured by (.*) will be assigned to the placeholder $2, which is then used as a parameter in the rewrite-target annotation.
For example, the ingress definition above will result in the following rewrites:
rewrite.bar.com/something rewrites to rewrite.bar.com/
rewrite.bar.com/something/ rewrites to rewrite.bar.com/
rewrite.bar.com/something/new rewrites to rewrite.bar.com/new
Just adjust the path and other variables with your data.
Please let me know if that helped.

K8S - Ingress - Route 2 different applications

In my minikube k8s cluster, I have 2 applications/services.
This is my ingress controller for the services I have.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-tutorial
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: mysite.com
http:
paths:
- path: /sva
backend:
serviceName: app-a
servicePort: 8080
- path: /svb
backend:
serviceName: app-b
servicePort: 8150
When i type mysite.com/sva or mysite.com/svb it routes to appropriate services. However, none of the static files (js, css, images etc) is loaded as they seem to request for the resources from mysite.com instead of mysite.com/sva/
How can I make it look for the resources under specific service?
Try to Add the following annotation in ingress
ingress.kubernetes.io/add-base-url: "true"
will solves this problem.
You can also review : https://github.com/kubernetes/ingress-nginx/issues/333
Based on Harsh Manvar answer, Looks like rewrite does not work on the static resources as of now.
This is a workaround I am planning to follow to route diff apps like sva.mysite.com or svb.mysite.com. It works fine. Just adding this as an answer if somebody faces similar problem.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-tutorial
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: sva.mysite.com
http:
paths:
- path: /
backend:
serviceName: app-a
servicePort: 8080
- host: svb.mysite.com
http:
paths:
- path: /
backend:
serviceName: app-b
servicePort: 8150

nginx-ingress works well for /, but not for subpaths

I have setup a name based ingress controller, but it doesn't seem to work for anything other than /.
So http://metabase.domain.com works but http://metabase.domain.com/style/app.css does not.
This is my config:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: domain
annotations:
kubernetes.io/ingress.global-static-ip-name: "domain"
name: domain-ingress
spec:
rules:
- host: metabase.domain.com
http:
paths:
- path: /
backend:
serviceName: metabase
servicePort: 80
- host: jenkins.domain.com
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 80
From the nginx.conf in the everything looks normal too. For some reason the nginx access and error logs are also empty so can't find anything from there too
As you mentioned, there is no error in the log files, and everything looks normal from
your perspective. I may suggest to tune up ingress using annotations tags.
I've checked documentation of ingress-nginx and found that below annotations may help a bit.
In some scenarios, the exposed URL in the backend service differs from the specified path
in the Ingress rule. Without a rewrite, any request will return 404.
Set the annotation
nginx.ingress.kubernetes.io/rewrite-target
to the path expected by the service.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something
If the Application Root is exposed in a different path and needs to be redirected, set the annotation
nginx.ingress.kubernetes.io/app-root
to redirect requests for /.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/app-root: /app1
name: approot
namespace: default
spec:
rules:
- host: approot.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /
If the application contains relative links, it is possible to add an additional annotation
nginx.ingress.kubernetes.io/add-base-url
that will prepend a base tag in the header of the returned HTML from the backend.
I got around to solving this, turns out somehow there was another ingress on my namespace that was a very old version of the nginx controller. I cleared my cluster and relaunched my ingress. Things worked out. Thanks for all the help everyone!

Resources