使用Google Cloud Run进行FastAPI Gunicorn Uvicorn生产部署(压力测试)

15
我想问一下,使用gunicorn uvicorn和Python运行以及使用默认的tiangolo有什么区别?
我已经尝试使用JMeter进行压力测试,并设置了线程属性:

Screenshot from 2021-02-18 12-29-26

通过这些,我得到了结果:

Screenshot from 2021-02-18 12-20-05

我尝试过以下方法:

  1. 使用tiangolo基础Dockerfile
  2. 使用python:3.8-slim-buster Dockerfile,并使用gunicorn命令运行
  3. 使用python:3.8-slim-buster Dockerfile,并使用python命令运行

这是我用于第一种情况(Tiangolo基础)的Dockerfile:

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8-slim
RUN apt-get update && apt-get install wget gcc -y
RUN mkdir -p /app
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN python -m pip install --upgrade pip
RUN pip install --no-cache-dir -r /app/requirements.txt
COPY . /app

这是我针对案例2编写的Dockerfile(基于Python,使用gunicorn命令):

FROM python:3.8-slim-buster as builder
RUN apt-get update --fix-missing
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libgl1-mesa-dev python3-pip git
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip3 install -U setuptools
RUN pip3 install --upgrade pip
RUN pip3 install -r ./requirements.txt
COPY . /usr/src/app
ENTRYPOINT gunicorn --bind :8080 --workers 1 --threads 8 main:app --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout 60 --worker-tmp-dir /dev/shm

这是我的第三个案例的Dockerfile(基于Python,包含python命令):

FROM python:3.8-slim-buster
RUN apt-get update --fix-missing
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libgl1-mesa-dev python3-pip git
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip3 install -U setuptools
RUN pip3 install --upgrade pip
RUN pip3 install -r ./requirements.txt --use-feature=2020-resolver
COPY . /usr/src/app
CMD ["python3", "/usr/src/app/main.py"]

在这里我有些困惑,从上面的结果看起来它们的结果相当,那么上面三种方法之间有什么区别呢?哪一种最适合生产?抱歉,我是生产部署 API 的新手。我需要一些关于这个问题的建议。谢谢。

这是我的 Cloud Run 命令

gcloud builds submit --tag gcr.io/gaguna3/priceengine

gcloud run deploy backend-pure-python \
    --image="gcr.io/gaguna3/priceengine" \
    --region asia-southeast2 \
    --allow-unauthenticated \
    --platform managed \
    --memory 4Gi \
    --cpu 2 \
    --timeout 900 \
    --project=gaguna3

1
如果您在本地环境上运行这些测试,是否收到了相同的性能差异? - Jan Hernandez
1
@JanHernandez 当我在本地尝试时,仍然得到了相同的结果。 - MADFROST
请直接使用单个Uvicorn worker进行测试,而不是通过gunicorn运行。我认为这是Cloud Run工作方式的最佳选择。 - Zaffer
感谢您的基准测试和努力。我想部署FastAPI以在K8s中提供ML模型服务。一些问题:(1)对于案例1,启动配置是什么,有多少线程、工作进程和使用了哪个工作进程类?或者所有3个案例都使用相同的配置?(2)在进行这项测试之后,您认为具有更多CPU线程和内存的机器将提供更好的性能吗?如果是,启动gunicorn需要做出哪些更改? - Naveen Reddy Marthala
1
Tiangolo建议在Kubernetes中使用单个UVicorn进程的容器,不鼓励使用Gunicorn,并且您可能无需使用此Docker镜像。来源:https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#-warning-you-probably-dont-need-this-docker-image。看到您的基准测试结果后,我不确定是否应该在Kubernetes中使用Gunicorn。 - Naveen Reddy Marthala
1
看了 OP 的基准测试结果后,我采用了这种方法,但是我甚至无法每秒处理 1000 个请求。请帮帮我,更多信息请参见:https://dev59.com/IG8NtIcB2Jgan1znlprM - Naveen Reddy Marthala
2个回答

9
建议使用 gunicorn 来加速,可以获得比使用uvicorn 快3倍的速度,例如:

gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app

更多细节和基准测试请参见:https://dev59.com/VVIH5IYBdhLWcg3wfezD#63427961

8
根据您提供的示例,我注意到第一个容器正在使用一个经过优化的gunicorn版本,在tiangolo的github页面中也提到了。

该镜像包含了一个“自动调整”机制,因此您只需添加您的代码,就可以自动获得相同的高性能。

在我的观点中,这可以通过对gunicorn worker进行动态缩放和/或使用Cpython模块来实现。
第二个和第三个容器之间的区别是为您的服务定义的worker数量,在第二个容器中,您只有1个具有8个线程的worker,如果您调整配置,则可以提高性能,正如在这篇文章中所提到的。
使用tiangolo/uvicorn-gunicorn并不是一个坏主意,但我建议您锁定容器的版本,以防止未来的更改影响您的生产环境。
另一方面,使用原始的Python映像使您能够自定义映像,而不用担心破坏某些东西,但这需要花费一些时间才能获得与tiangolo docker相同的性能。

在 Github 存储库中,Tiangolo 使用了 pip 安装的 Gunicorn,并且他还有一个名为 "gunicorn_conf.py" 的文件来配置它。是因为他使用的这个配置文件而获得了额外的性能吗?如果不是,那么是什么使 Tiangolo 的 Gunicorn 微调良好,我如何在我的 Docker 镜像中使用那个版本? - Naveen Reddy Marthala

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