2
votes

I have written an application that runs a FastAPI server inside a Kubernetes pod. The external communication with the pod goes through an nginx ingress controller in a separate pod. I am running nginx:1.17.0.

When it is all up and running I can use curl calls to interact with the app server through the ingress address, and access all the simple GET paths as well as address/openapi.json in my browser. I can also access the interactive documentation page if I use the internal ip of the app service in Kubernetes. However trying to reach the interactive documentation page (address/docs#/default/) gives me an error regarding /openapi.json.

enter image description here

Since the curl calls work as expected I do not think the problem is necessarily in the ingress definition but as using the internal ip of the app also works fine the issue should not be inside the app.
I have included the ingress definition file below.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: my-host.info
    http:
      paths:
      - path: /server(/|$)(.*)
        backend:
          serviceName: my-app-service # This is the service that runs my fastAPI server pod
          servicePort: 80

EDIT
This is the service.yaml file

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
    - protocol: "TCP"
      port: 80
      targetPort: 80

As the service is a ClusterIP inside my local cluster I have might be able to curl straight to it, I have not tried though. When I curl I use commands like

curl -X GET "http://my-host.info/server/subpath/" -H "accept: application/json"
curl -X POST "http://my-host.info/server/subpath/update/" -H "accept: application/json"

from outside the local cluster.

These are all the services that are running:

NAMESPACE              NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
default                kubernetes                  ClusterIP   10.96.0.1       <none>        443/TCP                  11d
default                my-app-service              ClusterIP   10.96.68.29     <none>        80/TCP                   18h
kube-system            kube-dns                    ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   28d
kubernetes-dashboard   dashboard-metrics-scraper   ClusterIP   10.96.114.1     <none>        8000/TCP                 28d
kubernetes-dashboard   kubernetes-dashboard        ClusterIP   10.96.249.255   <none>        80/TCP                   28d

and inside my /etc/hosts file I have connected 10.0.0.1 (cluster "external" IP) to my-host.info.

Any ideas of why this is happening?

1
Could you please share your service yaml and exact command you are using to curl it? You ale trying to connect from outside the cluster? Also could you provide kubectl get svc --all-namespaces? It's On-Prem or local cluster? - PjoterS
First issue I found here is that your deployment and service have different selectors/labels. In deployment its app: nginx but in svc is app: server. Should be the same. I will try to reproduce your issue. You can check this answer Issue 2 - stackoverflow.com/a/59372425/11148139 - PjoterS
Its On-Prem or local cluster? - PjoterS
@PjoterS I have the deployment in a separate file. The app for the server is server and the nginx app is a different one, at least that's the way all the examples I've seen work. If this was the problem it wouldn't have worked at all. And as I've stated it's on a local cluster. The app is functioning fully it is just the connection to the FastAPI default docs page that doesn't work. - Kajsa
@PjoterS Turns out I misunderstood the tutorials and the nginx pod I deploy is not being used at all. Instead a different pod that is deployed by my minikube is running the nginx-ingress-controller which is then used by the ingress I've defined. - Kajsa

1 Answers

3
votes

I think you definitely should look into official documentation of FastApi: https://fastapi.tiangolo.com/advanced/behind-a-proxy/

As you mentioned, when accessing your app internally the Swagger auto docs works fine but when accessing from outside your cluster you've got an error regarding /openapi.json.

In your service.yaml you have:

      - path: /server(/|$)(.*)
        backend:
          serviceName: my-app-service # This is the service that runs my fastAPI server pod
          servicePort: 80

and when starting your application with uvicorn you should pass the root_path

uvicorn main:app --root-path /server

ATTENTION: Here you will be able to access the routers endpoints but no the Swagger docs. In order to get Swagger docs you have to edit your main main.py file:


from fastapi import FastAPI, Request

app = FastAPI(openapi_prefix="/server")

@app.get("/")
def read_root(request: Request):
    return {"message": "Hello World", "root_path": request.scope.get("root_path")}

I searched why we need explicitly pass the OpenApi prefix but I found only workarounds like: https://github.com/iwpnd/fastapi-aws-lambda-example/issues/2

So I suggest to store root_path in environment variable $ROOT_PATH=/server on your system and pass it to uvicorn main:app --root-path $ROOT_PATH as well as in main.py:

import os
from fastapi import FastAPI, Request

app = FastAPI(openapi_prefix=os.getenv('ROOT_PATH', ''))

@app.get("/")
def read_root(request: Request):
    return {"message": "Hello World", "root_path": request.scope.get("root_path")}

UPDATE 07.07.2020

Currently tiangolo ready to use docker images https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker are "outdated" if goes on fastapi version which currently is: 0.55.1 - reported here: link

"root_path" is supported since 0.56.0