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.
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?
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