How to use Ingress Nginx to route requests by url query string? - nginx

We would like to use annotation for redirecting requests to a different backend service based on url args (query)
Example:
https://example.com/foo?differentQueryString=0 -> service-a
https://example.com/foo/bar?queryString=0 - service-b
Notes: path does not matter, this can be either /foo/bar or /foo or /bar/foo
We followed up on this
Kubernetes NGINX Ingress controller - different route if query string exists
and this
Kubernetes ingress routes with url parameter
But we don't want to setup ConfigMap just for this and also we don't want to duplicate requests to the ingress but rewriting
This is what we tried
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($args ~ queryString=0){
backend.service.name = service-b
}
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-a
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: service-b
port:
number: 80
We were expecting to get the response but we got 502 from the Ingress Nginx

We managed to find a nice solution without rewriting and ConfigMap
Works great and also include Nginx Ingress metrics so we can do HPA accordingly
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($args ~ queryString=0){
set $proxy_upstream_name "default-service-b-80";
set $proxy_host $proxy_upstream_name;
set $service_name "service-b";
}
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-a
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: service-b
port:
number: 80
$proxy_upsteam_name convention is NAMESPACE-SERVICE_NAME-PORT

Related

Kubernetes Ingress Exact not prioritized over Prefix

In Kubernetes we need a new service to handle the root path, but but still a catch everything else on our current frontend.
Current frontend Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: current-frontend
labels:
app: current-frontend
tier: frontend
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- my.domain.com
secretName: tls-secret
rules:
- host: my.domain.com
http:
paths:
- backend:
service:
name: current-frontend
port:
number: 80
path: /
pathType: Prefix
New service Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: new-service
labels:
app: new-service
tier: frontend
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- my.domain.com
secretName: tls-secret
rules:
- host: my.domain.com
http:
paths:
- backend:
service:
name: new-service
port:
number: 80
path: /someendpoint
pathType: ImplementationSpecific
- backend:
service:
name: new-service
port:
number: 80
path: /
pathType: Exact
According to the documentation of Kuberntes Ingress, it should prioritize Exact over Prefix
If two paths are still equally matched, precedence will be given to paths with an exact path type over prefix path type.
https://kubernetes.io/docs/concepts/services-networking/ingress/#multiple-matches
The problem is that everything else then my.domain.com/someendpoint goes to the current-frontend, while the expected would be that my.domain.com/ would go to new-service.
How do I achieving this?
Solution
I got it working, even If it doesn't seams to be the optimal solution (According to the Documentation)
I Followed Hemanth Kumar's answer and changed the Current Frontend to use Regex, with (.+) instead of (.*) as I wanted at least one char after the slash for it to be hit.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: current-frontend
labels:
app: current-frontend
tier: frontend
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- my.domain.com
secretName: tls-secret
rules:
- host: my.domain.com
http:
paths:
- backend:
service:
name: current-frontend
port:
number: 80
path: /(.+)
pathType: Prefix
At the same time I needed to change the New service to use Prefix instead of Exact as it does not work, event if there is no other services to hit.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: new-service
labels:
app: new-service
tier: frontend
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- my.domain.com
secretName: tls-secret
rules:
- host: my.domain.com
http:
paths:
- backend:
service:
name: new-service
port:
number: 80
path: /someendpoint
pathType: ImplementationSpecific
- backend:
service:
name: new-service
port:
number: 80
path: /
pathType: Prefix
Ingress path matching can be enabled by setting the
nginx.ingress.kubernetes.io/use-regex annotation to true .
See the description of the use-regex annotation :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: test.com
http:
paths:
- path: /foo/.*
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Refer to this ingress path matching for more information on path priority
The solution is to use a regular expression (regex) to match all paths except /someendpoint.

Kubernetes Ingress returns 404 status code

