首先废话一下,FastAPI是一种现代,快速(高性能)的Web框架,用于基于标准Python类型提示使用Python 3.6+构建API。据说是go+nodejs的竞争对手。
这里主要讲的是uvicorn-gunicorn-fastapi 这个Fastapi的官方镜像,主要的技术细节:
Uvicorn
Uvicorn是一款闪电般的" ASGI"服务器。
它在单个过程中运行异步Python Web代码。
Gunicorn
您可以使用Gunicorn管理Uvicorn和运行多个这些并发进程。
这样,您将获得最佳的并发性和并行性。
FastAPI
FastAPI是一种现代,快速(高性能)的Web框架,用于使用Python 3.6+构建API。
反正主要使用的技术就是**Uvicorn和Gunicorn,**官网介绍说其是站在巨人肩膀上的框架,也确实有其流弊之处吧。
这里主要介绍 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并在容器中进行配置。您可以在有关HTTPS的FastAPI文档中阅读有关它的更多信息。
(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"