Docker部署FastApi详解,这一篇就够了

首先废话一下,FastAPI是一种现代,快速(高性能)的Web框架,用于基于标准Python类型提示使用Python 3.6+构建API。据说是go+nodejs的竞争对手。

FastAPI 官方文档

这里主要讲的是uvicorn-gunicorn-fastapi 这个Fastapi的官方镜像,主要的技术细节:

Uvicorn

Uvicorn是一款闪电般的" ASGI"服务器。

它在单个过程中运行异步Python Web代码。

Gunicorn

您可以使用Gunicorn管理Uvicorn和运行多个这些并发进程。

这样,您将获得最佳的并发性和并行性。

FastAPI

FastAPI是一种现代,快速(高性能)的Web框架,用于使用Python 3.6+构建API。

反正主要使用的技术就是**UvicornGunicorn,**官网介绍说其是站在巨人肩膀上的框架,也确实有其流弊之处吧。

这里主要介绍 tiangolo/uvicorn-gunicorn-fastapi ,适用于生产环境,官网的其他镜像也至少改变的操作系统的版本为了缩减体积。

git 传送 ☞ uvicorn-gunicorn-fastapi

用法:

bash 复制代码
#Dockerfile

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

COPY ./app /app

image.gif

dockerfile的意思就是把你的代码(./app)复制到 /app文件夹中。

当然前提是至少需要一个main.py的配置文件下面是镜像能读取的两个默认位置,选择一个去放就好了。

/app/app/main.py

/app/main.py

main.py类似于这样:

python 复制代码
#这个其实就是镜像中自带的main.py 位置/app/main.py
import sys

from fastapi import FastAPI

version = f"{sys.version_info.major}.{sys.version_info.minor}"

app = FastAPI()

@app.get("/")
async def read_root():
    message = f"Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python {version}"
    return {"message": message}

image.gif

你自己的目录结构大概就是这样:

css 复制代码
.
├── app
│   └── main.py
└── Dockerfile

image.gif

然后就是构建镜像:

bash 复制代码
# 在dockerfile的路径下执行 myimage 替换成自己的起的名字作为镜像名
docker build -t myimage ./  

然后你可以试着启动一下了:

css 复制代码
docker run -d --name mycontainer -p 80:80 myimage

当然,上面讲的情况是把你自己代码放进去部署,如果只是想验证以下效果不用去看上面的直接执行:

undefined 复制代码
docker pull tiangolo/uvicorn-gunicorn-fastapi:python3.7

docker run -d --name fastapidemo -p 80:80 tiangolo/uvicorn-gunicorn-fastapi:python3.7

然后用浏览器打开以下链接测试以下:

objectivec 复制代码
http://127.0.0.1/   
#返回{"message":"Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7"}

http://127.0.0.1/docs
#API文档 

http://127.0.0.1/redoc
# 备用API文档

API文档类似:

image

不用惊讶,FastApi内置了swgger文档 ,你只要正常写方法就可以了,但是类型还是要注意一下像下面这样是完全没用问题的:

ruby 复制代码
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

当然肯定也有更复杂的需求,比如:安装一个依赖管理工具 **Poetry,**你可以这样组织你的dockerfile:

bash 复制代码
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

# Install Poetry
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \
    cd /usr/local/bin && \
    ln -s /opt/poetry/bin/poetry && \
    poetry config virtualenvs.create false

# Copy using poetry.lock* in case it doesn't exist yet
COPY ./app/pyproject.toml ./app/poetry.lock* /app/

RUN poetry install --no-root --no-dev

COPY ./app /app

用法挺简单,但是里面包含的功能一点也不简单。

我们上面使用的镜像 tiangolo/uvicorn-gunicorn-fastapi:python3.7 如果追溯它的源镜像那就是下面这个:

bash 复制代码
# DockerFile tiangolo/uvicorn-gunicorn-fastapi:python3.7
FROM python:3.7

LABEL maintainer="Sebastian Ramirez <tiangolo@gmail.com>"

RUN pip install --no-cache-dir uvicorn gunicorn

COPY ./start.sh /start.sh
RUN chmod +x /start.sh

COPY ./gunicorn_conf.py /gunicorn_conf.py

COPY ./start-reload.sh /start-reload.sh
RUN chmod +x /start-reload.sh

COPY ./app /app
WORKDIR /app/

ENV PYTHONPATH=/app

EXPOSE 80

# Run the start script, it will check for an /app/prestart.sh script (e.g. for migrations)
# And then will start Gunicorn with Uvicorn
CMD ["/start.sh"]

git 地址我也贴一下 ☞ tiangolo/uvicorn-gunicorn-fastapi:python3.7 构建代码

下面重点来了 ,如何使用 环境变量 去更改默认的配置:

(1)MODULE_NAME:

由Gunicorn导入的Python"模块"(文件),该模块将在变量中包含实际的应用程序,就是上面说的main.lpy

