I manage an Azure AKS cluster with several API's written in Python and Uvicorn (FastAPI). We use the ingress-nginx ingress controller.
Currently API's are reached through a unique url similar to: servicename.departmentname.companyname.com. I am attempting to transition unique url routes to a common root url + path routes. For instance, the above would then become departmentname.companyname.com/servicename.
However, this is consistently causing errors, either 404 not found error or "Failed to load API definition" error (see screenshot), depending on the exact setup I am testing.
I have looked into the FastApi documentation which mentions several approaches that might be relevant in this case, but so fat I haven't been able to understand the root cause of the problem. Any help or suggestions are welcome.

The current k8s uniqure url rout Ingress resource looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: servicename-api-ingress
namespace: servicename-api-prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-clusterissuer
spec:
ingressClassName: nginx
rules:
- host: servicename.departmentname.companyname.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: servicename-api-service
port:
number: 80
tls:
- hosts:
- servicename.departmentname.companyname.com
secretName: servicename-api-tls
The corresponding Dockerfile looks like this:
# build stage
FROM python:3.10
# install PDM
RUN pip install -U pip setuptools wheel
RUN pip install pdm
# copy files
COPY pyproject.toml pdm.lock README.md /project/
COPY src/ /project/src
# install dependencies and project
WORKDIR /project
RUN pdm install
# set command/entrypoint, adapt to fit your needs
CMD ["pdm", "run", "uvicorn", "companyname.servicename.api.main:app", "--host", "0.0.0.0", "--port", "8080"]
When transitioning to the root url + path route, the above Ingress resource would have to look something like the below, but this definition is not working.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: servicename-api-ingress
namespace: servicename-api-prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-clusterissuer
spec:
ingressClassName: nginx
rules:
- host: departmentname.companyname.com
http:
paths:
- path: /servicename/
pathType: Prefix
backend:
service:
name: servicename-api-service
port:
number: 80
tls:
- hosts:
- departmentname.companyname.com
secretName: servicename-api-tls
We have solved it like this:
In the Dockerfile we add a --root-path parameter to the run command, like this:
CMD ["pdm", "run", "uvicorn", "our.uvicorn.service.main:app", "--root-path", "/service/api/v1", "--host", "0.0.0.0", "--port", "8080"]
In the kubernetes ingress resource, we use regex to catch the right path:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: our-cool-ingress
namespace: our-cool-namespace
annotations:
cert-manager.io/cluster-issuer: letsencrypt-clusterissuer
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: our.cool.url.com
http:
paths:
- path: /service/api/v1(/|$)(.*)
pathType: Prefix
backend:
service:
name: our-cool-service
port:
number: 80
tls:
- hosts:
- our.cool.url.com
secretName: our-cool-url-com-tls
Path can be anything you like, as long as the value is the same in both DOckerfile and ingress path, in this example "/service/api/v1".
Cheers, Mike
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With