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

12

我有一个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服务上运行。运行在端口30000上的应用程序保留为内部HTTP流量,而运行在8443上的应用程序被映射到Kubernetes服务的443端口以向外部流量公开。

更多细节: 我将创建一个Docker容器来包含这个应用程序,并且想法是将其包括在

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

在运行应用程序的末尾。我正在寻找一个解决方案,要么提供一种改变Python代码(uvicorn.run(app,host =“0.0.0.0”,port =[30000,8443]))的方法,要么改变Dockerfile中CMD命令的方式,如这个GitHub问题评论 - gunicorn -k uvicorn.workers.UvicornWorker -w 1 --bind ip1:port1 --bind ip2:port2 --bind ip3:port3


1
你的应用程序是否关心请求接收的端口?换句话说,内部请求是否可以通过负载均衡器转发到8443端口,以便您的容器只需暴露一个端口? - Mike Holcomb
是的,确实如此。问题在于负载均衡器超出了我们的控制范围,因为AKS在组织中不同团队之间共享。因此,我们将无法控制ILB。 - Anirban Saha
4个回答

14

在我的情况下,我使用了上述相同的命令,但进行了小修改。我需要在私有端口上公开其他路由。

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

这对于用户想要运行两个不同的应用程序的情况非常有益。不错的补充! - Anirban Saha

8

其中一种选择是使用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')))

Dockerfile:

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个应用程序将启动,每个都在自己的端口上。


我会尝试这个并在验证它适用于我的用例后接受答案。 - Anirban Saha

2
以下解决方案适用于我。它在后台运行一个gunicorn进程,然后再运行另一个进程将其绑定到两个端口。其中一个端口将使用HTTP,另一个可以使用HTTPS。
Dockerfile:
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")

2
我建议运行多个副本以进行内部和外部通信。
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv('PORT')))

因此,您的部署之一将在Kubernetes中使用端口300008443运行。

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

Ingress流量将路由到在8443上运行的pod,而内部服务调用将路由到在30000上运行的pod。

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

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

在这两种部署方式中,您可以通过设置环境变量来更改端口
优点:
- 如果单个端口失败或应用程序在内部调用或外部调用期间崩溃,则POD的部署不会失败(因为将运行两个部署)。

我会尝试这个并在验证它适用于我的用例后接受答案。 - Anirban Saha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接