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.
Related
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.
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
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
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
I have a pod that responds to requests to /api/
I want to do a rewrite where requests to /auth/api/ go to /api/.
Using an Ingress (nginx), I thought that with the ingress.kubernetes.io/rewrite-target: annotation I could do it something like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api
backend:
serviceName: myapi
servicePort: myapi-port
What's happening however is that /auth/ is being passed to the service/pod and a 404 is rightfully being thrown. I must be misunderstanding the rewrite annotation.
Is there a way to do this via k8s & ingresses?
I don't know if this is still an issue, but since version 0.22 it seems you need to use capture groups to pass values to the rewrite-target value
From the nginx example available here
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
For your specific needs, something like this should do the trick
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api/$2
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api(/|$)(.*)
backend:
serviceName: myapi
servicePort: myapi-port
I have created the following example that works and which I will explain. To run this minimal example, run these commands:
$ minikube start
$ minikube addons enable ingress # might take a while for ingress pod to bootstrap
$ kubectl apply -f kubernetes.yaml
$ curl https://$(minikube ip)/auth/api/ --insecure
success - path: /api/
$ curl https://$(minikube ip)/auth/api --insecure
failure - path: /auth/api
$ curl https://$(minikube ip)/auth/api/blah/whatever --insecure
success - path: /api/blah/whatever
As you'll notice, the ingress rewrite annotation appears to be very particular about trailing slashes. If a trailing slash is not present, the request will not be rewritten. However, if a trailing slash is provided, the request uri will be rewritten and your proxy will function as expected.
After inspecting the generated nginx.conf file from inside the ingress controller, the line of code responsible for this behavior is:
rewrite /auth/api/(.*) api/$1 break;
This line tells us that only requests matching the first argument will be rewritten with the path specified by the second argument.
I believe this is bug worthy.
kubernetes.yaml
---
apiVersion: v1
kind: Service
metadata:
name: ingress-rewite-example
spec:
selector:
app: ingress-rewite-example
ports:
- name: nginx
port: 80
protocol: TCP
targetPort: 80
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ingress-rewite-example
spec:
template:
metadata:
labels:
app: ingress-rewite-example
spec:
containers:
- name: ingress-rewite-example
image: fbgrecojr/office-hours:so-47837087
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-rewite-example
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /auth/api
backend:
serviceName: ingress-rewite-example
servicePort: 80
main.go
package main
import (
"fmt"
"strings"
"net/http"
)
func httpHandler(w http.ResponseWriter, r *http.Request) {
var response string
if strings.HasPrefix(r.URL.Path, "/api") {
response = "success"
} else {
response = "failure"
}
fmt.Fprintf(w, response + " - path: " + r.URL.Path + "\n")
}
func main() {
http.HandleFunc("/", httpHandler)
panic(http.ListenAndServe(":80", nil))
}