默认值:

如果你的main.py 是这个路径 /app/app/main.py 那么 值为 app.main

路径是 /app/main.py 值为main

在运行的时候这样就改变默认的值:

bash 复制代码
docker run -d -p 80:80 -e MODULE_NAME="custom_app.custom_main" myimage

(2)VARIABLE_NAME

默认: app

如果你的main文件是这样写的 你需要把它改成api

python 复制代码
from fastapi import FastAPI

api = FastAPI()

@api.get("/")
def read_root():
    return {"Hello": "World"}
bash 复制代码
docker run -d -p 80:80 -e VARIABLE_NAME="api" myimage
# 运行时修改VARIABLE_NAME

(3)APP_MODULE

把上面两个变量结合起来就是这个变量

bash 复制代码
docker run -d -p 80:80 -e APP_MODULE="custom_app.custom_main:api" myimage

(4)GUNICORN_CONF

Gunicorn Python配置文件的路径。

默认:

  • /app/gunicorn_conf.py 如果存在
  • /app/app/gunicorn_conf.py 如果存在
  • /gunicorn_conf.py (包含的默认值)

设置方法:

bash 复制代码
docker run -d -p 80:80 -e GUNICORN_CONF="/app/custom_gunicorn_conf.py" myimage

(4)常用的 HOST(容器内使用)PORT BIND

默认就是0.0.0.0:80 略。

(5)LOG_LEVEL Gunicorn** 日志级别

  • debug
  • info
  • warning
  • error
  • critical

默认:info

bash 复制代码
docker run -d -p 80:8080 -e LOG_LEVEL="warning" myimage

(6)TIMEOUT沉默(silent **)了多少秒的Workers 被杀死并重新启动。

默认:120

像FastAPI这样的Uvicorn和ASGI框架是异步的,而不是同步的。因此,与同步工作器相比,拥有更高的超时可能是安全的。

您可以将其设置为:

bash 复制代码
docker run -d -p 80:8080 -e TIMEOUT="20" myimage

(7)GRACEFUL_TIMEOUT

正常工作人员的超时重新启动。在Gunicorn文档中了解更多有关此内容的信息:graceful-timeout

默认:120

bash 复制代码
docker run -d -p 80:8080 -e GRACEFUL_TIMEOUT="20" myimage

(8)KEEP_ALIVE

等待"保持活动"连接上的请求的秒数。

Gunicorn docs:keepalive中阅读更多有关它的信息。

默认情况下,设置为2

您可以将其设置为:

bash 复制代码
docker run -d -p 80:8080 -e KEEP_ALIVE="20" myimage

(9)ACCESS_LOG

要写入的访问日志文件。

默认情况下"-",表示stdout(在Docker日志中打印)。

如果要禁用ACCESS_LOG,请将其设置为空值。

例如,您可以通过以下方式禁用它:

bash 复制代码
docker run -d -p 80:8080 -e ACCESS_LOG=  myimage
#注意这里表示设置为空值

(10)ERROR_LOG

要写入的错误日志文件。

默认情况下"-",表示stderr(在Docker日志中打印)。

如果要禁用ERROR_LOG,请将其设置为空值。

例如,您可以通过以下方式禁用它:

undefined 复制代码
docker run -d -p 80:8080 -e ERROR_LOG = myimage

(11)GUNICORN_CMD_ARGS

Gunicorn的任何其他命令行设置都可以在GUNICORN_CMD_ARGS环境变量中传递。

Gunicorn文档:设置中阅读更多相关信息。

这些设置将优先于其他环境变量和任何Gunicorn配置文件。

例如,如果您具有要使用的自定义TLS / SSL证书,则可以将其复制到Docker映像或将其安装在容器中,然后设置--keyfile--certfile文件的位置,例如:

jsx 复制代码
docker run -d -p 80:8080 -e GUNICORN_CMD_ARGS = "" --keyfile = / secrets / key.pem --certfile = / secrets / cert.pem " -e PORT = 443

注意 :建议不要使用Traefik之类的" TLS终止代理",而是自己处理TLS / SSL并在容器中进行配置。您可以在有关HTTPSFastAPI文档中阅读有关它的更多信息。

(12)PRE_START_PATH

预启动脚本的路径

默认:/app/prestart.sh

设置方法:

bash 复制代码
docker run -d -p 80:8080 -e PRE_START_PATH="/custom/script.sh" myimage

这个重点说一下默认的文件如下:

bash 复制代码
#! /usr/bin/env sh
# 文件路径:/app/prestart.sh 
echo "Running inside /app/prestart.sh, you could add migrations to this file, e.g.:"

echo "
#! /usr/bin/env bash

# Let the DB start
sleep 10;
# Run migrations
alembic upgrade head
"

启动时就是这样的,在这里加载一些需要的脚本就好,一般写等待10s是给数据库启动留下的时间。