Context: I have an application running on GKE. The application has 3 APIs. I have an Ingress load balancer that exposes an external IP for my front-end to call, and depending on the path will forward the request to appropriate APIs.
Problem: Calling the individual path returns a "404 Not Found - nginx" message. I call using a proxy (request format: https://my-proxy-hosted-on-heroku/http://xx.xxx.xxx.xx/api/auth/accounts/login/) (xx.xxx.xxx.xx is the Ingress external IP)
My Ingress yaml file:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,X-LANG,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Api-Key,X-Device-Id,Access-Control-Allow-Origin"
nginx.ingress.kubernetes.io/cors-expose-headers: "*, X-CustomResponseHeader"
# nginx.ingress.kubernetes.io/ssl-redirect: "false"
# nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
# kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/enable-cors: "true"
# nginx.ingress.kubernetes.io/cors-allow-origin: "*"
# nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS, DELETE"
# nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,X-LANG,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Api-Key,X-Device-Id,Access-Control-Allow-Origin"
spec:
rules:
- host: acme.com
http:
paths:
- pathType: Prefix
path: /api/discussion
backend:
service:
name: fplms-discussionservice-clusterip
port:
number: 7218
- pathType: Prefix
path: /api/auth
backend:
service:
name: fplms-authservice-clusterip
port:
number: 7209
- pathType: Prefix
path: /api/management
backend:
service:
name: fplms-managementservice-clusterip
port:
number: 8083

Example needed: How to migrate ingress routing from Nginx to Traefik-V1 (or V2)?

I try to switch from nginx to traefik in a Kubernetes cluster. I am totally new to Traefik.
I have an app with Frontend and Backend:
demo.myapp.com/ # frontend
demo.myapp.com/backend # backend
With Nginx I did that following code, which worked like a charm:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: demo.myapp.at
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-app
port:
number: 80
- path: /backend(/|$)(.*)
pathType: Prefix
backend:
service:
name: backend-api
port:
number: 80
Do I need two Ingresses for one domain, if I wanna route to a subfolder?
It seems with Traefik V1 and Traefik V2 (where V2 also needs a CRD for IngressRoute and/or Middleware manifest) more complex.
But I am totally lost with the examples in the docs as well with the mix of Version1 and Version2.
At the moment I use rancher/library-traefik:1.7.19 but I also can give V2 a try.
my V1 approach at the moment:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: demo-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
traefik.frontend.rule.type: PathPrefixStrip
spec:
rules:
- host: demo.myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: frontend-app
servicePort: 80
- path: /backend # old nginx regex -> (/|$)(.*)
pathType: Prefix
backend:
serviceName: backend-api
servicePort: 80
Problem with that V1 example:
all paths below /backend are not rewritten correctly.
Instead of routing to /backend/someImage.png it routes to /someImage.png
If someone can help me with an example (optimal would be one for V1 and one for V2), would be great.
Thank you in advance
These examples are allegories from the Nginx questioned examples above
For V1
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: demo-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
# traefik.ingress.kubernetes.io/redirect-permanent: "true"
traefik.ingress.kubernetes.io/redirect-regex: /backend$
traefik.ingress.kubernetes.io/redirect-replacement: /backend/
traefik.ingress.kubernetes.io/request-modifier: "ReplacePathRegex: ^/backend/(.*) /$1"
spec:
rules:
- host: demo.myapp.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
serviceName: frontend-app
servicePort: 80
- path: /backend # (/|$)(.*)
pathType: ImplementationSpecific
backend:
serviceName: backend-api
servicePort: 80
V2
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
annotations:
kubernetes.io/ingress.class: traefik
name: demo-ingress-route
namespace: default
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`demo.myapp.com`)
priority: 0
services:
- name: frontend-app
port: 80
- kind: Rule
match: Host(`demo.myapp.com`) && PathPrefix(`/backend/`)
middlewares:
- name: middleware-to-strip-backend-path
priority: 0
services:
- name: backend-api
port: 80
---
# this middleware will strip /backend from your request to align the requested url to the root / path of your API
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: middleware-to-strip-backend-path
namespace: default
spec:
stripPrefix:
prefixes:
- /backend

Remove prefix after match before forwarding request to service

I have this base url api.example.com
So, ingress-nginx will get the request for api.example.com and it should do follow things.
Forward api.example.com/customer to customer-srv
It doesn't work, it forwards whole mtach to customer-srv i.e. /customer/requested_url
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: api.example.in
http:
paths:
- path: /customer/?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
I tried using rewrite annotation but that doesn't work either however this worked but this is not I want to achieve.
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
For example,
api.example.com/customer should go to http://localhost:3000 not http://localhost:3000/customer
Here is the yaml I used:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: api.example.in
http:
paths:
- path: /customer/?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
For test purpouses I created an echo server:
kubectl run --image mendhak/http-https-echo customer
And then a service:
kubectl expose po customer --name customer-srv --port 3000 --target-port 80
I checked igress ip:
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-service <none> api.example.in 192.168.39.254 80 3m43s
And I did a curl to check it:
curl 192.168.39.254/customer/asd -H "Host: api.example.in"
{
"path": "/asd",
"headers": {
"host": "api.example.in",
...
}
Notice that the echo server echoed back a path that it received, and sice it received a path that got rewritten from /customer/asd to /asd it shows this exectly path (/asd).
So as you see this does work.

ingress multiple path mismatch

I have two service
serviceOld
serviceNew
I want to achieve the following effect
http://host/any => http://serviceOld/any
http://host/any/aaa => http://serviceOld/any/aaa
http://host/feature => http://serviceNew/feature
http://host/feature/bbb => http://serviceNew/feature/bbb
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: new
nginx.ingress.kubernetes.io/rewrite-target: /
name: v2
namespace: api
spec:
rules:
- host: xxx.com
http:
paths:
- path: /
backend:
serviceName: serviceOld
servicePort: 80
- path: /feature/*
backend:
serviceName: serviceNew
servicePort: 8080
I tried multiple methods and failed to achieve the goal. Can anyone help me?
All you need about rewrite annotation and path can be found in Ingress Rewrite Docs.
As per example on the site:
apiVersion: networking.k8s.io/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(/|$)(.*)
It will redirect:
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
In your case it should looks like:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: v2
namespace: api
spec:
rules:
- host: xxx.com
http:
paths:
- path: /any(/|$)(.*)
backend:
serviceName: serviceOld
servicePort: 80
- path: /feature(/|$)(.*)
backend:
serviceName: serviceNew
servicePort: 8080
I found that my version is relatively old. Under the old version, if the ports are different, it will not work properly. Change to the same port or upgrade the ingress version

Resources