Nginx Ingress path is not pointing to service / path

Viewed 1778

I have nginx ingress installed on my cluster. Here is the yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-client
  annotations:
    kubernetes.io/ingress.class: nginx
  namespace: dev
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: client-service
            port: 
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port: 
              number: 80

When I hit / prefix it is good. curl http://example.com (All Good)

Problem:

But when I hit / api prefix, it returns /api of the service not / of the service

curl http://example.com/api (It should link to api-service, but it is linking to api-service/api)

Any help will be appreciated!

2 Answers

This is because a path / with type Prefix will match / and everything after, including /api. So your first rule overshadows the second rule in some sense.


I don't know if it's an option for you, but it would be probably most elegant and idiomatic to use different hostnames for both services. If you deploy cert-manager, this shouldn't be a problem.

rules:
- host: example.com
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: client-service
          port: 
            number: 80
# use a different hostname for the api
- host: api.example.com
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: api-service
          port: 
            number: 80

Another option could be to use regex in your frontend path rule. And let it not match when the slash is followed by api. For that, you need to set an annotation.

annotations:
  nginx.ingress.kubernetes.io/use-regex: "true"

Then you can do something like the below for your frontend service. Using a negative lookahead.

- path: /(?!api).*

Alternatively, but less pretty, you could add a path prefix to your frontend service and strip it away via path rewrite annotation. But then you may have to write two separate ingress manifests as this is annotation counts for both, or you need to use a more complex path rewrite rule.

annotations:
  nginx.ingress.kubernetes.io/rewrite-target: /$2
- path: /ui(/|$)(.*)

Perhaps it's also sufficient to move the more specific route, /api, before the generic / route. In that case, switch the path around in the list. If they end up in that order in the nginx config, nginx should be able to handle it as desired.

You could use nginx.ingress.kubernetes.io/rewrite-target:

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.

So, here you could change your ingress as next:

metadata:
  name: ingress-client
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
  namespace: dev

The ingress definition above will result in the following rewrites:

  • api-service/api rewrites to api-service/
Related