azureflaskazure-aksingress-controller

ingress routing definitions - using react frontend with python flask as backend server in Kubernetes (Azure)


I am trying to define routes for external traffic ie where should requests go once they reach my app gateway. I have a frontend and backend service, defined at ports 3000 and 5001 respectively. I want all traffic to go to my frontend service, however I would like api calls to go to my backend service (anything that has "/api"). This is how my ingress is setup currently:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    kubernetes.io/ingress.class: azure/application-gateway
    appgw.ingress.kubernetes.io/ssl-redirect: "true" # Enable SSL redirect
    appgw.ingress.kubernetes.io/backend-protocol: "http" # protocol AGIC communicates to pods with
    appgw.ingress.kubernetes.io/backend-path-prefix: "/"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - mydomain.com
      secretName: letsencrypt-prod-secret
  rules:
    - host: mydomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 3000
          - path: /(api)(.*)
            pathType: Prefix
            backend:
              service:
                name: backend-service
                port:
                  number: 5001

This does give me the result I want for navigating to my page visually, but when I try to do anything that requires backend calls such as signup, login, etc I get a 404 error. I also don't see any requests in the pods logs for these.

Backend logs before change:

2024-01-26 00:44:54,541 INFO :- - [26/Jan/2024 00:44:54] "GET / HTTP/1.1" 200 -
2024-01-26 00:44:55,099 INFO :- - [26/Jan/2024 00:44:55] "GET / HTTP/1.1" 200 -
2024-01-26 00:45:24,542 INFO :- - [26/Jan/2024 00:45:24] "GET / HTTP/1.1" 200 -
2024-01-26 00:45:25,098 INFO :- - [26/Jan/2024 00:45:25] "GET / HTTP/1.1" 200 -
2024-01-26 00:45:54,545 INFO :- - [26/Jan/2024 00:45:54] "GET / HTTP/1.1" 200 -
2024-01-26 00:45:55,101 INFO :- - [26/Jan/2024 00:45:55] "GET / HTTP/1.1" 200 -

How should I define my ingress routes? In summary, I want all traffic to go to the frontend pod set up and I want any request with "/api" to be sent to the backend pod. Should I be going about this in a different way?

However, if I change my ingress to (only changes):


    appgw.ingress.kubernetes.io/backend-path-prefix: "/api"
  rules:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: backend-service
                port:
                  number: 5001

I am able to see the requests being made to the backend but am no longer able to navigate to mydomain.com and see the frontend page I want up. After change:

2024-01-26 00:46:49,859 INFO :- - [26/Jan/2024 00:46:49] "GET /api HTTP/1.1" 404 -
2024-01-26 00:46:50,047 INFO :- - [26/Jan/2024 00:46:50] "GET /api HTTP/1.1" 404 -
2024-01-26 00:46:51,819 INFO :- - [26/Jan/2024 00:46:51] "POST /api/login HTTP/1.1" 404 -

I get a 404 after the changes because all my routes in flask don't have "/api" defined in them (I think).


Solution

  • The behavior you're experiencing is likely due to the way the Ingress controller is routing the traffic based on the paths defined in your Ingress resource. The first configuration you posted is close to what you need, but the ordering of the rules and the path definitions are critical in Kubernetes Ingress.

    Ingress paths are evaluated in order, and the first match is where the traffic will be routed. Therefore, if the catch-all path / is listed before /api, all traffic will go to the frontend service, including your API calls. To fix this, you should define the more specific /api path before the general / path.

    Try adjusting your Ingress something like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: myapp-ingress
      annotations:
        kubernetes.io/ingress.class: azure/application-gateway
        appgw.ingress.kubernetes.io/ssl-redirect: "true"
        appgw.ingress.kubernetes.io/backend-protocol: "http"
        cert-manager.io/cluster-issuer: letsencrypt-prod
    spec:
      tls:
        - hosts:
            - mydomain.com
          secretName: letsencrypt-prod-secret
      rules:
        - host: mydomain.com
          http:
            paths:
              - path: /api/ # Note the trailing slash
                pathType: Prefix
                backend:
                  service:
                    name: backend-service
                    port:
                      number: 5001
              - path: / # The catch-all rule should be last
                pathType: Prefix
                backend:
                  service:
                    name: frontend-service
                    port:
                      number: 3000
    

    In this setup:

    Remember to include the trailing slash in the /api/ path, as it denotes a prefix. Without it, only the exact path /api would be matched, not paths like /api/login

    Reference:

    Kubernetes Ingress Documentation Ingress Path Matching Introduction to Application Gateway Ingress Controller Annotations for Application Gateway Ingress Controller Use Let's Encrypt with Azure Kubernetes Service (AKS)