image

像 /app/prestart.sh 还有gunicorn_conf.py这种开发常用的配置文件推荐的方法:

是写你自己的dockerfile的时候,编写命令把他们的从默认位置替换掉就好了。

  • /app/gunicorn_conf.py
  • /app/app/gunicorn_conf.py
  • /gunicorn_conf.py

当然,环境变量也不是必须在启动时从docker run 后面指定,使用dockerfile的ENV设置能获得更好的体验

其他功能 :开发时重载

基于 : /start-reload.sh

生产环境是默认使用的是 /start.sh

使用方法:

ruby 复制代码
docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh
ruby 复制代码
  -v $(pwd):/app

:表示该目录

shell 复制代码
  $(pwd)

应作为卷挂载到位于的容器内

bash 复制代码
  /app

  • $(pwd):运行pwd("打印工作目录"),并将其作为字符串的一部分。
  • /start-reload.sh/start-reload.sh在命令末尾添加一些内容(如),用此命令替换默认的"命令"。在这种情况下,它将/start.sh开发替代项替换为default()/start-reload.sh

由于/start-reload.sh不与Gunicorn一起运行,因此您放入gunicorn_conf.py文件中的任何配置都将不适用。

但是这些环境变量的工作原理与上述相同:

  • MODULE_NAME
  • VARIABLE_NAME
  • APP_MODULE
  • HOST
  • PORT
  • LOG_LEVEL

贴一下这俩文件代码:

bash 复制代码
# /start.sh
#! /usr/bin/env sh
set -e

if [ -f /app/app/main.py ]; then
    DEFAULT_MODULE_NAME=app.main
elif [ -f /app/main.py ]; then
    DEFAULT_MODULE_NAME=main
fi
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
VARIABLE_NAME=${VARIABLE_NAME:-app}
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}

if [ -f /app/gunicorn_conf.py ]; then
    DEFAULT_GUNICORN_CONF=/app/gunicorn_conf.py
elif [ -f /app/app/gunicorn_conf.py ]; then
    DEFAULT_GUNICORN_CONF=/app/app/gunicorn_conf.py
else
    DEFAULT_GUNICORN_CONF=/gunicorn_conf.py
fi
export GUNICORN_CONF=${GUNICORN_CONF:-$DEFAULT_GUNICORN_CONF}
export WORKER_CLASS=${WORKER_CLASS:-"uvicorn.workers.UvicornWorker"}

# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
echo "Checking for script in $PRE_START_PATH"
if [ -f $PRE_START_PATH ] ; then
    echo "Running script $PRE_START_PATH"
    . "$PRE_START_PATH"
else
    echo "There is no script $PRE_START_PATH"
fi

# Start Gunicorn
exec gunicorn -k "$WORKER_CLASS" -c "$GUNICORN_CONF" "$APP_MODULE"
bash 复制代码
# /start-reload.sh
#! /usr/bin/env sh
set -e

if [ -f /app/app/main.py ]; then
    DEFAULT_MODULE_NAME=app.main
elif [ -f /app/main.py ]; then
    DEFAULT_MODULE_NAME=main
fi
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
VARIABLE_NAME=${VARIABLE_NAME:-app}
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}

HOST=${HOST:-0.0.0.0}
PORT=${PORT:-80}
LOG_LEVEL=${LOG_LEVEL:-info}

# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
echo "Checking for script in $PRE_START_PATH"
if [ -f $PRE_START_PATH ] ; then
    echo "Running script $PRE_START_PATH"
    . "$PRE_START_PATH"
else
    echo "There is no script $PRE_START_PATH"
fi

# Start Uvicorn with live reload
exec uvicorn --reload --host $HOST --port $PORT --log-level $LOG_LEVEL "$APP_MODULE"
相关推荐
LucianaiB17 分钟前
参加高德 AI 发布会的一点感受:地图,正在变成 AI 的行动入口
后端
属于自己的天空17 分钟前
一个文件让 Claude Code 理解你的项目:CLAUDE.md 从入门到精通
后端
jiangbo_dev23 分钟前
还在手搓分布式事务?我把 Saga + Outbox 模板化后,新服务接入从 5 天压到 1 天
后端
BING_Algorithm26 分钟前
深入理解JVM垃圾回收
jvm·后端·面试
RainCity1 小时前
Java Swing 自定义组件库分享(六)
java·笔记·后端
techdashen1 小时前
深入 Rust enum 的内存世界
开发语言·后端·rust
龙码精神1 小时前
TimescaleDB 物联网设备属性历史数据表设计及常用SQL文档
后端
小小小小宇1 小时前
Go 后端锁机制详解
后端
挖坑的张师傅1 小时前
你的仓库 Agent Ready 了吗?
后端
客场消音器2 小时前
如何使用codex进行UI重构,让AI开发的前端页面不再千篇一律
前端·后端·微信小程序