3

我编写了一个在 Kubernetes pod 内运行 FastAPI 服务器的应用程序。与 pod 的外部通信通过单独的 pod 中的 nginx 入口控制器进行。我正在运行 nginx:1.17.0。

当它全部启动并运行时,我可以使用curl调用通过入口地址与应用服务器交互,并在我的浏览器中访问所有简单的 GET 路径以及地址/openapi.json。如果我在 Kubernetes 中使用应用服务的内部 ip,我也可以访问交互式文档页面。但是,尝试访问交互式文档页面(address/docs#/default/)会给我一个关于/openapi.json的错误。

在此处输入图像描述

由于curl调用按预期工作,我认为问题不一定在入口定义中,但使用应用程序的内部 ip 也可以正常工作,问题不应该在应用程序内部。
我在下面包含了入口定义文件。

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

编辑
这是 service.yaml 文件

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

由于该服务是我本地集群中的 ClusterIP,我可能可以直接使用它,但我还没有尝试过。当我卷曲时,我使用类似的命令

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"

从本地集群外部。

这些是所有正在运行的服务:

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

在我的/etc/hosts文件中,我已将 10.0.0.1 (集群“外部”IP)连接到my-host.info

关于为什么会发生这种情况的任何想法?

4

2 回答 2

6

我认为您绝对应该查看 FastApi 的官方文档:https ://fastapi.tiangolo.com/advanced/behind-a-proxy/

正如您所提到的,在内部访问您的应用程序时,Swagger 自动文档可以正常工作,但是从集群外部访问时,您会遇到关于 /openapi.json 的错误。

在你的service.yaml你有:

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

当你使用 uvicorn 启动你的应用程序时,你应该通过root_path

uvicorn main:app --root-path /server

注意:在这里您将能够访问路由器端点,但不能访问 Swagger 文档。为了获得 Swagger 文档,您必须编辑主main.py文件:


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")}

我搜索了为什么我们需要明确传递 OpenApi 前缀,但我发现只有解决方法,例如:https ://github.com/iwpnd/fastapi-aws-lambda-example/issues/2

所以我建议存储root_path在您系统上的环境变量中$ROOT_PATH=/server,并将其传递给:uvicorn main:app --root-path $ROOT_PATHmain.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")}

更新 07.07.2020

目前 tiangolo 准备使用 docker 图像https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker如果继续使用当前为:0.55.1 的 fastapi 版本,则“已过时” - 此处报告:链接

从 0.56.0 开始支持“root_path”

于 2020-06-12T12:37:45.550 回答
2

使用 Kubernetes重写功能进行入口,我可以像这样解决我的问题:

ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: my-app
    http:
      paths:
      - path: /server(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-fastapi
            port:
              number: 80

然后我只需要将root_path添加到我的 FastAPI 应用程序中:

app = FastAPI(root_path="/server")
于 2021-11-08T14:57:16.467 回答