如何 运行 FastAPI 应用程序在多个端口上?

How to run FastAPI app on multiple ports?

我有一个 FastAPI 应用程序,我正在 运行 以编程方式使用 Uvicorn 连接端口 30000。现在我也想 运行 端口 8443 上的相同应用程序。同一个应用程序需要在这两个端口上 运行。我如何在 Python 代码中执行此操作?

最小可重现代码:

from fastapi import FastAPI
import uvicorn

app = FastAPI()


@app.get("/healthcheck/")
def healthcheck():
    return 'Health - OK'

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=30000)

我想要

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", ports=[30000,8443])

说明: 我的应用程序将在我的组织 Azure Kubernetes 服务上 运行ning。端口 30000 上的应用程序 运行ning 保留用于内部 HTTP 流量,应用程序 运行ning 上的 8443 映射到 Kubernetes 服务的 443 以暴露给外部流量。

更多详情: 我将从这个应用程序中创建一个 Docker 容器,我的想法是包含

CMD ["python3", "app.py"]

最后到 运行 应用程序。我正在寻找一种解决方案,它可以提供一种方法来更改 python 代码 ( uvicorn.run(app, host="0.0.0.0", ports=[30000,8443]) ) 或更改 Docker 文件中的 CMD 命令,例如 This GitHub Issue Comment - gunicorn -k uvicorn.workers.UvicornWorker -w 1 --bind ip1:port1 --bind ip2:port2 --bind ip3:port3

其中一个选项是使用docker-compose 并在可变环境中传输端口。您只需要部署应用程序的多个实例。该版本尚未准备好生产,只是一个最小的示例。

docker-compose.yml
Dockerfile
main.py
requirements.txt

main.py:

from fastapi import FastAPI
import uvicorn
import os

app = FastAPI()


@app.get("/healthcheck/")
def healthcheck():
    return 'Health - OK'


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=int(os.getenv('APP_PORT')))

Docker 文件:

FROM python:3.8-slim

WORKDIR /usr/src/app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD [ "python", "./main.py" ]

docker-compose.yml:

version: '2'

services:
  internal-app:
    image: internal-app
    environment:
      APP_PORT: "3000"
    build:
      context: .
      dockerfile: ./Dockerfile
    restart: unless-stopped
    network_mode: host

  external-app:
    image: external-app
    environment:
      APP_PORT: "8443"
    build:
      context: .
      dockerfile: ./Dockerfile
    restart: unless-stopped
    network_mode: host

requirements.txt:

uvicorn
fastapi

开始 - docker-compose up -d --build

2 个应用程序将在其自己的端口上启动。

我会建议 运行 多个副本用于内部和外部通信

uvicorn.run(app, host="0.0.0.0", port=int(os.getenv('PORT')))

因此您的部署之一将是 运行 端口 300008443 在 kubernets 中。

因此,您可以创建两种服务,一种用于外部通信,一种用于内部通信。

入口流量将路由到 8443 上的那些 pod 运行,而内部服务调用将路由到 30000 上的那些 pod 运行。

Ingress > service 1 > deployment 1  with port 8443 > pods

Internal traffic > service 2 > deployment 2 with port 30000 > pods

在这两种部署中,您可以通过设置环境更改端口

加分:

  • 如果您的单个端口出现故障或应用程序在内部调用或外部调用期间崩溃(因为两个部署将 运行),您的 PODs 部署不会失败

以下解决方案对我有用。它在后台运行一个 gunicorn 进程,然后运行另一个进程将其绑定到两个端口。其中一个将使用 HTTP,一个可以使用 HTTPS。

Docker 文件:

FROM python:3.7
WORKDIR /app
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . .
ENTRYPOINT ./docker-starter.sh
EXPOSE 30000 8443

docker-starter.sh:

gunicorn -k uvicorn.workers.UvicornWorker -w 3 -b 0.0.0.0:30000 -t 360 --reload --access-logfile - app:app & gunicorn --access-logfile - -k --ca_certs ca_certs.txt uvicorn.workers.UvicornWorker -w 3 -b 0.0.0.0:8443 -t 360 --reload --access-logfile - app:app

python 应用可以保持最小化:

from fastapi import FastAPI
import uvicorn

app = FastAPI()


@app.get("/healthcheck/")
def healthcheck():
    return 'Health - OK'

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0")

在我的例子中,我使用了上面相同的命令,但有一个小的变化。我需要在私有端口上公开其他路由。

app = FastAPI()
app2 = FastAPI()

然后,在 run.sh 文件中,我有:

uvicorn app.main:app --reload --host 0.0.0.0 --port $PORT & uvicorn app.main:app2 --reload --host 0.0.0.0 --port $PORT_INTERNAL_APP